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