]> git.mxchange.org Git - friendica.git/blob - include/text.php
Implement Smarty3
[friendica.git] / include / text.php
1 <?php
2
3 // This is our template processor.
4 // $s is the string requiring macro substitution.
5 // $r is an array of key value pairs (search => replace)
6 // returns substituted string.
7 // WARNING: this is pretty basic, and doesn't properly handle search strings that are substrings of each other.
8 // For instance if 'test' => "foo" and 'testing' => "bar", testing could become either bar or fooing, 
9 // depending on the order in which they were declared in the array.   
10
11 require_once("include/template_processor.php");
12 require_once("include/friendica_smarty.php");
13
14 if(! function_exists('replace_macros')) {  
15 function replace_macros($s,$r) {
16         global $t;
17
18 //      $ts = microtime();
19         $a = get_app();
20
21         if($a->theme['template_engine'] === 'smarty3') {
22                 $template = '';
23                 if(gettype($s) === 'string') {
24                         $template = $s;
25                         $s = new FriendicaSmarty();
26                 }
27                 foreach($r as $key=>$value) {
28                         if($key[0] === '$') {
29                                 $key = substr($key, 1);
30                         }
31                         $s->assign($key, $value);
32                 }
33                 $output = $s->parsed($template);
34         }
35         else {
36                 $r =  $t->replace($s,$r);
37         
38                 $output = template_unescape($r);
39         }
40 //      $tt = microtime() - $ts;
41 //      $a = get_app();
42 //      $a->page['debug'] .= "$tt <br>\n";
43         return $output;
44 }}
45
46
47 // random string, there are 86 characters max in text mode, 128 for hex
48 // output is urlsafe
49
50 define('RANDOM_STRING_HEX',  0x00 );
51 define('RANDOM_STRING_TEXT', 0x01 );
52
53 if(! function_exists('random_string')) {
54 function random_string($size = 64,$type = RANDOM_STRING_HEX) {
55         // generate a bit of entropy and run it through the whirlpool
56         $s = hash('whirlpool', (string) rand() . uniqid(rand(),true) . (string) rand(),(($type == RANDOM_STRING_TEXT) ? true : false));
57         $s = (($type == RANDOM_STRING_TEXT) ? str_replace("\n","",base64url_encode($s,true)) : $s);
58         return(substr($s,0,$size));
59 }}
60
61 /**
62  * This is our primary input filter. 
63  *
64  * The high bit hack only involved some old IE browser, forget which (IE5/Mac?)
65  * that had an XSS attack vector due to stripping the high-bit on an 8-bit character
66  * after cleansing, and angle chars with the high bit set could get through as markup.
67  * 
68  * This is now disabled because it was interfering with some legitimate unicode sequences 
69  * and hopefully there aren't a lot of those browsers left. 
70  *
71  * Use this on any text input where angle chars are not valid or permitted
72  * They will be replaced with safer brackets. This may be filtered further
73  * if these are not allowed either.   
74  *
75  */
76
77 if(! function_exists('notags')) {
78 function notags($string) {
79
80         return(str_replace(array("<",">"), array('[',']'), $string));
81
82 //  High-bit filter no longer used
83 //      return(str_replace(array("<",">","\xBA","\xBC","\xBE"), array('[',']','','',''), $string));
84 }}
85
86 // use this on "body" or "content" input where angle chars shouldn't be removed,
87 // and allow them to be safely displayed.
88
89 if(! function_exists('escape_tags')) {
90 function escape_tags($string) {
91
92         return(htmlspecialchars($string, ENT_COMPAT, 'UTF-8', false));
93 }}
94
95
96 // generate a string that's random, but usually pronounceable. 
97 // used to generate initial passwords
98
99 if(! function_exists('autoname')) {
100 function autoname($len) {
101
102         if($len <= 0)
103                 return '';
104
105         $vowels = array('a','a','ai','au','e','e','e','ee','ea','i','ie','o','ou','u'); 
106         if(mt_rand(0,5) == 4)
107                 $vowels[] = 'y';
108
109         $cons = array(
110                         'b','bl','br',
111                         'c','ch','cl','cr',
112                         'd','dr',
113                         'f','fl','fr',
114                         'g','gh','gl','gr',
115                         'h',
116                         'j',
117                         'k','kh','kl','kr',
118                         'l',
119                         'm',
120                         'n',
121                         'p','ph','pl','pr',
122                         'qu',
123                         'r','rh',
124                         's','sc','sh','sm','sp','st',
125                         't','th','tr',
126                         'v',
127                         'w','wh',
128                         'x',
129                         'z','zh'
130                         );
131
132         $midcons = array('ck','ct','gn','ld','lf','lm','lt','mb','mm', 'mn','mp',
133                                 'nd','ng','nk','nt','rn','rp','rt');
134
135         $noend = array('bl', 'br', 'cl','cr','dr','fl','fr','gl','gr',
136                                 'kh', 'kl','kr','mn','pl','pr','rh','tr','qu','wh');
137
138         $start = mt_rand(0,2);
139         if($start == 0)
140                 $table = $vowels;
141         else
142                 $table = $cons;
143
144         $word = '';
145
146         for ($x = 0; $x < $len; $x ++) {
147                 $r = mt_rand(0,count($table) - 1);
148                 $word .= $table[$r];
149   
150                 if($table == $vowels)
151                         $table = array_merge($cons,$midcons);
152                 else
153                         $table = $vowels;
154
155         }
156
157         $word = substr($word,0,$len);
158
159         foreach($noend as $noe) {
160                 if((strlen($word) > 2) && (substr($word,-2) == $noe)) {
161                         $word = substr($word,0,-1);
162                         break;
163                 }
164         }
165         if(substr($word,-1) == 'q')
166                 $word = substr($word,0,-1);    
167         return $word;
168 }}
169
170
171 // escape text ($str) for XML transport
172 // returns escaped text.
173
174 if(! function_exists('xmlify')) {
175 function xmlify($str) {
176         $buffer = '';
177         
178         for($x = 0; $x < mb_strlen($str); $x ++) {
179                 $char = $str[$x];
180         
181                 switch( $char ) {
182
183                         case "\r" :
184                                 break;
185                         case "&" :
186                                 $buffer .= '&amp;';
187                                 break;
188                         case "'" :
189                                 $buffer .= '&apos;';
190                                 break;
191                         case "\"" :
192                                 $buffer .= '&quot;';
193                                 break;
194                         case '<' :
195                                 $buffer .= '&lt;';
196                                 break;
197                         case '>' :
198                                 $buffer .= '&gt;';
199                                 break;
200                         case "\n" :
201                                 $buffer .= "\n";
202                                 break;
203                         default :
204                                 $buffer .= $char;
205                                 break;
206                 }       
207         }
208         $buffer = trim($buffer);
209         return($buffer);
210 }}
211
212 // undo an xmlify
213 // pass xml escaped text ($s), returns unescaped text
214
215 if(! function_exists('unxmlify')) {
216 function unxmlify($s) {
217         $ret = str_replace('&amp;','&', $s);
218         $ret = str_replace(array('&lt;','&gt;','&quot;','&apos;'),array('<','>','"',"'"),$ret);
219         return $ret;    
220 }}
221
222 // convenience wrapper, reverse the operation "bin2hex"
223
224 if(! function_exists('hex2bin')) {
225 function hex2bin($s) {
226         if(! (is_string($s) && strlen($s)))
227                 return '';
228
229         if(! ctype_xdigit($s)) {
230                 return($s);
231         }
232
233         return(pack("H*",$s));
234 }}
235
236 // Automatic pagination.
237 // To use, get the count of total items.
238 // Then call $a->set_pager_total($number_items);
239 // Optionally call $a->set_pager_itemspage($n) to the number of items to display on each page
240 // Then call paginate($a) after the end of the display loop to insert the pager block on the page
241 // (assuming there are enough items to paginate).
242 // When using with SQL, the setting LIMIT %d, %d => $a->pager['start'],$a->pager['itemspage']
243 // will limit the results to the correct items for the current page. 
244 // The actual page handling is then accomplished at the application layer. 
245
246 if(! function_exists('paginate')) {
247 function paginate(&$a) {
248         $o = '';
249         $stripped = preg_replace('/(&page=[0-9]*)/','',$a->query_string);
250
251 //      $stripped = preg_replace('/&zrl=(.*?)([\?&]|$)/ism','',$stripped);
252
253         $stripped = str_replace('q=','',$stripped);
254         $stripped = trim($stripped,'/');
255         $pagenum = $a->pager['page'];
256         $url = $a->get_baseurl() . '/' . $stripped;
257
258
259           if($a->pager['total'] > $a->pager['itemspage']) {
260                 $o .= '<div class="pager">';
261                 if($a->pager['page'] != 1)
262                         $o .= '<span class="pager_prev">'."<a href=\"$url".'&page='.($a->pager['page'] - 1).'">' . t('prev') . '</a></span> ';
263
264                 $o .=  "<span class=\"pager_first\"><a href=\"$url"."&page=1\">" . t('first') . "</a></span> ";
265
266                 $numpages = $a->pager['total'] / $a->pager['itemspage'];
267
268                         $numstart = 1;
269                 $numstop = $numpages;
270
271                 if($numpages > 14) {
272                         $numstart = (($pagenum > 7) ? ($pagenum - 7) : 1);
273                         $numstop = (($pagenum > ($numpages - 7)) ? $numpages : ($numstart + 14));
274                 }
275    
276                 for($i = $numstart; $i <= $numstop; $i++){
277                         if($i == $a->pager['page'])
278                                 $o .= '<span class="pager_current">'.(($i < 10) ? '&nbsp;'.$i : $i);
279                         else
280                                 $o .= "<span class=\"pager_n\"><a href=\"$url"."&page=$i\">".(($i < 10) ? '&nbsp;'.$i : $i)."</a>";
281                         $o .= '</span> ';
282                 }
283
284                 if(($a->pager['total'] % $a->pager['itemspage']) != 0) {
285                         if($i == $a->pager['page'])
286                                 $o .= '<span class="pager_current">'.(($i < 10) ? '&nbsp;'.$i : $i);
287                         else
288                                 $o .= "<span class=\"pager_n\"><a href=\"$url"."&page=$i\">".(($i < 10) ? '&nbsp;'.$i : $i)."</a>";
289                         $o .= '</span> ';
290                 }
291
292                 $lastpage = (($numpages > intval($numpages)) ? intval($numpages)+1 : $numpages);
293                 $o .= "<span class=\"pager_last\"><a href=\"$url"."&page=$lastpage\">" . t('last') . "</a></span> ";
294
295                 if(($a->pager['total'] - ($a->pager['itemspage'] * $a->pager['page'])) > 0)
296                         $o .= '<span class="pager_next">'."<a href=\"$url"."&page=".($a->pager['page'] + 1).'">' . t('next') . '</a></span>';
297                 $o .= '</div>'."\r\n";
298         }
299         return $o;
300 }}
301
302 if(! function_exists('alt_pager')) {
303 function alt_pager(&$a, $i) {
304         $o = '';
305         $stripped = preg_replace('/(&page=[0-9]*)/','',$a->query_string);
306         $stripped = str_replace('q=','',$stripped);
307         $stripped = trim($stripped,'/');
308         $pagenum = $a->pager['page'];
309         $url = $a->get_baseurl() . '/' . $stripped;
310
311         $o .= '<div class="pager">';
312
313         if($a->pager['page']>1)
314           $o .= "<a href=\"$url"."&page=".($a->pager['page'] - 1).'" class="pager_newer">' . t('newer') . '</a>';
315         if($i>0) {
316           if($a->pager['page']>1)
317                   $o .= "&nbsp;-&nbsp;";
318           $o .= "<a href=\"$url"."&page=".($a->pager['page'] + 1).'" class="pager_older">' . t('older') . '</a>';
319         }
320
321
322         $o .= '</div>'."\r\n";
323
324         return $o;
325 }}
326
327 // Turn user/group ACLs stored as angle bracketed text into arrays
328
329 if(! function_exists('expand_acl')) {
330 function expand_acl($s) {
331         // turn string array of angle-bracketed elements into numeric array
332         // e.g. "<1><2><3>" => array(1,2,3);
333         $ret = array();
334
335         if(strlen($s)) {
336                 $t = str_replace('<','',$s);
337                 $a = explode('>',$t);
338                 foreach($a as $aa) {
339                         if(intval($aa))
340                                 $ret[] = intval($aa);
341                 }
342         }
343         return $ret;
344 }}              
345
346 // Used to wrap ACL elements in angle brackets for storage 
347
348 if(! function_exists('sanitise_acl')) {
349 function sanitise_acl(&$item) {
350         if(intval($item))
351                 $item = '<' . intval(notags(trim($item))) . '>';
352         else
353                 unset($item);
354 }}
355
356
357 // Convert an ACL array to a storable string
358 // Normally ACL permissions will be an array.
359 // We'll also allow a comma-separated string.
360
361 if(! function_exists('perms2str')) {
362 function perms2str($p) {
363         $ret = '';
364
365         if(is_array($p))
366                 $tmp = $p;
367         else
368                 $tmp = explode(',',$p);
369
370         if(is_array($tmp)) {
371                 array_walk($tmp,'sanitise_acl');
372                 $ret = implode('',$tmp);
373         }
374         return $ret;
375 }}
376
377 // generate a guaranteed unique (for this domain) item ID for ATOM
378 // safe from birthday paradox
379
380 if(! function_exists('item_new_uri')) {
381 function item_new_uri($hostname,$uid) {
382
383         do {
384                 $dups = false;
385                 $hash = random_string();
386
387                 $uri = "urn:X-dfrn:" . $hostname . ':' . $uid . ':' . $hash;
388
389                 $r = q("SELECT `id` FROM `item` WHERE `uri` = '%s' LIMIT 1",
390                         dbesc($uri));
391                 if(count($r))
392                         $dups = true;
393         } while($dups == true);
394         return $uri;
395 }}
396
397 // Generate a guaranteed unique photo ID.
398 // safe from birthday paradox
399
400 if(! function_exists('photo_new_resource')) {
401 function photo_new_resource() {
402
403         do {
404                 $found = false;
405                 $resource = hash('md5',uniqid(mt_rand(),true));
406                 $r = q("SELECT `id` FROM `photo` WHERE `resource-id` = '%s' LIMIT 1",
407                         dbesc($resource)
408                 );
409                 if(count($r))
410                         $found = true;
411         } while($found == true);
412         return $resource;
413 }}
414
415
416 // wrapper to load a view template, checking for alternate
417 // languages before falling back to the default
418
419 // obsolete, deprecated.
420
421 if(! function_exists('load_view_file')) {
422 function load_view_file($s) {
423         global $lang, $a;
424         if(! isset($lang))
425                 $lang = 'en';
426         $b = basename($s);
427         $d = dirname($s);
428         if(file_exists("$d/$lang/$b"))
429                 return file_get_contents("$d/$lang/$b");
430         
431         $theme = current_theme();
432
433         if(file_exists("$d/theme/$theme/$b"))
434                 return file_get_contents("$d/theme/$theme/$b");
435                         
436         return file_get_contents($s);
437 }}
438
439 if(! function_exists('get_intltext_template')) {
440 function get_intltext_template($s) {
441         global $lang;
442
443         $a = get_app();
444         $engine = '';
445         if($a->theme['template_engine'] === 'smarty3')
446                 $engine = "/smarty3";
447
448         if(! isset($lang))
449                 $lang = 'en';
450
451         if(file_exists("view/$lang$engine/$s"))
452                 return file_get_contents("view/$lang$engine/$s");
453         elseif(file_exists("view/en$engine/$s"))
454                 return file_get_contents("view/en$engine/$s");
455         else
456                 return file_get_contents("view$engine/$s");
457 }}
458
459 if(! function_exists('get_markup_template')) {
460 function get_markup_template($s, $root = '') {
461 //      $ts = microtime();
462         $a = get_app();
463
464         if($a->theme['template_engine'] === 'smarty3') {
465                 $template_file = get_template_file($a, 'smarty3/' . $s, $root);
466
467                 $template = new FriendicaSmarty();
468                 $template->filename = $template_file;
469
470 //              $tt = microtime() - $ts;
471 //              $a->page['debug'] .= "$tt <br>\n";
472                 return $template;
473         }
474         else {
475                 $template_file = get_template_file($a, $s, $root);
476 //              $file_contents = file_get_contents($template_file);
477 //              $tt = microtime() - $ts;
478 //              $a->page['debug'] .= "$tt <br>\n";
479 //              return $file_contents;
480                 return file_get_contents($template_file);
481         }       
482 }}
483
484 if(! function_exists("get_template_file")) {
485 function get_template_file($a, $filename, $root = '') {
486         $theme = current_theme();
487
488         // Make sure $root ends with a slash /
489         if($root !== '' && $root[strlen($root)-1] !== '/')
490                 $root = $root . '/';
491
492         if(file_exists($root . "view/theme/$theme/$filename"))
493                 $template_file = $root . "view/theme/$theme/$filename";
494         elseif (x($a->theme_info,"extends") && file_exists($root . "view/theme/".$a->theme_info["extends"]."/$filename"))
495                 $template_file = $root . "view/theme/".$a->theme_info["extends"]."/$filename";
496         else
497                 $template_file = $root . "view/$filename";
498
499         return $template_file;
500 }}
501
502 if(! function_exists("set_template_includes")) {
503 function set_template_includes($engine, $includes) {
504         if($engine === 'smarty3') {
505                 $a = get_app();
506                 foreach($includes as $name=>$path) {
507 //                      $sm_includes[$name] = $_SERVER['DOCUMENT_ROOT'] . '/' . get_template_file($a, 'smarty3/' . $path);
508                         $sm_includes[$name] = get_template_file($a, 'smarty3/' . $path);
509                 }
510                 return $sm_includes;
511         }
512         else {
513                 return $includes;
514         }
515 }}
516
517
518
519
520
521 // for html,xml parsing - let's say you've got
522 // an attribute foobar="class1 class2 class3"
523 // and you want to find out if it contains 'class3'.
524 // you can't use a normal sub string search because you
525 // might match 'notclass3' and a regex to do the job is 
526 // possible but a bit complicated. 
527 // pass the attribute string as $attr and the attribute you 
528 // are looking for as $s - returns true if found, otherwise false
529
530 if(! function_exists('attribute_contains')) {
531 function attribute_contains($attr,$s) {
532         $a = explode(' ', $attr);
533         if(count($a) && in_array($s,$a))
534                 return true;
535         return false;
536 }}
537
538 if(! function_exists('logger')) {
539 function logger($msg,$level = 0) {
540         // turn off logger in install mode
541         global $a;
542         global $db;
543
544         if(($a->module == 'install') || (! ($db && $db->connected))) return;
545
546         $debugging = get_config('system','debugging');
547         $loglevel  = intval(get_config('system','loglevel'));
548         $logfile   = get_config('system','logfile');
549
550         if((! $debugging) || (! $logfile) || ($level > $loglevel))
551                 return;
552         
553         @file_put_contents($logfile, datetime_convert() . ':' . session_id() . ' ' . $msg . "\n", FILE_APPEND);
554         return;
555 }}
556
557
558 if(! function_exists('activity_match')) {
559 function activity_match($haystack,$needle) {
560         if(($haystack === $needle) || ((basename($needle) === $haystack) && strstr($needle,NAMESPACE_ACTIVITY_SCHEMA)))
561                 return true;
562         return false;
563 }}
564
565
566 // Pull out all #hashtags and @person tags from $s;
567 // We also get @person@domain.com - which would make 
568 // the regex quite complicated as tags can also
569 // end a sentence. So we'll run through our results
570 // and strip the period from any tags which end with one.
571 // Returns array of tags found, or empty array.
572
573
574 if(! function_exists('get_tags')) {
575 function get_tags($s) {
576         $ret = array();
577
578         // ignore anything in a code block
579
580         $s = preg_replace('/\[code\](.*?)\[\/code\]/sm','',$s);
581
582         // ignore anything in a bbtag
583
584         $s = preg_replace('/\[(.*?)\]/sm','',$s);
585
586         // Match full names against @tags including the space between first and last
587         // We will look these up afterward to see if they are full names or not recognisable.
588
589         if(preg_match_all('/(@[^ \x0D\x0A,:?]+ [^ \x0D\x0A@,:?]+)([ \x0D\x0A@,:?]|$)/',$s,$match)) {
590                 foreach($match[1] as $mtch) {
591                         if(strstr($mtch,"]")) {
592                                 // we might be inside a bbcode color tag - leave it alone
593                                 continue;
594                         }
595                         if(substr($mtch,-1,1) === '.')
596                                 $ret[] = substr($mtch,0,-1);
597                         else
598                                 $ret[] = $mtch;
599                 }
600         }
601
602         // Otherwise pull out single word tags. These can be @nickname, @first_last
603         // and #hash tags.
604
605         if(preg_match_all('/([@#][^ \x0D\x0A,;:?]+)([ \x0D\x0A,;:?]|$)/',$s,$match)) {
606                 foreach($match[1] as $mtch) {
607                         if(strstr($mtch,"]")) {
608                                 // we might be inside a bbcode color tag - leave it alone
609                                 continue;
610                         }
611                         if(substr($mtch,-1,1) === '.')
612                                 $mtch = substr($mtch,0,-1);
613                         // ignore strictly numeric tags like #1
614                         if((strpos($mtch,'#') === 0) && ctype_digit(substr($mtch,1)))
615                                 continue;
616                         // try not to catch url fragments
617                         if(strpos($s,$mtch) && preg_match('/[a-zA-z0-9\/]/',substr($s,strpos($s,$mtch)-1,1)))
618                                 continue;
619                         $ret[] = $mtch;
620                 }
621         }
622         return $ret;
623 }}
624
625
626 // quick and dirty quoted_printable encoding
627
628 if(! function_exists('qp')) {
629 function qp($s) {
630 return str_replace ("%","=",rawurlencode($s));
631 }} 
632
633
634
635 if(! function_exists('get_mentions')) {
636 function get_mentions($item) {
637         $o = '';
638         if(! strlen($item['tag']))
639                 return $o;
640
641         $arr = explode(',',$item['tag']);
642         foreach($arr as $x) {
643                 $matches = null;
644                 if(preg_match('/@\[url=([^\]]*)\]/',$x,$matches)) {
645                         $o .= "\t\t" . '<link rel="mentioned" href="' . $matches[1] . '" />' . "\r\n";
646                         $o .= "\t\t" . '<link rel="ostatus:attention" href="' . $matches[1] . '" />' . "\r\n";
647                 }
648         }
649         return $o;
650 }}
651
652 if(! function_exists('contact_block')) {
653 function contact_block() {
654         $o = '';
655         $a = get_app();
656
657         $shown = get_pconfig($a->profile['uid'],'system','display_friend_count');
658         if($shown === false)
659                 $shown = 24;
660         if($shown == 0)
661                 return;
662
663         if((! is_array($a->profile)) || ($a->profile['hide-friends']))
664                 return $o;
665         $r = q("SELECT COUNT(*) AS `total` FROM `contact` WHERE `uid` = %d AND `self` = 0 AND `blocked` = 0 and `pending` = 0 AND `hidden` = 0 AND `archive` = 0",
666                         intval($a->profile['uid'])
667         );
668         if(count($r)) {
669                 $total = intval($r[0]['total']);
670         }
671         if(! $total) {
672                 $contacts = t('No contacts');
673                 $micropro = Null;
674                 
675         } else {
676                 $r = q("SELECT * FROM `contact` WHERE `uid` = %d AND `self` = 0 AND `blocked` = 0 and `pending` = 0 AND `hidden` = 0 AND `archive` = 0 ORDER BY RAND() LIMIT %d",
677                                 intval($a->profile['uid']),
678                                 intval($shown)
679                 );
680                 if(count($r)) {
681                         $contacts = sprintf( tt('%d Contact','%d Contacts', $total),$total);
682                         $micropro = Array();
683                         foreach($r as $rr) {
684                                 $micropro[] = micropro($rr,true,'mpfriend');
685                         }
686                 }
687         }
688         
689         $tpl = get_markup_template('contact_block.tpl');
690         $o = replace_macros($tpl, array(
691                 '$contacts' => $contacts,
692                 '$nickname' => $a->profile['nickname'],
693                 '$viewcontacts' => t('View Contacts'),
694                 '$micropro' => $micropro,
695         ));
696
697         $arr = array('contacts' => $r, 'output' => $o);
698
699         call_hooks('contact_block_end', $arr);
700         return $o;
701
702 }}
703
704 if(! function_exists('micropro')) {
705 function micropro($contact, $redirect = false, $class = '', $textmode = false) {
706
707         if($class)
708                 $class = ' ' . $class;
709
710         $url = $contact['url'];
711         $sparkle = '';
712         $redir = false;
713
714         if($redirect) {
715                 $a = get_app();
716                 $redirect_url = $a->get_baseurl() . '/redir/' . $contact['id'];
717                 if(local_user() && ($contact['uid'] == local_user()) && ($contact['network'] === 'dfrn')) {
718                         $redir = true;
719                         $url = $redirect_url;
720                         $sparkle = ' sparkle';
721                 }
722                 else
723                         $url = zrl($url);
724         }
725         $click = ((x($contact,'click')) ? ' onclick="' . $contact['click'] . '" ' : '');
726         if($click)
727                 $url = '';
728         if($textmode) {
729                 return '<div class="contact-block-textdiv' . $class . '"><a class="contact-block-link' . $class . $sparkle 
730                         . (($click) ? ' fakelink' : '') . '" '
731                         . (($redir) ? ' target="redir" ' : '')
732                         . (($url) ? ' href="' . $url . '"' : '') . $click
733                         . '" title="' . $contact['name'] . ' [' . $contact['url'] . ']" alt="' . $contact['name'] 
734                         . '" >'. $contact['name'] . '</a></div>' . "\r\n";
735         }
736         else {
737                 return '<div class="contact-block-div' . $class . '"><a class="contact-block-link' . $class . $sparkle 
738                         . (($click) ? ' fakelink' : '') . '" '
739                         . (($redir) ? ' target="redir" ' : '')
740                         . (($url) ? ' href="' . $url . '"' : '') . $click . ' ><img class="contact-block-img' . $class . $sparkle . '" src="' 
741                         . $contact['micro'] . '" title="' . $contact['name'] . ' [' . $contact['url'] . ']" alt="' . $contact['name'] 
742                         . '" /></a></div>' . "\r\n";
743         }
744 }}
745
746
747
748 if(! function_exists('search')) {
749 function search($s,$id='search-box',$url='/search',$save = false) {
750         $a = get_app();
751         $o  = '<div id="' . $id . '">';
752         $o .= '<form action="' . $a->get_baseurl((stristr($url,'network')) ? true : false) . $url . '" method="get" >';
753         $o .= '<input type="text" name="search" id="search-text" placeholder="' . t('Search') . '" value="' . $s .'" />';
754         $o .= '<input type="submit" name="submit" id="search-submit" value="' . t('Search') . '" />'; 
755         if($save)
756                 $o .= '<input type="submit" name="save" id="search-save" value="' . t('Save') . '" />'; 
757         $o .= '</form></div>';
758         return $o;
759 }}
760
761 if(! function_exists('valid_email')) {
762 function valid_email($x){
763
764         if(get_config('system','disable_email_validation'))
765                 return true;
766
767         if(preg_match('/^[_a-zA-Z0-9\-\+]+(\.[_a-zA-Z0-9\-\+]+)*@[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+)+$/',$x))
768                 return true;
769         return false;
770 }}
771
772
773 /**
774  *
775  * Function: linkify
776  *
777  * Replace naked text hyperlink with HTML formatted hyperlink
778  *
779  */
780
781 if(! function_exists('linkify')) {
782 function linkify($s) {
783         $s = preg_replace("/(https?\:\/\/[a-zA-Z0-9\:\/\-\?\&\;\.\=\_\~\#\'\%\$\!\+]*)/", ' <a href="$1" target="external-link">$1</a>', $s);
784         $s = preg_replace("/\<(.*?)(src|href)=(.*?)\&amp\;(.*?)\>/ism",'<$1$2=$3&$4>',$s);
785         return($s);
786 }}
787
788 function get_poke_verbs() {
789         
790         // index is present tense verb
791         // value is array containing past tense verb, translation of present, translation of past
792
793         $arr = array(
794                 'poke' => array( 'poked', t('poke'), t('poked')),
795                 'ping' => array( 'pinged', t('ping'), t('pinged')),
796                 'prod' => array( 'prodded', t('prod'), t('prodded')),
797                 'slap' => array( 'slapped', t('slap'), t('slapped')),
798                 'finger' => array( 'fingered', t('finger'), t('fingered')),
799                 'rebuff' => array( 'rebuffed', t('rebuff'), t('rebuffed')),
800         );
801         call_hooks('poke_verbs', $arr);
802         return $arr;
803 }
804
805 function get_mood_verbs() {
806         
807         // index is present tense verb
808         // value is array containing past tense verb, translation of present, translation of past
809
810         $arr = array(
811                 'happy'      => t('happy'),
812                 'sad'        => t('sad'),
813                 'mellow'     => t('mellow'),
814                 'tired'      => t('tired'),
815                 'perky'      => t('perky'),
816                 'angry'      => t('angry'),
817                 'stupefied'  => t('stupified'),
818                 'puzzled'    => t('puzzled'),
819                 'interested' => t('interested'),
820                 'bitter'     => t('bitter'),
821                 'cheerful'   => t('cheerful'),
822                 'alive'      => t('alive'),
823                 'annoyed'    => t('annoyed'),
824                 'anxious'    => t('anxious'),
825                 'cranky'     => t('cranky'),
826                 'disturbed'  => t('disturbed'),
827                 'frustrated' => t('frustrated'),
828                 'motivated'  => t('motivated'),
829                 'relaxed'    => t('relaxed'),
830                 'surprised'  => t('surprised'),
831         );
832
833         call_hooks('mood_verbs', $arr);
834         return $arr;
835 }
836
837
838 /**
839  * 
840  * Function: smilies
841  *
842  * Description:
843  * Replaces text emoticons with graphical images
844  *
845  * @Parameter: string $s
846  *
847  * Returns string
848  *
849  * It is expected that this function will be called using HTML text.
850  * We will escape text between HTML pre and code blocks from being 
851  * processed. 
852  * 
853  * At a higher level, the bbcode [nosmile] tag can be used to prevent this 
854  * function from being executed by the prepare_text() routine when preparing
855  * bbcode source for HTML display
856  *
857  */
858
859 if(! function_exists('smilies')) {
860 function smilies($s, $sample = false) {
861
862         $a = get_app();
863
864         if(intval(get_config('system','no_smilies')) 
865                 || (local_user() && intval(get_pconfig(local_user(),'system','no_smilies'))))
866                 return $s;
867
868         $s = preg_replace_callback('/<pre>(.*?)<\/pre>/ism','smile_encode',$s);
869         $s = preg_replace_callback('/<code>(.*?)<\/code>/ism','smile_encode',$s);
870
871         $texts =  array( 
872                 '&lt;3', 
873                 '&lt;/3', 
874                 '&lt;\\3', 
875                 ':-)', 
876                 ';-)', 
877                 ':-(', 
878                 ':-P', 
879                 ':-p', 
880                 ':-"', 
881                 ':-&quot;', 
882                 ':-x', 
883                 ':-X', 
884                 ':-D', 
885                 '8-|', 
886                 '8-O', 
887                 ':-O', 
888                 '\\o/', 
889                 'o.O', 
890                 'O.o', 
891                 'o_O', 
892                 'O_o', 
893                 ":'(", 
894                 ":-!", 
895                 ":-/", 
896                 ":-[", 
897                 "8-)",
898                 ':beer', 
899                 ':homebrew', 
900                 ':coffee', 
901                 ':facepalm',
902                 ':like',
903                 ':dislike',
904                 '~friendica'
905
906         );
907
908         $icons = array(
909                 '<img class="smiley" src="' . $a->get_baseurl() . '/images/smiley-heart.gif" alt="<3" />',
910                 '<img class="smiley" src="' . $a->get_baseurl() . '/images/smiley-brokenheart.gif" alt="</3" />',
911                 '<img class="smiley" src="' . $a->get_baseurl() . '/images/smiley-brokenheart.gif" alt="<\\3" />',
912                 '<img class="smiley" src="' . $a->get_baseurl() . '/images/smiley-smile.gif" alt=":-)" />',
913                 '<img class="smiley" src="' . $a->get_baseurl() . '/images/smiley-wink.gif" alt=";-)" />',
914                 '<img class="smiley" src="' . $a->get_baseurl() . '/images/smiley-frown.gif" alt=":-(" />',
915                 '<img class="smiley" src="' . $a->get_baseurl() . '/images/smiley-tongue-out.gif" alt=":-P" />',
916                 '<img class="smiley" src="' . $a->get_baseurl() . '/images/smiley-tongue-out.gif" alt=":-p" />',
917                 '<img class="smiley" src="' . $a->get_baseurl() . '/images/smiley-kiss.gif" alt=":-\"" />',
918                 '<img class="smiley" src="' . $a->get_baseurl() . '/images/smiley-kiss.gif" alt=":-\"" />',
919                 '<img class="smiley" src="' . $a->get_baseurl() . '/images/smiley-kiss.gif" alt=":-x" />',
920                 '<img class="smiley" src="' . $a->get_baseurl() . '/images/smiley-kiss.gif" alt=":-X" />',
921                 '<img class="smiley" src="' . $a->get_baseurl() . '/images/smiley-laughing.gif" alt=":-D" />',
922                 '<img class="smiley" src="' . $a->get_baseurl() . '/images/smiley-surprised.gif" alt="8-|" />',
923                 '<img class="smiley" src="' . $a->get_baseurl() . '/images/smiley-surprised.gif" alt="8-O" />',
924                 '<img class="smiley" src="' . $a->get_baseurl() . '/images/smiley-surprised.gif" alt=":-O" />',                
925                 '<img class="smiley" src="' . $a->get_baseurl() . '/images/smiley-thumbsup.gif" alt="\\o/" />',
926                 '<img class="smiley" src="' . $a->get_baseurl() . '/images/smiley-Oo.gif" alt="o.O" />',
927                 '<img class="smiley" src="' . $a->get_baseurl() . '/images/smiley-Oo.gif" alt="O.o" />',
928                 '<img class="smiley" src="' . $a->get_baseurl() . '/images/smiley-Oo.gif" alt="o_O" />',
929                 '<img class="smiley" src="' . $a->get_baseurl() . '/images/smiley-Oo.gif" alt="O_o" />',
930                 '<img class="smiley" src="' . $a->get_baseurl() . '/images/smiley-cry.gif" alt=":\'(" />',
931                 '<img class="smiley" src="' . $a->get_baseurl() . '/images/smiley-foot-in-mouth.gif" alt=":-!" />',
932                 '<img class="smiley" src="' . $a->get_baseurl() . '/images/smiley-undecided.gif" alt=":-/" />',
933                 '<img class="smiley" src="' . $a->get_baseurl() . '/images/smiley-embarassed.gif" alt=":-[" />',
934                 '<img class="smiley" src="' . $a->get_baseurl() . '/images/smiley-cool.gif" alt="8-)" />',
935                 '<img class="smiley" src="' . $a->get_baseurl() . '/images/beer_mug.gif" alt=":beer" />',
936                 '<img class="smiley" src="' . $a->get_baseurl() . '/images/beer_mug.gif" alt=":homebrew" />',
937                 '<img class="smiley" src="' . $a->get_baseurl() . '/images/coffee.gif" alt=":coffee" />',
938                 '<img class="smiley" src="' . $a->get_baseurl() . '/images/smiley-facepalm.gif" alt=":facepalm" />',
939                 '<img class="smiley" src="' . $a->get_baseurl() . '/images/like.gif" alt=":like" />',
940                 '<img class="smiley" src="' . $a->get_baseurl() . '/images/dislike.gif" alt=":dislike" />',
941                 '<a href="http://friendica.com">~friendica <img class="smiley" src="' . $a->get_baseurl() . '/images/friendica-16.png" alt="~friendica" /></a>'
942         );
943
944         $params = array('texts' => $texts, 'icons' => $icons, 'string' => $s);
945         call_hooks('smilie', $params);
946
947         if($sample) {
948                 $s = '<div class="smiley-sample">';
949                 for($x = 0; $x < count($params['texts']); $x ++) {
950                         $s .= '<dl><dt>' . $params['texts'][$x] . '</dt><dd>' . $params['icons'][$x] . '</dd></dl>';
951                 }
952         }
953         else {
954                 $params['string'] = preg_replace_callback('/&lt;(3+)/','preg_heart',$params['string']);
955                 $s = str_replace($params['texts'],$params['icons'],$params['string']);
956         }
957
958         $s = preg_replace_callback('/<pre>(.*?)<\/pre>/ism','smile_decode',$s);
959         $s = preg_replace_callback('/<code>(.*?)<\/code>/ism','smile_decode',$s);
960
961         return $s;
962
963 }}
964
965 function smile_encode($m) {
966         return(str_replace($m[1],base64url_encode($m[1]),$m[0]));
967 }
968
969 function smile_decode($m) {
970         return(str_replace($m[1],base64url_decode($m[1]),$m[0]));
971 }
972
973 // expand <3333 to the correct number of hearts
974
975 function preg_heart($x) {
976         $a = get_app();
977         if(strlen($x[1]) == 1)
978                 return $x[0];
979         $t = '';
980         for($cnt = 0; $cnt < strlen($x[1]); $cnt ++)
981                 $t .= '<img class="smiley" src="' . $a->get_baseurl() . '/images/smiley-heart.gif" alt="<3" />';
982         $r =  str_replace($x[0],$t,$x[0]);
983         return $r;
984 }
985
986
987 if(! function_exists('day_translate')) {
988 function day_translate($s) {
989         $ret = str_replace(array('Monday','Tuesday','Wednesday','Thursday','Friday','Saturday','Sunday'),
990                 array( t('Monday'), t('Tuesday'), t('Wednesday'), t('Thursday'), t('Friday'), t('Saturday'), t('Sunday')),
991                 $s);
992
993         $ret = str_replace(array('January','February','March','April','May','June','July','August','September','October','November','December'),
994                 array( t('January'), t('February'), t('March'), t('April'), t('May'), t('June'), t('July'), t('August'), t('September'), t('October'), t('November'), t('December')),
995                 $ret);
996
997         return $ret;
998 }}
999
1000
1001 if(! function_exists('normalise_link')) {
1002 function normalise_link($url) {
1003         $ret = str_replace(array('https:','//www.'), array('http:','//'), $url);
1004         return(rtrim($ret,'/'));
1005 }}
1006
1007 /**
1008  *
1009  * Compare two URLs to see if they are the same, but ignore
1010  * slight but hopefully insignificant differences such as if one 
1011  * is https and the other isn't, or if one is www.something and 
1012  * the other isn't - and also ignore case differences.
1013  *
1014  * Return true if the URLs match, otherwise false.
1015  *
1016  */
1017
1018 if(! function_exists('link_compare')) {
1019 function link_compare($a,$b) {
1020         if(strcasecmp(normalise_link($a),normalise_link($b)) === 0)
1021                 return true;
1022         return false;
1023 }}
1024
1025 // Given an item array, convert the body element from bbcode to html and add smilie icons.
1026 // If attach is true, also add icons for item attachments
1027
1028
1029 if(! function_exists('prepare_body')) {
1030 function prepare_body($item,$attach = false) {
1031
1032         $a = get_app();
1033         call_hooks('prepare_body_init', $item);
1034
1035         $cachefile = get_cachefile($item["guid"]."-".strtotime($item["edited"])."-".hash("crc32", $item['body']));
1036
1037         if (($cachefile != '')) {
1038                 if (file_exists($cachefile))
1039                         $s = file_get_contents($cachefile);
1040                 else {
1041                         $s = prepare_text($item['body']);
1042                         file_put_contents($cachefile, $s);
1043                 }
1044         } else
1045                 $s = prepare_text($item['body']);
1046
1047
1048         $prep_arr = array('item' => $item, 'html' => $s);
1049         call_hooks('prepare_body', $prep_arr);
1050         $s = $prep_arr['html'];
1051
1052         if(! $attach) {
1053                 // Replace the blockquotes with quotes that are used in mails
1054                 $mailquote = '<blockquote type="cite" class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex;">';
1055                 $s = str_replace(array('<blockquote>', '<blockquote class="spoiler">', '<blockquote class="author">'), array($mailquote, $mailquote, $mailquote), $s);
1056                 return $s;
1057         }
1058
1059         $arr = explode(',',$item['attach']);
1060         if(count($arr)) {
1061                 $s .= '<div class="body-attach">';
1062                 foreach($arr as $r) {
1063                         $matches = false;
1064                         $icon = '';
1065                         $cnt = preg_match_all('|\[attach\]href=\"(.*?)\" length=\"(.*?)\" type=\"(.*?)\" title=\"(.*?)\"\[\/attach\]|',$r,$matches, PREG_SET_ORDER);
1066                         if($cnt) {
1067                                 foreach($matches as $mtch) {
1068                                         $icontype = strtolower(substr($mtch[3],0,strpos($mtch[3],'/')));
1069                                         switch($icontype) {
1070                                                 case 'video':
1071                                                 case 'audio':
1072                                                 case 'image':
1073                                                 case 'text':
1074                                                         $icon = '<div class="attachtype icon s22 type-' . $icontype . '"></div>';
1075                                                         break;
1076                                                 default:
1077                                                         $icon = '<div class="attachtype icon s22 type-unkn"></div>';
1078                                                         break;
1079                                         }
1080                                         $title = ((strlen(trim($mtch[4]))) ? escape_tags(trim($mtch[4])) : escape_tags($mtch[1]));
1081                                         $title .= ' ' . $mtch[2] . ' ' . t('bytes');
1082                                         if((local_user() == $item['uid']) && ($item['contact-id'] != $a->contact['id']) && ($item['network'] == NETWORK_DFRN))
1083                                                 $the_url = $a->get_baseurl() . '/redir/' . $item['contact-id'] . '?f=1&url=' . $mtch[1];
1084                                         else
1085                                                 $the_url = $mtch[1];
1086
1087                                         $s .= '<a href="' . strip_tags($the_url) . '" title="' . $title . '" class="attachlink" target="external-link" >' . $icon . '</a>';
1088                                 }
1089                         }
1090                 }
1091                 $s .= '<div class="clear"></div></div>';
1092         }
1093
1094
1095         // Look for spoiler
1096         $spoilersearch = '<blockquote class="spoiler">';
1097
1098         // Remove line breaks before the spoiler
1099         while ((strpos($s, "\n".$spoilersearch) !== false))
1100                 $s = str_replace("\n".$spoilersearch, $spoilersearch, $s);
1101         while ((strpos($s, "<br />".$spoilersearch) !== false))
1102                 $s = str_replace("<br />".$spoilersearch, $spoilersearch, $s);
1103
1104         while ((strpos($s, $spoilersearch) !== false)) {
1105
1106                 $pos = strpos($s, $spoilersearch);
1107                 $rnd = random_string(8);
1108                 $spoilerreplace = '<br /> <span id="spoiler-wrap-'.$rnd.'" style="white-space:nowrap;" class="fakelink" onclick="openClose(\'spoiler-'.$rnd.'\');">'.sprintf(t('Click to open/close')).'</span>'.
1109                                         '<blockquote class="spoiler" id="spoiler-'.$rnd.'" style="display: none;">';
1110                 $s = substr($s, 0, $pos).$spoilerreplace.substr($s, $pos+strlen($spoilersearch));
1111         }
1112
1113         // Look for quote with author
1114         $authorsearch = '<blockquote class="author">';
1115
1116         while ((strpos($s, $authorsearch) !== false)) {
1117
1118                 $pos = strpos($s, $authorsearch);
1119                 $rnd = random_string(8);
1120                 $authorreplace = '<br /> <span id="author-wrap-'.$rnd.'" style="white-space:nowrap;" class="fakelink" onclick="openClose(\'author-'.$rnd.'\');">'.sprintf(t('Click to open/close')).'</span>'.
1121                                         '<blockquote class="author" id="author-'.$rnd.'" style="display: block;">';
1122                 $s = substr($s, 0, $pos).$authorreplace.substr($s, $pos+strlen($authorsearch));
1123         }
1124
1125         $prep_arr = array('item' => $item, 'html' => $s);
1126         call_hooks('prepare_body_final', $prep_arr);
1127
1128         return $prep_arr['html'];
1129 }}
1130
1131
1132 // Given a text string, convert from bbcode to html and add smilie icons.
1133
1134 if(! function_exists('prepare_text')) {
1135 function prepare_text($text) {
1136
1137         require_once('include/bbcode.php');
1138
1139         if(stristr($text,'[nosmile]'))
1140                 $s = bbcode($text);
1141         else
1142                 $s = smilies(bbcode($text));
1143
1144         return $s;
1145 }}
1146
1147
1148 /**
1149  * returns 
1150  * [
1151  *    //categories [
1152  *          {
1153  *               'name': 'category name',
1154  *              'removeurl': 'url to remove this category',
1155  *             'first': 'is the first in this array? true/false',
1156  *               'last': 'is the last in this array? true/false',
1157  *           } ,
1158  *           ....
1159  *       ],
1160  *       // folders [
1161  *               'name': 'folder name',
1162  *               'removeurl': 'url to remove this folder',
1163  *               'first': 'is the first in this array? true/false',
1164  *               'last': 'is the last in this array? true/false',
1165  *           } ,
1166  *           ....       
1167  *       ]
1168  *   ]
1169  */
1170 function get_cats_and_terms($item) {
1171     $a = get_app();
1172     $categories = array();
1173     $folders = array();
1174
1175     $matches = false; $first = true;
1176     $cnt = preg_match_all('/<(.*?)>/',$item['file'],$matches,PREG_SET_ORDER);
1177     if($cnt) {
1178         foreach($matches as $mtch) {
1179             $categories[] = array(
1180                 'name' => xmlify(file_tag_decode($mtch[1])),
1181                 'url' =>  "#",
1182                 'removeurl' => ((local_user() == $item['uid'])?$a->get_baseurl() . '/filerm/' . $item['id'] . '?f=&cat=' . xmlify(file_tag_decode($mtch[1])):""),
1183                 'first' => $first,
1184                 'last' => false
1185             );
1186             $first = false;
1187         }
1188     }
1189     if (count($categories)) $categories[count($categories)-1]['last'] = true;
1190     
1191
1192         if(local_user() == $item['uid']) {
1193             $matches = false; $first = true;
1194         $cnt = preg_match_all('/\[(.*?)\]/',$item['file'],$matches,PREG_SET_ORDER);
1195             if($cnt) {
1196             foreach($matches as $mtch) {
1197                     $folders[] = array(
1198                     'name' => xmlify(file_tag_decode($mtch[1])),
1199                          'url' =>  "#",
1200                         'removeurl' => ((local_user() == $item['uid'])?$a->get_baseurl() . '/filerm/' . $item['id'] . '?f=&term=' . xmlify(file_tag_decode($mtch[1])):""),
1201                     'first' => $first,
1202                         'last' => false
1203                 );
1204                     $first = false;
1205                         }
1206         }
1207     }
1208
1209     if (count($folders)) $folders[count($folders)-1]['last'] = true;
1210
1211     return array($categories, $folders);
1212 }
1213
1214
1215 /**
1216  * return atom link elements for all of our hubs
1217  */
1218
1219 if(! function_exists('feed_hublinks')) {
1220 function feed_hublinks() {
1221
1222         $hub = get_config('system','huburl');
1223
1224         $hubxml = '';
1225         if(strlen($hub)) {
1226                 $hubs = explode(',', $hub);
1227                 if(count($hubs)) {
1228                         foreach($hubs as $h) {
1229                                 $h = trim($h);
1230                                 if(! strlen($h))
1231                                         continue;
1232                                 $hubxml .= '<link rel="hub" href="' . xmlify($h) . '" />' . "\n" ;
1233                         }
1234                 }
1235         }
1236         return $hubxml;
1237 }}
1238
1239 /* return atom link elements for salmon endpoints */
1240
1241 if(! function_exists('feed_salmonlinks')) {
1242 function feed_salmonlinks($nick) {
1243
1244         $a = get_app();
1245
1246         $salmon  = '<link rel="salmon" href="' . xmlify($a->get_baseurl() . '/salmon/' . $nick) . '" />' . "\n" ;
1247
1248         // old style links that status.net still needed as of 12/2010 
1249
1250         $salmon .= '  <link rel="http://salmon-protocol.org/ns/salmon-replies" href="' . xmlify($a->get_baseurl() . '/salmon/' . $nick) . '" />' . "\n" ; 
1251         $salmon .= '  <link rel="http://salmon-protocol.org/ns/salmon-mention" href="' . xmlify($a->get_baseurl() . '/salmon/' . $nick) . '" />' . "\n" ; 
1252         return $salmon;
1253 }}
1254
1255 if(! function_exists('get_plink')) {
1256 function get_plink($item) {
1257         $a = get_app(); 
1258         if (x($item,'plink') && ($item['private'] != 1)) {
1259                 return array(
1260                         'href' => $item['plink'],
1261                         'title' => t('link to source'),
1262                 );
1263         } 
1264         else {
1265                 return false;
1266         }
1267 }}
1268
1269 if(! function_exists('unamp')) {
1270 function unamp($s) {
1271         return str_replace('&amp;', '&', $s);
1272 }}
1273
1274
1275
1276
1277 if(! function_exists('lang_selector')) {
1278 function lang_selector() {
1279         global $lang;
1280         
1281         $langs = glob('view/*/strings.php');
1282         
1283         $lang_options = array();
1284         $selected = "";
1285         
1286         if(is_array($langs) && count($langs)) {
1287                 $langs[] = '';
1288                 if(! in_array('view/en/strings.php',$langs))
1289                         $langs[] = 'view/en/';
1290                 asort($langs);
1291                 foreach($langs as $l) {
1292                         if($l == '') {
1293                                 $lang_options[""] = t('default');
1294                                 continue;
1295                         }
1296                         $ll = substr($l,5);
1297                         $ll = substr($ll,0,strrpos($ll,'/'));
1298                         $selected = (($ll === $lang && (x($_SESSION, 'language'))) ? $ll : $selected);
1299                         $lang_options[$ll]=$ll;
1300                 }
1301         }
1302
1303         $tpl = get_markup_template("lang_selector.tpl");        
1304         $o = replace_macros($tpl, array(
1305                 '$title' => t('Select an alternate language'),
1306                 '$langs' => array($lang_options, $selected),
1307                 
1308         ));
1309         return $o;
1310 }}
1311
1312
1313 if(! function_exists('return_bytes')) {
1314 function return_bytes ($size_str) {
1315     switch (substr ($size_str, -1))
1316     {
1317         case 'M': case 'm': return (int)$size_str * 1048576;
1318         case 'K': case 'k': return (int)$size_str * 1024;
1319         case 'G': case 'g': return (int)$size_str * 1073741824;
1320         default: return $size_str;
1321     }
1322 }}
1323
1324 function generate_user_guid() {
1325         $found = true;
1326         do {
1327                 $guid = random_string(16);
1328                 $x = q("SELECT `uid` FROM `user` WHERE `guid` = '%s' LIMIT 1",
1329                         dbesc($guid)
1330                 );
1331                 if(! count($x))
1332                         $found = false;
1333         } while ($found == true );
1334         return $guid;
1335 }
1336
1337
1338
1339 function base64url_encode($s, $strip_padding = false) {
1340
1341         $s = strtr(base64_encode($s),'+/','-_');
1342
1343         if($strip_padding)
1344                 $s = str_replace('=','',$s);
1345
1346         return $s;
1347 }
1348
1349 function base64url_decode($s) {
1350
1351         if(is_array($s)) {
1352                 logger('base64url_decode: illegal input: ' . print_r(debug_backtrace(), true));
1353                 return $s;
1354         }
1355
1356 /*
1357  *  // Placeholder for new rev of salmon which strips base64 padding.
1358  *  // PHP base64_decode handles the un-padded input without requiring this step
1359  *  // Uncomment if you find you need it.
1360  *
1361  *      $l = strlen($s);
1362  *      if(! strpos($s,'=')) {
1363  *              $m = $l % 4;
1364  *              if($m == 2)
1365  *                      $s .= '==';
1366  *              if($m == 3)
1367  *                      $s .= '=';
1368  *      }
1369  *
1370  */
1371
1372         return base64_decode(strtr($s,'-_','+/'));
1373 }
1374
1375
1376 if (!function_exists('str_getcsv')) {
1377     function str_getcsv($input, $delimiter = ',', $enclosure = '"', $escape = '\\', $eol = '\n') {
1378         if (is_string($input) && !empty($input)) {
1379             $output = array();
1380             $tmp    = preg_split("/".$eol."/",$input);
1381             if (is_array($tmp) && !empty($tmp)) {
1382                 while (list($line_num, $line) = each($tmp)) {
1383                     if (preg_match("/".$escape.$enclosure."/",$line)) {
1384                         while ($strlen = strlen($line)) {
1385                             $pos_delimiter       = strpos($line,$delimiter);
1386                             $pos_enclosure_start = strpos($line,$enclosure);
1387                             if (
1388                                 is_int($pos_delimiter) && is_int($pos_enclosure_start)
1389                                 && ($pos_enclosure_start < $pos_delimiter)
1390                                 ) {
1391                                 $enclosed_str = substr($line,1);
1392                                 $pos_enclosure_end = strpos($enclosed_str,$enclosure);
1393                                 $enclosed_str = substr($enclosed_str,0,$pos_enclosure_end);
1394                                 $output[$line_num][] = $enclosed_str;
1395                                 $offset = $pos_enclosure_end+3;
1396                             } else {
1397                                 if (empty($pos_delimiter) && empty($pos_enclosure_start)) {
1398                                     $output[$line_num][] = substr($line,0);
1399                                     $offset = strlen($line);
1400                                 } else {
1401                                     $output[$line_num][] = substr($line,0,$pos_delimiter);
1402                                     $offset = (
1403                                                 !empty($pos_enclosure_start)
1404                                                 && ($pos_enclosure_start < $pos_delimiter)
1405                                                 )
1406                                                 ?$pos_enclosure_start
1407                                                 :$pos_delimiter+1;
1408                                 }
1409                             }
1410                             $line = substr($line,$offset);
1411                         }
1412                     } else {
1413                         $line = preg_split("/".$delimiter."/",$line);
1414    
1415                         /*
1416                          * Validating against pesky extra line breaks creating false rows.
1417                          */
1418                         if (is_array($line) && !empty($line[0])) {
1419                             $output[$line_num] = $line;
1420                         } 
1421                     }
1422                 }
1423                 return $output;
1424             } else {
1425                 return false;
1426             }
1427         } else {
1428             return false;
1429         }
1430     }
1431
1432
1433 function cleardiv() {
1434         return '<div class="clear"></div>';
1435 }
1436
1437
1438 function bb_translate_video($s) {
1439
1440         $matches = null;
1441         $r = preg_match_all("/\[video\](.*?)\[\/video\]/ism",$s,$matches,PREG_SET_ORDER);
1442         if($r) {
1443                 foreach($matches as $mtch) {
1444                         if((stristr($mtch[1],'youtube')) || (stristr($mtch[1],'youtu.be')))
1445                                 $s = str_replace($mtch[0],'[youtube]' . $mtch[1] . '[/youtube]',$s);
1446                         elseif(stristr($mtch[1],'vimeo'))
1447                                 $s = str_replace($mtch[0],'[vimeo]' . $mtch[1] . '[/vimeo]',$s);
1448                 }
1449         }
1450         return $s;      
1451 }
1452
1453 function html2bb_video($s) {
1454
1455         $s = preg_replace('#<object[^>]+>(.*?)https?://www.youtube.com/((?:v|cp)/[A-Za-z0-9\-_=]+)(.*?)</object>#ism',
1456                         '[youtube]$2[/youtube]', $s);
1457
1458         $s = preg_replace('#<iframe[^>](.*?)https?://www.youtube.com/embed/([A-Za-z0-9\-_=]+)(.*?)</iframe>#ism',
1459                         '[youtube]$2[/youtube]', $s);
1460
1461         $s = preg_replace('#<iframe[^>](.*?)https?://player.vimeo.com/video/([0-9]+)(.*?)</iframe>#ism',
1462                         '[vimeo]$2[/vimeo]', $s);
1463
1464         return $s;
1465 }
1466
1467 /**
1468  * apply xmlify() to all values of array $val, recursively
1469  */
1470 function array_xmlify($val){
1471         if (is_bool($val)) return $val?"true":"false";
1472         if (is_array($val)) return array_map('array_xmlify', $val);
1473         return xmlify((string) $val);
1474 }
1475
1476
1477 function reltoabs($text, $base)
1478 {
1479   if (empty($base))
1480     return $text;
1481
1482   $base = rtrim($base,'/');
1483
1484   $base2 = $base . "/";
1485         
1486   // Replace links
1487   $pattern = "/<a([^>]*) href=\"(?!http|https|\/)([^\"]*)\"/";
1488   $replace = "<a\${1} href=\"" . $base2 . "\${2}\"";
1489   $text = preg_replace($pattern, $replace, $text);
1490
1491   $pattern = "/<a([^>]*) href=\"(?!http|https)([^\"]*)\"/";
1492   $replace = "<a\${1} href=\"" . $base . "\${2}\"";
1493   $text = preg_replace($pattern, $replace, $text);
1494
1495   // Replace images
1496   $pattern = "/<img([^>]*) src=\"(?!http|https|\/)([^\"]*)\"/";
1497   $replace = "<img\${1} src=\"" . $base2 . "\${2}\"";
1498   $text = preg_replace($pattern, $replace, $text); 
1499
1500   $pattern = "/<img([^>]*) src=\"(?!http|https)([^\"]*)\"/";
1501   $replace = "<img\${1} src=\"" . $base . "\${2}\"";
1502   $text = preg_replace($pattern, $replace, $text); 
1503
1504
1505   // Done
1506   return $text;
1507 }
1508
1509 function item_post_type($item) {
1510         if(intval($item['event-id']))
1511                 return t('event');
1512         if(strlen($item['resource-id']))
1513                 return t('photo');
1514         if(strlen($item['verb']) && $item['verb'] !== ACTIVITY_POST)
1515                 return t('activity');
1516         if($item['id'] != $item['parent'])
1517                 return t('comment');
1518         return t('post');
1519 }
1520
1521 // post categories and "save to file" use the same item.file table for storage.
1522 // We will differentiate the different uses by wrapping categories in angle brackets
1523 // and save to file categories in square brackets.
1524 // To do this we need to escape these characters if they appear in our tag. 
1525
1526 function file_tag_encode($s) {
1527         return str_replace(array('<','>','[',']'),array('%3c','%3e','%5b','%5d'),$s);
1528 }
1529
1530 function file_tag_decode($s) {
1531         return str_replace(array('%3c','%3e','%5b','%5d'),array('<','>','[',']'),$s);
1532 }
1533
1534 function file_tag_file_query($table,$s,$type = 'file') {
1535
1536         if($type == 'file')
1537                 $str = preg_quote( '[' . str_replace('%','%%',file_tag_encode($s)) . ']' );
1538         else
1539                 $str = preg_quote( '<' . str_replace('%','%%',file_tag_encode($s)) . '>' );
1540         return " AND " . (($table) ? dbesc($table) . '.' : '') . "file regexp '" . dbesc($str) . "' ";
1541 }
1542
1543 // ex. given music,video return <music><video> or [music][video]
1544 function file_tag_list_to_file($list,$type = 'file') {
1545         $tag_list = '';
1546         if(strlen($list)) {
1547                 $list_array = explode(",",$list);
1548                 if($type == 'file') {
1549                         $lbracket = '[';
1550                         $rbracket = ']';
1551                 }
1552                 else {
1553                         $lbracket = '<';
1554                         $rbracket = '>';
1555                 }
1556
1557                 foreach($list_array as $item) {
1558                   if(strlen($item)) {
1559                                 $tag_list .= $lbracket . file_tag_encode(trim($item))  . $rbracket;
1560                         }
1561                 }
1562         }
1563         return $tag_list;
1564 }
1565
1566 // ex. given <music><video>[friends], return music,video or friends
1567 function file_tag_file_to_list($file,$type = 'file') {
1568         $matches = false;
1569         $list = '';
1570         if($type == 'file') {
1571                 $cnt = preg_match_all('/\[(.*?)\]/',$file,$matches,PREG_SET_ORDER);
1572         }
1573         else {
1574                 $cnt = preg_match_all('/<(.*?)>/',$file,$matches,PREG_SET_ORDER);
1575         }
1576         if($cnt) {
1577                 foreach($matches as $mtch) {
1578                         if(strlen($list))
1579                                 $list .= ',';
1580                         $list .= file_tag_decode($mtch[1]);
1581                 }
1582         }
1583
1584         return $list;
1585 }
1586
1587 function file_tag_update_pconfig($uid,$file_old,$file_new,$type = 'file') {
1588         // $file_old - categories previously associated with an item
1589         // $file_new - new list of categories for an item
1590
1591         if(! intval($uid))
1592                 return false;
1593
1594         if($file_old == $file_new)
1595                 return true;
1596
1597         $saved = get_pconfig($uid,'system','filetags');
1598         if(strlen($saved)) {
1599                 if($type == 'file') {
1600                         $lbracket = '[';
1601                         $rbracket = ']';
1602                 }
1603                 else {
1604                         $lbracket = '<';
1605                         $rbracket = '>';
1606                 }
1607
1608                 $filetags_updated = $saved;
1609
1610                 // check for new tags to be added as filetags in pconfig
1611                 $new_tags = array();
1612                 $check_new_tags = explode(",",file_tag_file_to_list($file_new,$type));
1613
1614                 foreach($check_new_tags as $tag) {
1615                         if(! stristr($saved,$lbracket . file_tag_encode($tag) . $rbracket))
1616                                 $new_tags[] = $tag;
1617                 }
1618
1619                 $filetags_updated .= file_tag_list_to_file(implode(",",$new_tags),$type);
1620
1621                 // check for deleted tags to be removed from filetags in pconfig
1622                 $deleted_tags = array();
1623                 $check_deleted_tags = explode(",",file_tag_file_to_list($file_old,$type));
1624
1625                 foreach($check_deleted_tags as $tag) {
1626                         if(! stristr($file_new,$lbracket . file_tag_encode($tag) . $rbracket))
1627                                 $deleted_tags[] = $tag;
1628                 }
1629
1630                 foreach($deleted_tags as $key => $tag) {
1631                         $r = q("select file from item where uid = %d " . file_tag_file_query('item',$tag,$type),
1632                                 intval($uid)
1633                         );
1634
1635                         if(count($r)) {
1636                                 unset($deleted_tags[$key]);
1637                         }
1638                         else {
1639                                 $filetags_updated = str_replace($lbracket . file_tag_encode($tag) . $rbracket,'',$filetags_updated);
1640                         }
1641                 }
1642
1643                 if($saved != $filetags_updated) {
1644                         set_pconfig($uid,'system','filetags', $filetags_updated);
1645                 }
1646                 return true;
1647         }
1648         else
1649                 if(strlen($file_new)) {
1650                         set_pconfig($uid,'system','filetags', $file_new);
1651                 }
1652                 return true;
1653 }
1654
1655 function file_tag_save_file($uid,$item,$file) {
1656         $result = false;
1657         if(! intval($uid))
1658                 return false;
1659         $r = q("select file from item where id = %d and uid = %d limit 1",
1660                 intval($item),
1661                 intval($uid)
1662         );
1663         if(count($r)) {
1664                 if(! stristr($r[0]['file'],'[' . file_tag_encode($file) . ']'))
1665                         q("update item set file = '%s' where id = %d and uid = %d limit 1",
1666                                 dbesc($r[0]['file'] . '[' . file_tag_encode($file) . ']'),
1667                                 intval($item),
1668                                 intval($uid)
1669                         );
1670                 $saved = get_pconfig($uid,'system','filetags');
1671                 if((! strlen($saved)) || (! stristr($saved,'[' . file_tag_encode($file) . ']')))
1672                         set_pconfig($uid,'system','filetags',$saved . '[' . file_tag_encode($file) . ']');
1673                 info( t('Item filed') );
1674         }
1675         return true;
1676 }
1677
1678 function file_tag_unsave_file($uid,$item,$file,$cat = false) {
1679         $result = false;
1680         if(! intval($uid))
1681                 return false;
1682
1683         if($cat == true)
1684                 $pattern = '<' . file_tag_encode($file) . '>' ;
1685         else
1686                 $pattern = '[' . file_tag_encode($file) . ']' ;
1687
1688
1689         $r = q("select file from item where id = %d and uid = %d limit 1",
1690                 intval($item),
1691                 intval($uid)
1692         );
1693         if(! count($r))
1694                 return false;
1695
1696         q("update item set file = '%s' where id = %d and uid = %d limit 1",
1697                 dbesc(str_replace($pattern,'',$r[0]['file'])),
1698                 intval($item),
1699                 intval($uid)
1700         );
1701
1702         $r = q("select file from item where uid = %d and deleted = 0 " . file_tag_file_query('item',$file,(($cat) ? 'category' : 'file')),
1703                 intval($uid)
1704         );
1705
1706         if(! count($r)) {
1707                 $saved = get_pconfig($uid,'system','filetags');
1708                 set_pconfig($uid,'system','filetags',str_replace($pattern,'',$saved));
1709
1710         }
1711         return true;
1712 }
1713
1714 function normalise_openid($s) {
1715         return trim(str_replace(array('http://','https://'),array('',''),$s),'/');
1716 }
1717
1718
1719 function undo_post_tagging($s) {
1720         $matches = null;
1721         $cnt = preg_match_all('/([@#])\[url=(.*?)\](.*?)\[\/url\]/ism',$s,$matches,PREG_SET_ORDER);
1722         if($cnt) {
1723                 foreach($matches as $mtch) {
1724                         $s = str_replace($mtch[0], $mtch[1] . $mtch[3],$s);
1725                 }
1726         }
1727         return $s;
1728 }
1729
1730 function fix_mce_lf($s) {
1731         $s = str_replace("\r\n","\n",$s);
1732 //      $s = str_replace("\n\n","\n",$s);
1733         return $s;
1734 }
1735
1736
1737 function protect_sprintf($s) {
1738         return(str_replace('%','%%',$s));
1739 }
1740
1741
1742 function is_a_date_arg($s) {
1743         $i = intval($s);
1744         if($i > 1900) {
1745                 $y = date('Y');
1746                 if($i <= $y+1 && strpos($s,'-') == 4) {
1747                         $m = intval(substr($s,5));
1748                         if($m > 0 && $m <= 12)
1749                                 return true;
1750                 }
1751         }
1752         return false;
1753 }