]> git.mxchange.org Git - friendica.git/blob - include/text.php
simplify by making Smarty include directives behave like Friendica native ones
[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 //              logger($template_file);
481                 return file_get_contents($template_file);
482         }       
483 }}
484
485 if(! function_exists("get_template_file")) {
486 function get_template_file($a, $filename, $root = '') {
487         $theme = current_theme();
488
489         // Make sure $root ends with a slash /
490         if($root !== '' && $root[strlen($root)-1] !== '/')
491                 $root = $root . '/';
492
493         if(file_exists($root . "view/theme/$theme/$filename"))
494                 $template_file = $root . "view/theme/$theme/$filename";
495         elseif (x($a->theme_info,"extends") && file_exists($root . "view/theme/".$a->theme_info["extends"]."/$filename"))
496                 $template_file = $root . "view/theme/".$a->theme_info["extends"]."/$filename";
497         else
498                 $template_file = $root . "view/$filename";
499
500         return $template_file;
501 }}
502
503
504
505
506
507 // for html,xml parsing - let's say you've got
508 // an attribute foobar="class1 class2 class3"
509 // and you want to find out if it contains 'class3'.
510 // you can't use a normal sub string search because you
511 // might match 'notclass3' and a regex to do the job is 
512 // possible but a bit complicated. 
513 // pass the attribute string as $attr and the attribute you 
514 // are looking for as $s - returns true if found, otherwise false
515
516 if(! function_exists('attribute_contains')) {
517 function attribute_contains($attr,$s) {
518         $a = explode(' ', $attr);
519         if(count($a) && in_array($s,$a))
520                 return true;
521         return false;
522 }}
523
524 if(! function_exists('logger')) {
525 function logger($msg,$level = 0) {
526         // turn off logger in install mode
527         global $a;
528         global $db;
529
530         if(($a->module == 'install') || (! ($db && $db->connected))) return;
531
532         $debugging = get_config('system','debugging');
533         $loglevel  = intval(get_config('system','loglevel'));
534         $logfile   = get_config('system','logfile');
535
536         if((! $debugging) || (! $logfile) || ($level > $loglevel))
537                 return;
538         
539         @file_put_contents($logfile, datetime_convert() . ':' . session_id() . ' ' . $msg . "\n", FILE_APPEND);
540         return;
541 }}
542
543
544 if(! function_exists('activity_match')) {
545 function activity_match($haystack,$needle) {
546         if(($haystack === $needle) || ((basename($needle) === $haystack) && strstr($needle,NAMESPACE_ACTIVITY_SCHEMA)))
547                 return true;
548         return false;
549 }}
550
551
552 // Pull out all #hashtags and @person tags from $s;
553 // We also get @person@domain.com - which would make 
554 // the regex quite complicated as tags can also
555 // end a sentence. So we'll run through our results
556 // and strip the period from any tags which end with one.
557 // Returns array of tags found, or empty array.
558
559
560 if(! function_exists('get_tags')) {
561 function get_tags($s) {
562         $ret = array();
563
564         // ignore anything in a code block
565
566         $s = preg_replace('/\[code\](.*?)\[\/code\]/sm','',$s);
567
568         // ignore anything in a bbtag
569
570         $s = preg_replace('/\[(.*?)\]/sm','',$s);
571
572         // Match full names against @tags including the space between first and last
573         // We will look these up afterward to see if they are full names or not recognisable.
574
575         if(preg_match_all('/(@[^ \x0D\x0A,:?]+ [^ \x0D\x0A@,:?]+)([ \x0D\x0A@,:?]|$)/',$s,$match)) {
576                 foreach($match[1] as $mtch) {
577                         if(strstr($mtch,"]")) {
578                                 // we might be inside a bbcode color tag - leave it alone
579                                 continue;
580                         }
581                         if(substr($mtch,-1,1) === '.')
582                                 $ret[] = substr($mtch,0,-1);
583                         else
584                                 $ret[] = $mtch;
585                 }
586         }
587
588         // Otherwise pull out single word tags. These can be @nickname, @first_last
589         // and #hash tags.
590
591         if(preg_match_all('/([@#][^ \x0D\x0A,;:?]+)([ \x0D\x0A,;:?]|$)/',$s,$match)) {
592                 foreach($match[1] as $mtch) {
593                         if(strstr($mtch,"]")) {
594                                 // we might be inside a bbcode color tag - leave it alone
595                                 continue;
596                         }
597                         if(substr($mtch,-1,1) === '.')
598                                 $mtch = substr($mtch,0,-1);
599                         // ignore strictly numeric tags like #1
600                         if((strpos($mtch,'#') === 0) && ctype_digit(substr($mtch,1)))
601                                 continue;
602                         // try not to catch url fragments
603                         if(strpos($s,$mtch) && preg_match('/[a-zA-z0-9\/]/',substr($s,strpos($s,$mtch)-1,1)))
604                                 continue;
605                         $ret[] = $mtch;
606                 }
607         }
608         return $ret;
609 }}
610
611
612 // quick and dirty quoted_printable encoding
613
614 if(! function_exists('qp')) {
615 function qp($s) {
616 return str_replace ("%","=",rawurlencode($s));
617 }} 
618
619
620
621 if(! function_exists('get_mentions')) {
622 function get_mentions($item) {
623         $o = '';
624         if(! strlen($item['tag']))
625                 return $o;
626
627         $arr = explode(',',$item['tag']);
628         foreach($arr as $x) {
629                 $matches = null;
630                 if(preg_match('/@\[url=([^\]]*)\]/',$x,$matches)) {
631                         $o .= "\t\t" . '<link rel="mentioned" href="' . $matches[1] . '" />' . "\r\n";
632                         $o .= "\t\t" . '<link rel="ostatus:attention" href="' . $matches[1] . '" />' . "\r\n";
633                 }
634         }
635         return $o;
636 }}
637
638 if(! function_exists('contact_block')) {
639 function contact_block() {
640         $o = '';
641         $a = get_app();
642
643         $shown = get_pconfig($a->profile['uid'],'system','display_friend_count');
644         if($shown === false)
645                 $shown = 24;
646         if($shown == 0)
647                 return;
648
649         if((! is_array($a->profile)) || ($a->profile['hide-friends']))
650                 return $o;
651         $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",
652                         intval($a->profile['uid'])
653         );
654         if(count($r)) {
655                 $total = intval($r[0]['total']);
656         }
657         if(! $total) {
658                 $contacts = t('No contacts');
659                 $micropro = Null;
660                 
661         } else {
662                 $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",
663                                 intval($a->profile['uid']),
664                                 intval($shown)
665                 );
666                 if(count($r)) {
667                         $contacts = sprintf( tt('%d Contact','%d Contacts', $total),$total);
668                         $micropro = Array();
669                         foreach($r as $rr) {
670                                 $micropro[] = micropro($rr,true,'mpfriend');
671                         }
672                 }
673         }
674         
675         $tpl = get_markup_template('contact_block.tpl');
676         $o = replace_macros($tpl, array(
677                 '$contacts' => $contacts,
678                 '$nickname' => $a->profile['nickname'],
679                 '$viewcontacts' => t('View Contacts'),
680                 '$micropro' => $micropro,
681         ));
682
683         $arr = array('contacts' => $r, 'output' => $o);
684
685         call_hooks('contact_block_end', $arr);
686         return $o;
687
688 }}
689
690 if(! function_exists('micropro')) {
691 function micropro($contact, $redirect = false, $class = '', $textmode = false) {
692
693         if($class)
694                 $class = ' ' . $class;
695
696         $url = $contact['url'];
697         $sparkle = '';
698         $redir = false;
699
700         if($redirect) {
701                 $a = get_app();
702                 $redirect_url = $a->get_baseurl() . '/redir/' . $contact['id'];
703                 if(local_user() && ($contact['uid'] == local_user()) && ($contact['network'] === 'dfrn')) {
704                         $redir = true;
705                         $url = $redirect_url;
706                         $sparkle = ' sparkle';
707                 }
708                 else
709                         $url = zrl($url);
710         }
711         $click = ((x($contact,'click')) ? ' onclick="' . $contact['click'] . '" ' : '');
712         if($click)
713                 $url = '';
714         if($textmode) {
715                 return '<div class="contact-block-textdiv' . $class . '"><a class="contact-block-link' . $class . $sparkle 
716                         . (($click) ? ' fakelink' : '') . '" '
717                         . (($redir) ? ' target="redir" ' : '')
718                         . (($url) ? ' href="' . $url . '"' : '') . $click
719                         . '" title="' . $contact['name'] . ' [' . $contact['url'] . ']" alt="' . $contact['name'] 
720                         . '" >'. $contact['name'] . '</a></div>' . "\r\n";
721         }
722         else {
723                 return '<div class="contact-block-div' . $class . '"><a class="contact-block-link' . $class . $sparkle 
724                         . (($click) ? ' fakelink' : '') . '" '
725                         . (($redir) ? ' target="redir" ' : '')
726                         . (($url) ? ' href="' . $url . '"' : '') . $click . ' ><img class="contact-block-img' . $class . $sparkle . '" src="' 
727                         . $contact['micro'] . '" title="' . $contact['name'] . ' [' . $contact['url'] . ']" alt="' . $contact['name'] 
728                         . '" /></a></div>' . "\r\n";
729         }
730 }}
731
732
733
734 if(! function_exists('search')) {
735 function search($s,$id='search-box',$url='/search',$save = false) {
736         $a = get_app();
737         $o  = '<div id="' . $id . '">';
738         $o .= '<form action="' . $a->get_baseurl((stristr($url,'network')) ? true : false) . $url . '" method="get" >';
739         $o .= '<input type="text" name="search" id="search-text" placeholder="' . t('Search') . '" value="' . $s .'" />';
740         $o .= '<input type="submit" name="submit" id="search-submit" value="' . t('Search') . '" />'; 
741         if($save)
742                 $o .= '<input type="submit" name="save" id="search-save" value="' . t('Save') . '" />'; 
743         $o .= '</form></div>';
744         return $o;
745 }}
746
747 if(! function_exists('valid_email')) {
748 function valid_email($x){
749
750         if(get_config('system','disable_email_validation'))
751                 return true;
752
753         if(preg_match('/^[_a-zA-Z0-9\-\+]+(\.[_a-zA-Z0-9\-\+]+)*@[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+)+$/',$x))
754                 return true;
755         return false;
756 }}
757
758
759 /**
760  *
761  * Function: linkify
762  *
763  * Replace naked text hyperlink with HTML formatted hyperlink
764  *
765  */
766
767 if(! function_exists('linkify')) {
768 function linkify($s) {
769         $s = preg_replace("/(https?\:\/\/[a-zA-Z0-9\:\/\-\?\&\;\.\=\_\~\#\'\%\$\!\+]*)/", ' <a href="$1" target="external-link">$1</a>', $s);
770         $s = preg_replace("/\<(.*?)(src|href)=(.*?)\&amp\;(.*?)\>/ism",'<$1$2=$3&$4>',$s);
771         return($s);
772 }}
773
774 function get_poke_verbs() {
775         
776         // index is present tense verb
777         // value is array containing past tense verb, translation of present, translation of past
778
779         $arr = array(
780                 'poke' => array( 'poked', t('poke'), t('poked')),
781                 'ping' => array( 'pinged', t('ping'), t('pinged')),
782                 'prod' => array( 'prodded', t('prod'), t('prodded')),
783                 'slap' => array( 'slapped', t('slap'), t('slapped')),
784                 'finger' => array( 'fingered', t('finger'), t('fingered')),
785                 'rebuff' => array( 'rebuffed', t('rebuff'), t('rebuffed')),
786         );
787         call_hooks('poke_verbs', $arr);
788         return $arr;
789 }
790
791 function get_mood_verbs() {
792         
793         // index is present tense verb
794         // value is array containing past tense verb, translation of present, translation of past
795
796         $arr = array(
797                 'happy'      => t('happy'),
798                 'sad'        => t('sad'),
799                 'mellow'     => t('mellow'),
800                 'tired'      => t('tired'),
801                 'perky'      => t('perky'),
802                 'angry'      => t('angry'),
803                 'stupefied'  => t('stupified'),
804                 'puzzled'    => t('puzzled'),
805                 'interested' => t('interested'),
806                 'bitter'     => t('bitter'),
807                 'cheerful'   => t('cheerful'),
808                 'alive'      => t('alive'),
809                 'annoyed'    => t('annoyed'),
810                 'anxious'    => t('anxious'),
811                 'cranky'     => t('cranky'),
812                 'disturbed'  => t('disturbed'),
813                 'frustrated' => t('frustrated'),
814                 'motivated'  => t('motivated'),
815                 'relaxed'    => t('relaxed'),
816                 'surprised'  => t('surprised'),
817         );
818
819         call_hooks('mood_verbs', $arr);
820         return $arr;
821 }
822
823
824 /**
825  * 
826  * Function: smilies
827  *
828  * Description:
829  * Replaces text emoticons with graphical images
830  *
831  * @Parameter: string $s
832  *
833  * Returns string
834  *
835  * It is expected that this function will be called using HTML text.
836  * We will escape text between HTML pre and code blocks from being 
837  * processed. 
838  * 
839  * At a higher level, the bbcode [nosmile] tag can be used to prevent this 
840  * function from being executed by the prepare_text() routine when preparing
841  * bbcode source for HTML display
842  *
843  */
844
845 if(! function_exists('smilies')) {
846 function smilies($s, $sample = false) {
847
848         $a = get_app();
849
850         if(intval(get_config('system','no_smilies')) 
851                 || (local_user() && intval(get_pconfig(local_user(),'system','no_smilies'))))
852                 return $s;
853
854         $s = preg_replace_callback('/<pre>(.*?)<\/pre>/ism','smile_encode',$s);
855         $s = preg_replace_callback('/<code>(.*?)<\/code>/ism','smile_encode',$s);
856
857         $texts =  array( 
858                 '&lt;3', 
859                 '&lt;/3', 
860                 '&lt;\\3', 
861                 ':-)', 
862                 ';-)', 
863                 ':-(', 
864                 ':-P', 
865                 ':-p', 
866                 ':-"', 
867                 ':-&quot;', 
868                 ':-x', 
869                 ':-X', 
870                 ':-D', 
871                 '8-|', 
872                 '8-O', 
873                 ':-O', 
874                 '\\o/', 
875                 'o.O', 
876                 'O.o', 
877                 'o_O', 
878                 'O_o', 
879                 ":'(", 
880                 ":-!", 
881                 ":-/", 
882                 ":-[", 
883                 "8-)",
884                 ':beer', 
885                 ':homebrew', 
886                 ':coffee', 
887                 ':facepalm',
888                 ':like',
889                 ':dislike',
890                 '~friendica'
891
892         );
893
894         $icons = array(
895                 '<img class="smiley" src="' . $a->get_baseurl() . '/images/smiley-heart.gif" alt="<3" />',
896                 '<img class="smiley" src="' . $a->get_baseurl() . '/images/smiley-brokenheart.gif" alt="</3" />',
897                 '<img class="smiley" src="' . $a->get_baseurl() . '/images/smiley-brokenheart.gif" alt="<\\3" />',
898                 '<img class="smiley" src="' . $a->get_baseurl() . '/images/smiley-smile.gif" alt=":-)" />',
899                 '<img class="smiley" src="' . $a->get_baseurl() . '/images/smiley-wink.gif" alt=";-)" />',
900                 '<img class="smiley" src="' . $a->get_baseurl() . '/images/smiley-frown.gif" alt=":-(" />',
901                 '<img class="smiley" src="' . $a->get_baseurl() . '/images/smiley-tongue-out.gif" alt=":-P" />',
902                 '<img class="smiley" src="' . $a->get_baseurl() . '/images/smiley-tongue-out.gif" alt=":-p" />',
903                 '<img class="smiley" src="' . $a->get_baseurl() . '/images/smiley-kiss.gif" alt=":-\"" />',
904                 '<img class="smiley" src="' . $a->get_baseurl() . '/images/smiley-kiss.gif" alt=":-\"" />',
905                 '<img class="smiley" src="' . $a->get_baseurl() . '/images/smiley-kiss.gif" alt=":-x" />',
906                 '<img class="smiley" src="' . $a->get_baseurl() . '/images/smiley-kiss.gif" alt=":-X" />',
907                 '<img class="smiley" src="' . $a->get_baseurl() . '/images/smiley-laughing.gif" alt=":-D" />',
908                 '<img class="smiley" src="' . $a->get_baseurl() . '/images/smiley-surprised.gif" alt="8-|" />',
909                 '<img class="smiley" src="' . $a->get_baseurl() . '/images/smiley-surprised.gif" alt="8-O" />',
910                 '<img class="smiley" src="' . $a->get_baseurl() . '/images/smiley-surprised.gif" alt=":-O" />',                
911                 '<img class="smiley" src="' . $a->get_baseurl() . '/images/smiley-thumbsup.gif" alt="\\o/" />',
912                 '<img class="smiley" src="' . $a->get_baseurl() . '/images/smiley-Oo.gif" alt="o.O" />',
913                 '<img class="smiley" src="' . $a->get_baseurl() . '/images/smiley-Oo.gif" alt="O.o" />',
914                 '<img class="smiley" src="' . $a->get_baseurl() . '/images/smiley-Oo.gif" alt="o_O" />',
915                 '<img class="smiley" src="' . $a->get_baseurl() . '/images/smiley-Oo.gif" alt="O_o" />',
916                 '<img class="smiley" src="' . $a->get_baseurl() . '/images/smiley-cry.gif" alt=":\'(" />',
917                 '<img class="smiley" src="' . $a->get_baseurl() . '/images/smiley-foot-in-mouth.gif" alt=":-!" />',
918                 '<img class="smiley" src="' . $a->get_baseurl() . '/images/smiley-undecided.gif" alt=":-/" />',
919                 '<img class="smiley" src="' . $a->get_baseurl() . '/images/smiley-embarassed.gif" alt=":-[" />',
920                 '<img class="smiley" src="' . $a->get_baseurl() . '/images/smiley-cool.gif" alt="8-)" />',
921                 '<img class="smiley" src="' . $a->get_baseurl() . '/images/beer_mug.gif" alt=":beer" />',
922                 '<img class="smiley" src="' . $a->get_baseurl() . '/images/beer_mug.gif" alt=":homebrew" />',
923                 '<img class="smiley" src="' . $a->get_baseurl() . '/images/coffee.gif" alt=":coffee" />',
924                 '<img class="smiley" src="' . $a->get_baseurl() . '/images/smiley-facepalm.gif" alt=":facepalm" />',
925                 '<img class="smiley" src="' . $a->get_baseurl() . '/images/like.gif" alt=":like" />',
926                 '<img class="smiley" src="' . $a->get_baseurl() . '/images/dislike.gif" alt=":dislike" />',
927                 '<a href="http://friendica.com">~friendica <img class="smiley" src="' . $a->get_baseurl() . '/images/friendica-16.png" alt="~friendica" /></a>'
928         );
929
930         $params = array('texts' => $texts, 'icons' => $icons, 'string' => $s);
931         call_hooks('smilie', $params);
932
933         if($sample) {
934                 $s = '<div class="smiley-sample">';
935                 for($x = 0; $x < count($params['texts']); $x ++) {
936                         $s .= '<dl><dt>' . $params['texts'][$x] . '</dt><dd>' . $params['icons'][$x] . '</dd></dl>';
937                 }
938         }
939         else {
940                 $params['string'] = preg_replace_callback('/&lt;(3+)/','preg_heart',$params['string']);
941                 $s = str_replace($params['texts'],$params['icons'],$params['string']);
942         }
943
944         $s = preg_replace_callback('/<pre>(.*?)<\/pre>/ism','smile_decode',$s);
945         $s = preg_replace_callback('/<code>(.*?)<\/code>/ism','smile_decode',$s);
946
947         return $s;
948
949 }}
950
951 function smile_encode($m) {
952         return(str_replace($m[1],base64url_encode($m[1]),$m[0]));
953 }
954
955 function smile_decode($m) {
956         return(str_replace($m[1],base64url_decode($m[1]),$m[0]));
957 }
958
959 // expand <3333 to the correct number of hearts
960
961 function preg_heart($x) {
962         $a = get_app();
963         if(strlen($x[1]) == 1)
964                 return $x[0];
965         $t = '';
966         for($cnt = 0; $cnt < strlen($x[1]); $cnt ++)
967                 $t .= '<img class="smiley" src="' . $a->get_baseurl() . '/images/smiley-heart.gif" alt="<3" />';
968         $r =  str_replace($x[0],$t,$x[0]);
969         return $r;
970 }
971
972
973 if(! function_exists('day_translate')) {
974 function day_translate($s) {
975         $ret = str_replace(array('Monday','Tuesday','Wednesday','Thursday','Friday','Saturday','Sunday'),
976                 array( t('Monday'), t('Tuesday'), t('Wednesday'), t('Thursday'), t('Friday'), t('Saturday'), t('Sunday')),
977                 $s);
978
979         $ret = str_replace(array('January','February','March','April','May','June','July','August','September','October','November','December'),
980                 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')),
981                 $ret);
982
983         return $ret;
984 }}
985
986
987 if(! function_exists('normalise_link')) {
988 function normalise_link($url) {
989         $ret = str_replace(array('https:','//www.'), array('http:','//'), $url);
990         return(rtrim($ret,'/'));
991 }}
992
993 /**
994  *
995  * Compare two URLs to see if they are the same, but ignore
996  * slight but hopefully insignificant differences such as if one 
997  * is https and the other isn't, or if one is www.something and 
998  * the other isn't - and also ignore case differences.
999  *
1000  * Return true if the URLs match, otherwise false.
1001  *
1002  */
1003
1004 if(! function_exists('link_compare')) {
1005 function link_compare($a,$b) {
1006         if(strcasecmp(normalise_link($a),normalise_link($b)) === 0)
1007                 return true;
1008         return false;
1009 }}
1010
1011 // Given an item array, convert the body element from bbcode to html and add smilie icons.
1012 // If attach is true, also add icons for item attachments
1013
1014
1015 if(! function_exists('prepare_body')) {
1016 function prepare_body($item,$attach = false) {
1017
1018         $a = get_app();
1019         call_hooks('prepare_body_init', $item);
1020
1021         $cachefile = get_cachefile($item["guid"]."-".strtotime($item["edited"])."-".hash("crc32", $item['body']));
1022
1023         if (($cachefile != '')) {
1024                 if (file_exists($cachefile))
1025                         $s = file_get_contents($cachefile);
1026                 else {
1027                         $s = prepare_text($item['body']);
1028                         file_put_contents($cachefile, $s);
1029                 }
1030         } else
1031                 $s = prepare_text($item['body']);
1032
1033
1034         $prep_arr = array('item' => $item, 'html' => $s);
1035         call_hooks('prepare_body', $prep_arr);
1036         $s = $prep_arr['html'];
1037
1038         if(! $attach) {
1039                 // Replace the blockquotes with quotes that are used in mails
1040                 $mailquote = '<blockquote type="cite" class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex;">';
1041                 $s = str_replace(array('<blockquote>', '<blockquote class="spoiler">', '<blockquote class="author">'), array($mailquote, $mailquote, $mailquote), $s);
1042                 return $s;
1043         }
1044
1045         $arr = explode(',',$item['attach']);
1046         if(count($arr)) {
1047                 $s .= '<div class="body-attach">';
1048                 foreach($arr as $r) {
1049                         $matches = false;
1050                         $icon = '';
1051                         $cnt = preg_match_all('|\[attach\]href=\"(.*?)\" length=\"(.*?)\" type=\"(.*?)\" title=\"(.*?)\"\[\/attach\]|',$r,$matches, PREG_SET_ORDER);
1052                         if($cnt) {
1053                                 foreach($matches as $mtch) {
1054                                         $icontype = strtolower(substr($mtch[3],0,strpos($mtch[3],'/')));
1055                                         switch($icontype) {
1056                                                 case 'video':
1057                                                 case 'audio':
1058                                                 case 'image':
1059                                                 case 'text':
1060                                                         $icon = '<div class="attachtype icon s22 type-' . $icontype . '"></div>';
1061                                                         break;
1062                                                 default:
1063                                                         $icon = '<div class="attachtype icon s22 type-unkn"></div>';
1064                                                         break;
1065                                         }
1066                                         $title = ((strlen(trim($mtch[4]))) ? escape_tags(trim($mtch[4])) : escape_tags($mtch[1]));
1067                                         $title .= ' ' . $mtch[2] . ' ' . t('bytes');
1068                                         if((local_user() == $item['uid']) && ($item['contact-id'] != $a->contact['id']) && ($item['network'] == NETWORK_DFRN))
1069                                                 $the_url = $a->get_baseurl() . '/redir/' . $item['contact-id'] . '?f=1&url=' . $mtch[1];
1070                                         else
1071                                                 $the_url = $mtch[1];
1072
1073                                         $s .= '<a href="' . strip_tags($the_url) . '" title="' . $title . '" class="attachlink" target="external-link" >' . $icon . '</a>';
1074                                 }
1075                         }
1076                 }
1077                 $s .= '<div class="clear"></div></div>';
1078         }
1079
1080
1081         // Look for spoiler
1082         $spoilersearch = '<blockquote class="spoiler">';
1083
1084         // Remove line breaks before the spoiler
1085         while ((strpos($s, "\n".$spoilersearch) !== false))
1086                 $s = str_replace("\n".$spoilersearch, $spoilersearch, $s);
1087         while ((strpos($s, "<br />".$spoilersearch) !== false))
1088                 $s = str_replace("<br />".$spoilersearch, $spoilersearch, $s);
1089
1090         while ((strpos($s, $spoilersearch) !== false)) {
1091
1092                 $pos = strpos($s, $spoilersearch);
1093                 $rnd = random_string(8);
1094                 $spoilerreplace = '<br /> <span id="spoiler-wrap-'.$rnd.'" style="white-space:nowrap;" class="fakelink" onclick="openClose(\'spoiler-'.$rnd.'\');">'.sprintf(t('Click to open/close')).'</span>'.
1095                                         '<blockquote class="spoiler" id="spoiler-'.$rnd.'" style="display: none;">';
1096                 $s = substr($s, 0, $pos).$spoilerreplace.substr($s, $pos+strlen($spoilersearch));
1097         }
1098
1099         // Look for quote with author
1100         $authorsearch = '<blockquote class="author">';
1101
1102         while ((strpos($s, $authorsearch) !== false)) {
1103
1104                 $pos = strpos($s, $authorsearch);
1105                 $rnd = random_string(8);
1106                 $authorreplace = '<br /> <span id="author-wrap-'.$rnd.'" style="white-space:nowrap;" class="fakelink" onclick="openClose(\'author-'.$rnd.'\');">'.sprintf(t('Click to open/close')).'</span>'.
1107                                         '<blockquote class="author" id="author-'.$rnd.'" style="display: block;">';
1108                 $s = substr($s, 0, $pos).$authorreplace.substr($s, $pos+strlen($authorsearch));
1109         }
1110
1111         $prep_arr = array('item' => $item, 'html' => $s);
1112         call_hooks('prepare_body_final', $prep_arr);
1113
1114         return $prep_arr['html'];
1115 }}
1116
1117
1118 // Given a text string, convert from bbcode to html and add smilie icons.
1119
1120 if(! function_exists('prepare_text')) {
1121 function prepare_text($text) {
1122
1123         require_once('include/bbcode.php');
1124
1125         if(stristr($text,'[nosmile]'))
1126                 $s = bbcode($text);
1127         else
1128                 $s = smilies(bbcode($text));
1129
1130         return $s;
1131 }}
1132
1133
1134 /**
1135  * returns 
1136  * [
1137  *    //categories [
1138  *          {
1139  *               'name': 'category name',
1140  *              'removeurl': 'url to remove this category',
1141  *             'first': 'is the first in this array? true/false',
1142  *               'last': 'is the last in this array? true/false',
1143  *           } ,
1144  *           ....
1145  *       ],
1146  *       // folders [
1147  *               'name': 'folder name',
1148  *               'removeurl': 'url to remove this folder',
1149  *               'first': 'is the first in this array? true/false',
1150  *               'last': 'is the last in this array? true/false',
1151  *           } ,
1152  *           ....       
1153  *       ]
1154  *   ]
1155  */
1156 function get_cats_and_terms($item) {
1157     $a = get_app();
1158     $categories = array();
1159     $folders = array();
1160
1161     $matches = false; $first = true;
1162     $cnt = preg_match_all('/<(.*?)>/',$item['file'],$matches,PREG_SET_ORDER);
1163     if($cnt) {
1164         foreach($matches as $mtch) {
1165             $categories[] = array(
1166                 'name' => xmlify(file_tag_decode($mtch[1])),
1167                 'url' =>  "#",
1168                 'removeurl' => ((local_user() == $item['uid'])?$a->get_baseurl() . '/filerm/' . $item['id'] . '?f=&cat=' . xmlify(file_tag_decode($mtch[1])):""),
1169                 'first' => $first,
1170                 'last' => false
1171             );
1172             $first = false;
1173         }
1174     }
1175     if (count($categories)) $categories[count($categories)-1]['last'] = true;
1176     
1177
1178         if(local_user() == $item['uid']) {
1179             $matches = false; $first = true;
1180         $cnt = preg_match_all('/\[(.*?)\]/',$item['file'],$matches,PREG_SET_ORDER);
1181             if($cnt) {
1182             foreach($matches as $mtch) {
1183                     $folders[] = array(
1184                     'name' => xmlify(file_tag_decode($mtch[1])),
1185                          'url' =>  "#",
1186                         'removeurl' => ((local_user() == $item['uid'])?$a->get_baseurl() . '/filerm/' . $item['id'] . '?f=&term=' . xmlify(file_tag_decode($mtch[1])):""),
1187                     'first' => $first,
1188                         'last' => false
1189                 );
1190                     $first = false;
1191                         }
1192         }
1193     }
1194
1195     if (count($folders)) $folders[count($folders)-1]['last'] = true;
1196
1197     return array($categories, $folders);
1198 }
1199
1200
1201 /**
1202  * return atom link elements for all of our hubs
1203  */
1204
1205 if(! function_exists('feed_hublinks')) {
1206 function feed_hublinks() {
1207
1208         $hub = get_config('system','huburl');
1209
1210         $hubxml = '';
1211         if(strlen($hub)) {
1212                 $hubs = explode(',', $hub);
1213                 if(count($hubs)) {
1214                         foreach($hubs as $h) {
1215                                 $h = trim($h);
1216                                 if(! strlen($h))
1217                                         continue;
1218                                 $hubxml .= '<link rel="hub" href="' . xmlify($h) . '" />' . "\n" ;
1219                         }
1220                 }
1221         }
1222         return $hubxml;
1223 }}
1224
1225 /* return atom link elements for salmon endpoints */
1226
1227 if(! function_exists('feed_salmonlinks')) {
1228 function feed_salmonlinks($nick) {
1229
1230         $a = get_app();
1231
1232         $salmon  = '<link rel="salmon" href="' . xmlify($a->get_baseurl() . '/salmon/' . $nick) . '" />' . "\n" ;
1233
1234         // old style links that status.net still needed as of 12/2010 
1235
1236         $salmon .= '  <link rel="http://salmon-protocol.org/ns/salmon-replies" href="' . xmlify($a->get_baseurl() . '/salmon/' . $nick) . '" />' . "\n" ; 
1237         $salmon .= '  <link rel="http://salmon-protocol.org/ns/salmon-mention" href="' . xmlify($a->get_baseurl() . '/salmon/' . $nick) . '" />' . "\n" ; 
1238         return $salmon;
1239 }}
1240
1241 if(! function_exists('get_plink')) {
1242 function get_plink($item) {
1243         $a = get_app(); 
1244         if (x($item,'plink') && ($item['private'] != 1)) {
1245                 return array(
1246                         'href' => $item['plink'],
1247                         'title' => t('link to source'),
1248                 );
1249         } 
1250         else {
1251                 return false;
1252         }
1253 }}
1254
1255 if(! function_exists('unamp')) {
1256 function unamp($s) {
1257         return str_replace('&amp;', '&', $s);
1258 }}
1259
1260
1261
1262
1263 if(! function_exists('lang_selector')) {
1264 function lang_selector() {
1265         global $lang;
1266         
1267         $langs = glob('view/*/strings.php');
1268         
1269         $lang_options = array();
1270         $selected = "";
1271         
1272         if(is_array($langs) && count($langs)) {
1273                 $langs[] = '';
1274                 if(! in_array('view/en/strings.php',$langs))
1275                         $langs[] = 'view/en/';
1276                 asort($langs);
1277                 foreach($langs as $l) {
1278                         if($l == '') {
1279                                 $lang_options[""] = t('default');
1280                                 continue;
1281                         }
1282                         $ll = substr($l,5);
1283                         $ll = substr($ll,0,strrpos($ll,'/'));
1284                         $selected = (($ll === $lang && (x($_SESSION, 'language'))) ? $ll : $selected);
1285                         $lang_options[$ll]=$ll;
1286                 }
1287         }
1288
1289         $tpl = get_markup_template("lang_selector.tpl");        
1290         $o = replace_macros($tpl, array(
1291                 '$title' => t('Select an alternate language'),
1292                 '$langs' => array($lang_options, $selected),
1293                 
1294         ));
1295         return $o;
1296 }}
1297
1298
1299 if(! function_exists('return_bytes')) {
1300 function return_bytes ($size_str) {
1301     switch (substr ($size_str, -1))
1302     {
1303         case 'M': case 'm': return (int)$size_str * 1048576;
1304         case 'K': case 'k': return (int)$size_str * 1024;
1305         case 'G': case 'g': return (int)$size_str * 1073741824;
1306         default: return $size_str;
1307     }
1308 }}
1309
1310 function generate_user_guid() {
1311         $found = true;
1312         do {
1313                 $guid = random_string(16);
1314                 $x = q("SELECT `uid` FROM `user` WHERE `guid` = '%s' LIMIT 1",
1315                         dbesc($guid)
1316                 );
1317                 if(! count($x))
1318                         $found = false;
1319         } while ($found == true );
1320         return $guid;
1321 }
1322
1323
1324
1325 function base64url_encode($s, $strip_padding = false) {
1326
1327         $s = strtr(base64_encode($s),'+/','-_');
1328
1329         if($strip_padding)
1330                 $s = str_replace('=','',$s);
1331
1332         return $s;
1333 }
1334
1335 function base64url_decode($s) {
1336
1337         if(is_array($s)) {
1338                 logger('base64url_decode: illegal input: ' . print_r(debug_backtrace(), true));
1339                 return $s;
1340         }
1341
1342 /*
1343  *  // Placeholder for new rev of salmon which strips base64 padding.
1344  *  // PHP base64_decode handles the un-padded input without requiring this step
1345  *  // Uncomment if you find you need it.
1346  *
1347  *      $l = strlen($s);
1348  *      if(! strpos($s,'=')) {
1349  *              $m = $l % 4;
1350  *              if($m == 2)
1351  *                      $s .= '==';
1352  *              if($m == 3)
1353  *                      $s .= '=';
1354  *      }
1355  *
1356  */
1357
1358         return base64_decode(strtr($s,'-_','+/'));
1359 }
1360
1361
1362 if (!function_exists('str_getcsv')) {
1363     function str_getcsv($input, $delimiter = ',', $enclosure = '"', $escape = '\\', $eol = '\n') {
1364         if (is_string($input) && !empty($input)) {
1365             $output = array();
1366             $tmp    = preg_split("/".$eol."/",$input);
1367             if (is_array($tmp) && !empty($tmp)) {
1368                 while (list($line_num, $line) = each($tmp)) {
1369                     if (preg_match("/".$escape.$enclosure."/",$line)) {
1370                         while ($strlen = strlen($line)) {
1371                             $pos_delimiter       = strpos($line,$delimiter);
1372                             $pos_enclosure_start = strpos($line,$enclosure);
1373                             if (
1374                                 is_int($pos_delimiter) && is_int($pos_enclosure_start)
1375                                 && ($pos_enclosure_start < $pos_delimiter)
1376                                 ) {
1377                                 $enclosed_str = substr($line,1);
1378                                 $pos_enclosure_end = strpos($enclosed_str,$enclosure);
1379                                 $enclosed_str = substr($enclosed_str,0,$pos_enclosure_end);
1380                                 $output[$line_num][] = $enclosed_str;
1381                                 $offset = $pos_enclosure_end+3;
1382                             } else {
1383                                 if (empty($pos_delimiter) && empty($pos_enclosure_start)) {
1384                                     $output[$line_num][] = substr($line,0);
1385                                     $offset = strlen($line);
1386                                 } else {
1387                                     $output[$line_num][] = substr($line,0,$pos_delimiter);
1388                                     $offset = (
1389                                                 !empty($pos_enclosure_start)
1390                                                 && ($pos_enclosure_start < $pos_delimiter)
1391                                                 )
1392                                                 ?$pos_enclosure_start
1393                                                 :$pos_delimiter+1;
1394                                 }
1395                             }
1396                             $line = substr($line,$offset);
1397                         }
1398                     } else {
1399                         $line = preg_split("/".$delimiter."/",$line);
1400    
1401                         /*
1402                          * Validating against pesky extra line breaks creating false rows.
1403                          */
1404                         if (is_array($line) && !empty($line[0])) {
1405                             $output[$line_num] = $line;
1406                         } 
1407                     }
1408                 }
1409                 return $output;
1410             } else {
1411                 return false;
1412             }
1413         } else {
1414             return false;
1415         }
1416     }
1417
1418
1419 function cleardiv() {
1420         return '<div class="clear"></div>';
1421 }
1422
1423
1424 function bb_translate_video($s) {
1425
1426         $matches = null;
1427         $r = preg_match_all("/\[video\](.*?)\[\/video\]/ism",$s,$matches,PREG_SET_ORDER);
1428         if($r) {
1429                 foreach($matches as $mtch) {
1430                         if((stristr($mtch[1],'youtube')) || (stristr($mtch[1],'youtu.be')))
1431                                 $s = str_replace($mtch[0],'[youtube]' . $mtch[1] . '[/youtube]',$s);
1432                         elseif(stristr($mtch[1],'vimeo'))
1433                                 $s = str_replace($mtch[0],'[vimeo]' . $mtch[1] . '[/vimeo]',$s);
1434                 }
1435         }
1436         return $s;      
1437 }
1438
1439 function html2bb_video($s) {
1440
1441         $s = preg_replace('#<object[^>]+>(.*?)https?://www.youtube.com/((?:v|cp)/[A-Za-z0-9\-_=]+)(.*?)</object>#ism',
1442                         '[youtube]$2[/youtube]', $s);
1443
1444         $s = preg_replace('#<iframe[^>](.*?)https?://www.youtube.com/embed/([A-Za-z0-9\-_=]+)(.*?)</iframe>#ism',
1445                         '[youtube]$2[/youtube]', $s);
1446
1447         $s = preg_replace('#<iframe[^>](.*?)https?://player.vimeo.com/video/([0-9]+)(.*?)</iframe>#ism',
1448                         '[vimeo]$2[/vimeo]', $s);
1449
1450         return $s;
1451 }
1452
1453 /**
1454  * apply xmlify() to all values of array $val, recursively
1455  */
1456 function array_xmlify($val){
1457         if (is_bool($val)) return $val?"true":"false";
1458         if (is_array($val)) return array_map('array_xmlify', $val);
1459         return xmlify((string) $val);
1460 }
1461
1462
1463 function reltoabs($text, $base)
1464 {
1465   if (empty($base))
1466     return $text;
1467
1468   $base = rtrim($base,'/');
1469
1470   $base2 = $base . "/";
1471         
1472   // Replace links
1473   $pattern = "/<a([^>]*) href=\"(?!http|https|\/)([^\"]*)\"/";
1474   $replace = "<a\${1} href=\"" . $base2 . "\${2}\"";
1475   $text = preg_replace($pattern, $replace, $text);
1476
1477   $pattern = "/<a([^>]*) href=\"(?!http|https)([^\"]*)\"/";
1478   $replace = "<a\${1} href=\"" . $base . "\${2}\"";
1479   $text = preg_replace($pattern, $replace, $text);
1480
1481   // Replace images
1482   $pattern = "/<img([^>]*) src=\"(?!http|https|\/)([^\"]*)\"/";
1483   $replace = "<img\${1} src=\"" . $base2 . "\${2}\"";
1484   $text = preg_replace($pattern, $replace, $text); 
1485
1486   $pattern = "/<img([^>]*) src=\"(?!http|https)([^\"]*)\"/";
1487   $replace = "<img\${1} src=\"" . $base . "\${2}\"";
1488   $text = preg_replace($pattern, $replace, $text); 
1489
1490
1491   // Done
1492   return $text;
1493 }
1494
1495 function item_post_type($item) {
1496         if(intval($item['event-id']))
1497                 return t('event');
1498         if(strlen($item['resource-id']))
1499                 return t('photo');
1500         if(strlen($item['verb']) && $item['verb'] !== ACTIVITY_POST)
1501                 return t('activity');
1502         if($item['id'] != $item['parent'])
1503                 return t('comment');
1504         return t('post');
1505 }
1506
1507 // post categories and "save to file" use the same item.file table for storage.
1508 // We will differentiate the different uses by wrapping categories in angle brackets
1509 // and save to file categories in square brackets.
1510 // To do this we need to escape these characters if they appear in our tag. 
1511
1512 function file_tag_encode($s) {
1513         return str_replace(array('<','>','[',']'),array('%3c','%3e','%5b','%5d'),$s);
1514 }
1515
1516 function file_tag_decode($s) {
1517         return str_replace(array('%3c','%3e','%5b','%5d'),array('<','>','[',']'),$s);
1518 }
1519
1520 function file_tag_file_query($table,$s,$type = 'file') {
1521
1522         if($type == 'file')
1523                 $str = preg_quote( '[' . str_replace('%','%%',file_tag_encode($s)) . ']' );
1524         else
1525                 $str = preg_quote( '<' . str_replace('%','%%',file_tag_encode($s)) . '>' );
1526         return " AND " . (($table) ? dbesc($table) . '.' : '') . "file regexp '" . dbesc($str) . "' ";
1527 }
1528
1529 // ex. given music,video return <music><video> or [music][video]
1530 function file_tag_list_to_file($list,$type = 'file') {
1531         $tag_list = '';
1532         if(strlen($list)) {
1533                 $list_array = explode(",",$list);
1534                 if($type == 'file') {
1535                         $lbracket = '[';
1536                         $rbracket = ']';
1537                 }
1538                 else {
1539                         $lbracket = '<';
1540                         $rbracket = '>';
1541                 }
1542
1543                 foreach($list_array as $item) {
1544                   if(strlen($item)) {
1545                                 $tag_list .= $lbracket . file_tag_encode(trim($item))  . $rbracket;
1546                         }
1547                 }
1548         }
1549         return $tag_list;
1550 }
1551
1552 // ex. given <music><video>[friends], return music,video or friends
1553 function file_tag_file_to_list($file,$type = 'file') {
1554         $matches = false;
1555         $list = '';
1556         if($type == 'file') {
1557                 $cnt = preg_match_all('/\[(.*?)\]/',$file,$matches,PREG_SET_ORDER);
1558         }
1559         else {
1560                 $cnt = preg_match_all('/<(.*?)>/',$file,$matches,PREG_SET_ORDER);
1561         }
1562         if($cnt) {
1563                 foreach($matches as $mtch) {
1564                         if(strlen($list))
1565                                 $list .= ',';
1566                         $list .= file_tag_decode($mtch[1]);
1567                 }
1568         }
1569
1570         return $list;
1571 }
1572
1573 function file_tag_update_pconfig($uid,$file_old,$file_new,$type = 'file') {
1574         // $file_old - categories previously associated with an item
1575         // $file_new - new list of categories for an item
1576
1577         if(! intval($uid))
1578                 return false;
1579
1580         if($file_old == $file_new)
1581                 return true;
1582
1583         $saved = get_pconfig($uid,'system','filetags');
1584         if(strlen($saved)) {
1585                 if($type == 'file') {
1586                         $lbracket = '[';
1587                         $rbracket = ']';
1588                 }
1589                 else {
1590                         $lbracket = '<';
1591                         $rbracket = '>';
1592                 }
1593
1594                 $filetags_updated = $saved;
1595
1596                 // check for new tags to be added as filetags in pconfig
1597                 $new_tags = array();
1598                 $check_new_tags = explode(",",file_tag_file_to_list($file_new,$type));
1599
1600                 foreach($check_new_tags as $tag) {
1601                         if(! stristr($saved,$lbracket . file_tag_encode($tag) . $rbracket))
1602                                 $new_tags[] = $tag;
1603                 }
1604
1605                 $filetags_updated .= file_tag_list_to_file(implode(",",$new_tags),$type);
1606
1607                 // check for deleted tags to be removed from filetags in pconfig
1608                 $deleted_tags = array();
1609                 $check_deleted_tags = explode(",",file_tag_file_to_list($file_old,$type));
1610
1611                 foreach($check_deleted_tags as $tag) {
1612                         if(! stristr($file_new,$lbracket . file_tag_encode($tag) . $rbracket))
1613                                 $deleted_tags[] = $tag;
1614                 }
1615
1616                 foreach($deleted_tags as $key => $tag) {
1617                         $r = q("select file from item where uid = %d " . file_tag_file_query('item',$tag,$type),
1618                                 intval($uid)
1619                         );
1620
1621                         if(count($r)) {
1622                                 unset($deleted_tags[$key]);
1623                         }
1624                         else {
1625                                 $filetags_updated = str_replace($lbracket . file_tag_encode($tag) . $rbracket,'',$filetags_updated);
1626                         }
1627                 }
1628
1629                 if($saved != $filetags_updated) {
1630                         set_pconfig($uid,'system','filetags', $filetags_updated);
1631                 }
1632                 return true;
1633         }
1634         else
1635                 if(strlen($file_new)) {
1636                         set_pconfig($uid,'system','filetags', $file_new);
1637                 }
1638                 return true;
1639 }
1640
1641 function file_tag_save_file($uid,$item,$file) {
1642         $result = false;
1643         if(! intval($uid))
1644                 return false;
1645         $r = q("select file from item where id = %d and uid = %d limit 1",
1646                 intval($item),
1647                 intval($uid)
1648         );
1649         if(count($r)) {
1650                 if(! stristr($r[0]['file'],'[' . file_tag_encode($file) . ']'))
1651                         q("update item set file = '%s' where id = %d and uid = %d limit 1",
1652                                 dbesc($r[0]['file'] . '[' . file_tag_encode($file) . ']'),
1653                                 intval($item),
1654                                 intval($uid)
1655                         );
1656                 $saved = get_pconfig($uid,'system','filetags');
1657                 if((! strlen($saved)) || (! stristr($saved,'[' . file_tag_encode($file) . ']')))
1658                         set_pconfig($uid,'system','filetags',$saved . '[' . file_tag_encode($file) . ']');
1659                 info( t('Item filed') );
1660         }
1661         return true;
1662 }
1663
1664 function file_tag_unsave_file($uid,$item,$file,$cat = false) {
1665         $result = false;
1666         if(! intval($uid))
1667                 return false;
1668
1669         if($cat == true)
1670                 $pattern = '<' . file_tag_encode($file) . '>' ;
1671         else
1672                 $pattern = '[' . file_tag_encode($file) . ']' ;
1673
1674
1675         $r = q("select file from item where id = %d and uid = %d limit 1",
1676                 intval($item),
1677                 intval($uid)
1678         );
1679         if(! count($r))
1680                 return false;
1681
1682         q("update item set file = '%s' where id = %d and uid = %d limit 1",
1683                 dbesc(str_replace($pattern,'',$r[0]['file'])),
1684                 intval($item),
1685                 intval($uid)
1686         );
1687
1688         $r = q("select file from item where uid = %d and deleted = 0 " . file_tag_file_query('item',$file,(($cat) ? 'category' : 'file')),
1689                 intval($uid)
1690         );
1691
1692         if(! count($r)) {
1693                 $saved = get_pconfig($uid,'system','filetags');
1694                 set_pconfig($uid,'system','filetags',str_replace($pattern,'',$saved));
1695
1696         }
1697         return true;
1698 }
1699
1700 function normalise_openid($s) {
1701         return trim(str_replace(array('http://','https://'),array('',''),$s),'/');
1702 }
1703
1704
1705 function undo_post_tagging($s) {
1706         $matches = null;
1707         $cnt = preg_match_all('/([@#])\[url=(.*?)\](.*?)\[\/url\]/ism',$s,$matches,PREG_SET_ORDER);
1708         if($cnt) {
1709                 foreach($matches as $mtch) {
1710                         $s = str_replace($mtch[0], $mtch[1] . $mtch[3],$s);
1711                 }
1712         }
1713         return $s;
1714 }
1715
1716 function fix_mce_lf($s) {
1717         $s = str_replace("\r\n","\n",$s);
1718 //      $s = str_replace("\n\n","\n",$s);
1719         return $s;
1720 }
1721
1722
1723 function protect_sprintf($s) {
1724         return(str_replace('%','%%',$s));
1725 }
1726
1727
1728 function is_a_date_arg($s) {
1729         $i = intval($s);
1730         if($i > 1900) {
1731                 $y = date('Y');
1732                 if($i <= $y+1 && strpos($s,'-') == 4) {
1733                         $m = intval(substr($s,5));
1734                         if($m > 0 && $m <= 12)
1735                                 return true;
1736                 }
1737         }
1738         return false;
1739 }