]> git.mxchange.org Git - friendica.git/blob - include/text.php
Preloading of items in the cache when receiving them.
[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         $cachefile = get_cachefile($item["guid"]."-".hash("md5", $item['body']));
1024
1025         if (($cachefile != '')) {
1026                 if (file_exists($cachefile))
1027                         $s = file_get_contents($cachefile);
1028                 else {
1029                         $s = prepare_text($item['body']);
1030                         file_put_contents($cachefile, $s);
1031                         logger('prepare_body: put item '.$item["id"].' into cachefile '.$cachefile);
1032                 }
1033         } else
1034                 $s = prepare_text($item['body']);
1035
1036
1037         $prep_arr = array('item' => $item, 'html' => $s);
1038         call_hooks('prepare_body', $prep_arr);
1039         $s = $prep_arr['html'];
1040
1041         if(! $attach) {
1042                 // Replace the blockquotes with quotes that are used in mails
1043                 $mailquote = '<blockquote type="cite" class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex;">';
1044                 $s = str_replace(array('<blockquote>', '<blockquote class="spoiler">', '<blockquote class="author">'), array($mailquote, $mailquote, $mailquote), $s);
1045                 return $s;
1046         }
1047
1048         $arr = explode(',',$item['attach']);
1049         if(count($arr)) {
1050                 $s .= '<div class="body-attach">';
1051                 foreach($arr as $r) {
1052                         $matches = false;
1053                         $icon = '';
1054                         $cnt = preg_match_all('|\[attach\]href=\"(.*?)\" length=\"(.*?)\" type=\"(.*?)\" title=\"(.*?)\"\[\/attach\]|',$r,$matches, PREG_SET_ORDER);
1055                         if($cnt) {
1056                                 foreach($matches as $mtch) {
1057                                         $icontype = strtolower(substr($mtch[3],0,strpos($mtch[3],'/')));
1058                                         switch($icontype) {
1059                                                 case 'video':
1060                                                 case 'audio':
1061                                                 case 'image':
1062                                                 case 'text':
1063                                                         $icon = '<div class="attachtype icon s22 type-' . $icontype . '"></div>';
1064                                                         break;
1065                                                 default:
1066                                                         $icon = '<div class="attachtype icon s22 type-unkn"></div>';
1067                                                         break;
1068                                         }
1069                                         $title = ((strlen(trim($mtch[4]))) ? escape_tags(trim($mtch[4])) : escape_tags($mtch[1]));
1070                                         $title .= ' ' . $mtch[2] . ' ' . t('bytes');
1071                                         if((local_user() == $item['uid']) && ($item['contact-id'] != $a->contact['id']) && ($item['network'] == NETWORK_DFRN))
1072                                                 $the_url = $a->get_baseurl() . '/redir/' . $item['contact-id'] . '?f=1&url=' . $mtch[1];
1073                                         else
1074                                                 $the_url = $mtch[1];
1075
1076                                         $s .= '<a href="' . strip_tags($the_url) . '" title="' . $title . '" class="attachlink" target="external-link" >' . $icon . '</a>';
1077                                 }
1078                         }
1079                 }
1080                 $s .= '<div class="clear"></div></div>';
1081         }
1082
1083
1084         // Look for spoiler
1085         $spoilersearch = '<blockquote class="spoiler">';
1086
1087         // Remove line breaks before the spoiler
1088         while ((strpos($s, "\n".$spoilersearch) !== false))
1089                 $s = str_replace("\n".$spoilersearch, $spoilersearch, $s);
1090         while ((strpos($s, "<br />".$spoilersearch) !== false))
1091                 $s = str_replace("<br />".$spoilersearch, $spoilersearch, $s);
1092
1093         while ((strpos($s, $spoilersearch) !== false)) {
1094
1095                 $pos = strpos($s, $spoilersearch);
1096                 $rnd = random_string(8);
1097                 $spoilerreplace = '<br /> <span id="spoiler-wrap-'.$rnd.'" style="white-space:nowrap;" class="fakelink" onclick="openClose(\'spoiler-'.$rnd.'\');">'.sprintf(t('Click to open/close')).'</span>'.
1098                                         '<blockquote class="spoiler" id="spoiler-'.$rnd.'" style="display: none;">';
1099                 $s = substr($s, 0, $pos).$spoilerreplace.substr($s, $pos+strlen($spoilersearch));
1100         }
1101
1102         // Look for quote with author
1103         $authorsearch = '<blockquote class="author">';
1104
1105         while ((strpos($s, $authorsearch) !== false)) {
1106
1107                 $pos = strpos($s, $authorsearch);
1108                 $rnd = random_string(8);
1109                 $authorreplace = '<br /> <span id="author-wrap-'.$rnd.'" style="white-space:nowrap;" class="fakelink" onclick="openClose(\'author-'.$rnd.'\');">'.sprintf(t('Click to open/close')).'</span>'.
1110                                         '<blockquote class="author" id="author-'.$rnd.'" style="display: block;">';
1111                 $s = substr($s, 0, $pos).$authorreplace.substr($s, $pos+strlen($authorsearch));
1112         }
1113
1114         $prep_arr = array('item' => $item, 'html' => $s);
1115         call_hooks('prepare_body_final', $prep_arr);
1116
1117         return $prep_arr['html'];
1118 }}
1119
1120
1121 // Given a text string, convert from bbcode to html and add smilie icons.
1122
1123 if(! function_exists('prepare_text')) {
1124 function prepare_text($text) {
1125
1126         require_once('include/bbcode.php');
1127
1128         if(stristr($text,'[nosmile]'))
1129                 $s = bbcode($text);
1130         else
1131                 $s = smilies(bbcode($text));
1132
1133         return $s;
1134 }}
1135
1136
1137 /**
1138  * returns 
1139  * [
1140  *    //categories [
1141  *          {
1142  *               'name': 'category name',
1143  *              'removeurl': 'url to remove this category',
1144  *             'first': 'is the first in this array? true/false',
1145  *               'last': 'is the last in this array? true/false',
1146  *           } ,
1147  *           ....
1148  *       ],
1149  *       // folders [
1150  *               'name': 'folder name',
1151  *               'removeurl': 'url to remove this folder',
1152  *               'first': 'is the first in this array? true/false',
1153  *               'last': 'is the last in this array? true/false',
1154  *           } ,
1155  *           ....       
1156  *       ]
1157  *   ]
1158  */
1159 function get_cats_and_terms($item) {
1160     $a = get_app();
1161     $categories = array();
1162     $folders = array();
1163
1164     $matches = false; $first = true;
1165     $cnt = preg_match_all('/<(.*?)>/',$item['file'],$matches,PREG_SET_ORDER);
1166     if($cnt) {
1167         foreach($matches as $mtch) {
1168             $categories[] = array(
1169                 'name' => xmlify(file_tag_decode($mtch[1])),
1170                 'url' =>  "#",
1171                 'removeurl' => ((local_user() == $item['uid'])?$a->get_baseurl() . '/filerm/' . $item['id'] . '?f=&cat=' . xmlify(file_tag_decode($mtch[1])):""),
1172                 'first' => $first,
1173                 'last' => false
1174             );
1175             $first = false;
1176         }
1177     }
1178     if (count($categories)) $categories[count($categories)-1]['last'] = true;
1179     
1180
1181         if(local_user() == $item['uid']) {
1182             $matches = false; $first = true;
1183         $cnt = preg_match_all('/\[(.*?)\]/',$item['file'],$matches,PREG_SET_ORDER);
1184             if($cnt) {
1185             foreach($matches as $mtch) {
1186                     $folders[] = array(
1187                     'name' => xmlify(file_tag_decode($mtch[1])),
1188                          'url' =>  "#",
1189                         'removeurl' => ((local_user() == $item['uid'])?$a->get_baseurl() . '/filerm/' . $item['id'] . '?f=&term=' . xmlify(file_tag_decode($mtch[1])):""),
1190                     'first' => $first,
1191                         'last' => false
1192                 );
1193                     $first = false;
1194                         }
1195         }
1196     }
1197
1198     if (count($folders)) $folders[count($folders)-1]['last'] = true;
1199
1200     return array($categories, $folders);
1201 }
1202
1203
1204 /**
1205  * return atom link elements for all of our hubs
1206  */
1207
1208 if(! function_exists('feed_hublinks')) {
1209 function feed_hublinks() {
1210
1211         $hub = get_config('system','huburl');
1212
1213         $hubxml = '';
1214         if(strlen($hub)) {
1215                 $hubs = explode(',', $hub);
1216                 if(count($hubs)) {
1217                         foreach($hubs as $h) {
1218                                 $h = trim($h);
1219                                 if(! strlen($h))
1220                                         continue;
1221                                 $hubxml .= '<link rel="hub" href="' . xmlify($h) . '" />' . "\n" ;
1222                         }
1223                 }
1224         }
1225         return $hubxml;
1226 }}
1227
1228 /* return atom link elements for salmon endpoints */
1229
1230 if(! function_exists('feed_salmonlinks')) {
1231 function feed_salmonlinks($nick) {
1232
1233         $a = get_app();
1234
1235         $salmon  = '<link rel="salmon" href="' . xmlify($a->get_baseurl() . '/salmon/' . $nick) . '" />' . "\n" ;
1236
1237         // old style links that status.net still needed as of 12/2010 
1238
1239         $salmon .= '  <link rel="http://salmon-protocol.org/ns/salmon-replies" href="' . xmlify($a->get_baseurl() . '/salmon/' . $nick) . '" />' . "\n" ; 
1240         $salmon .= '  <link rel="http://salmon-protocol.org/ns/salmon-mention" href="' . xmlify($a->get_baseurl() . '/salmon/' . $nick) . '" />' . "\n" ; 
1241         return $salmon;
1242 }}
1243
1244 if(! function_exists('get_plink')) {
1245 function get_plink($item) {
1246         $a = get_app(); 
1247         if (x($item,'plink') && ($item['private'] != 1)) {
1248                 return array(
1249                         'href' => $item['plink'],
1250                         'title' => t('link to source'),
1251                 );
1252         } 
1253         else {
1254                 return false;
1255         }
1256 }}
1257
1258 if(! function_exists('unamp')) {
1259 function unamp($s) {
1260         return str_replace('&amp;', '&', $s);
1261 }}
1262
1263
1264
1265
1266 if(! function_exists('lang_selector')) {
1267 function lang_selector() {
1268         global $lang;
1269         
1270         $langs = glob('view/*/strings.php');
1271         
1272         $lang_options = array();
1273         $selected = "";
1274         
1275         if(is_array($langs) && count($langs)) {
1276                 $langs[] = '';
1277                 if(! in_array('view/en/strings.php',$langs))
1278                         $langs[] = 'view/en/';
1279                 asort($langs);
1280                 foreach($langs as $l) {
1281                         if($l == '') {
1282                                 $lang_options[""] = t('default');
1283                                 continue;
1284                         }
1285                         $ll = substr($l,5);
1286                         $ll = substr($ll,0,strrpos($ll,'/'));
1287                         $selected = (($ll === $lang && (x($_SESSION, 'language'))) ? $ll : $selected);
1288                         $lang_options[$ll]=$ll;
1289                 }
1290         }
1291
1292         $tpl = get_markup_template("lang_selector.tpl");        
1293         $o = replace_macros($tpl, array(
1294                 '$title' => t('Select an alternate language'),
1295                 '$langs' => array($lang_options, $selected),
1296                 
1297         ));
1298         return $o;
1299 }}
1300
1301
1302 if(! function_exists('return_bytes')) {
1303 function return_bytes ($size_str) {
1304     switch (substr ($size_str, -1))
1305     {
1306         case 'M': case 'm': return (int)$size_str * 1048576;
1307         case 'K': case 'k': return (int)$size_str * 1024;
1308         case 'G': case 'g': return (int)$size_str * 1073741824;
1309         default: return $size_str;
1310     }
1311 }}
1312
1313 function generate_user_guid() {
1314         $found = true;
1315         do {
1316                 $guid = random_string(16);
1317                 $x = q("SELECT `uid` FROM `user` WHERE `guid` = '%s' LIMIT 1",
1318                         dbesc($guid)
1319                 );
1320                 if(! count($x))
1321                         $found = false;
1322         } while ($found == true );
1323         return $guid;
1324 }
1325
1326
1327
1328 function base64url_encode($s, $strip_padding = false) {
1329
1330         $s = strtr(base64_encode($s),'+/','-_');
1331
1332         if($strip_padding)
1333                 $s = str_replace('=','',$s);
1334
1335         return $s;
1336 }
1337
1338 function base64url_decode($s) {
1339
1340         if(is_array($s)) {
1341                 logger('base64url_decode: illegal input: ' . print_r(debug_backtrace(), true));
1342                 return $s;
1343         }
1344
1345 /*
1346  *  // Placeholder for new rev of salmon which strips base64 padding.
1347  *  // PHP base64_decode handles the un-padded input without requiring this step
1348  *  // Uncomment if you find you need it.
1349  *
1350  *      $l = strlen($s);
1351  *      if(! strpos($s,'=')) {
1352  *              $m = $l % 4;
1353  *              if($m == 2)
1354  *                      $s .= '==';
1355  *              if($m == 3)
1356  *                      $s .= '=';
1357  *      }
1358  *
1359  */
1360
1361         return base64_decode(strtr($s,'-_','+/'));
1362 }
1363
1364
1365 if (!function_exists('str_getcsv')) {
1366     function str_getcsv($input, $delimiter = ',', $enclosure = '"', $escape = '\\', $eol = '\n') {
1367         if (is_string($input) && !empty($input)) {
1368             $output = array();
1369             $tmp    = preg_split("/".$eol."/",$input);
1370             if (is_array($tmp) && !empty($tmp)) {
1371                 while (list($line_num, $line) = each($tmp)) {
1372                     if (preg_match("/".$escape.$enclosure."/",$line)) {
1373                         while ($strlen = strlen($line)) {
1374                             $pos_delimiter       = strpos($line,$delimiter);
1375                             $pos_enclosure_start = strpos($line,$enclosure);
1376                             if (
1377                                 is_int($pos_delimiter) && is_int($pos_enclosure_start)
1378                                 && ($pos_enclosure_start < $pos_delimiter)
1379                                 ) {
1380                                 $enclosed_str = substr($line,1);
1381                                 $pos_enclosure_end = strpos($enclosed_str,$enclosure);
1382                                 $enclosed_str = substr($enclosed_str,0,$pos_enclosure_end);
1383                                 $output[$line_num][] = $enclosed_str;
1384                                 $offset = $pos_enclosure_end+3;
1385                             } else {
1386                                 if (empty($pos_delimiter) && empty($pos_enclosure_start)) {
1387                                     $output[$line_num][] = substr($line,0);
1388                                     $offset = strlen($line);
1389                                 } else {
1390                                     $output[$line_num][] = substr($line,0,$pos_delimiter);
1391                                     $offset = (
1392                                                 !empty($pos_enclosure_start)
1393                                                 && ($pos_enclosure_start < $pos_delimiter)
1394                                                 )
1395                                                 ?$pos_enclosure_start
1396                                                 :$pos_delimiter+1;
1397                                 }
1398                             }
1399                             $line = substr($line,$offset);
1400                         }
1401                     } else {
1402                         $line = preg_split("/".$delimiter."/",$line);
1403    
1404                         /*
1405                          * Validating against pesky extra line breaks creating false rows.
1406                          */
1407                         if (is_array($line) && !empty($line[0])) {
1408                             $output[$line_num] = $line;
1409                         } 
1410                     }
1411                 }
1412                 return $output;
1413             } else {
1414                 return false;
1415             }
1416         } else {
1417             return false;
1418         }
1419     }
1420
1421
1422 function cleardiv() {
1423         return '<div class="clear"></div>';
1424 }
1425
1426
1427 function bb_translate_video($s) {
1428
1429         $matches = null;
1430         $r = preg_match_all("/\[video\](.*?)\[\/video\]/ism",$s,$matches,PREG_SET_ORDER);
1431         if($r) {
1432                 foreach($matches as $mtch) {
1433                         if((stristr($mtch[1],'youtube')) || (stristr($mtch[1],'youtu.be')))
1434                                 $s = str_replace($mtch[0],'[youtube]' . $mtch[1] . '[/youtube]',$s);
1435                         elseif(stristr($mtch[1],'vimeo'))
1436                                 $s = str_replace($mtch[0],'[vimeo]' . $mtch[1] . '[/vimeo]',$s);
1437                 }
1438         }
1439         return $s;      
1440 }
1441
1442 function html2bb_video($s) {
1443
1444         $s = preg_replace('#<object[^>]+>(.*?)https?://www.youtube.com/((?:v|cp)/[A-Za-z0-9\-_=]+)(.*?)</object>#ism',
1445                         '[youtube]$2[/youtube]', $s);
1446
1447         $s = preg_replace('#<iframe[^>](.*?)https?://www.youtube.com/embed/([A-Za-z0-9\-_=]+)(.*?)</iframe>#ism',
1448                         '[youtube]$2[/youtube]', $s);
1449
1450         $s = preg_replace('#<iframe[^>](.*?)https?://player.vimeo.com/video/([0-9]+)(.*?)</iframe>#ism',
1451                         '[vimeo]$2[/vimeo]', $s);
1452
1453         return $s;
1454 }
1455
1456 /**
1457  * apply xmlify() to all values of array $val, recursively
1458  */
1459 function array_xmlify($val){
1460         if (is_bool($val)) return $val?"true":"false";
1461         if (is_array($val)) return array_map('array_xmlify', $val);
1462         return xmlify((string) $val);
1463 }
1464
1465
1466 function reltoabs($text, $base)
1467 {
1468   if (empty($base))
1469     return $text;
1470
1471   $base = rtrim($base,'/');
1472
1473   $base2 = $base . "/";
1474         
1475   // Replace links
1476   $pattern = "/<a([^>]*) href=\"(?!http|https|\/)([^\"]*)\"/";
1477   $replace = "<a\${1} href=\"" . $base2 . "\${2}\"";
1478   $text = preg_replace($pattern, $replace, $text);
1479
1480   $pattern = "/<a([^>]*) href=\"(?!http|https)([^\"]*)\"/";
1481   $replace = "<a\${1} href=\"" . $base . "\${2}\"";
1482   $text = preg_replace($pattern, $replace, $text);
1483
1484   // Replace images
1485   $pattern = "/<img([^>]*) src=\"(?!http|https|\/)([^\"]*)\"/";
1486   $replace = "<img\${1} src=\"" . $base2 . "\${2}\"";
1487   $text = preg_replace($pattern, $replace, $text); 
1488
1489   $pattern = "/<img([^>]*) src=\"(?!http|https)([^\"]*)\"/";
1490   $replace = "<img\${1} src=\"" . $base . "\${2}\"";
1491   $text = preg_replace($pattern, $replace, $text); 
1492
1493
1494   // Done
1495   return $text;
1496 }
1497
1498 function item_post_type($item) {
1499         if(intval($item['event-id']))
1500                 return t('event');
1501         if(strlen($item['resource-id']))
1502                 return t('photo');
1503         if(strlen($item['verb']) && $item['verb'] !== ACTIVITY_POST)
1504                 return t('activity');
1505         if($item['id'] != $item['parent'])
1506                 return t('comment');
1507         return t('post');
1508 }
1509
1510 // post categories and "save to file" use the same item.file table for storage.
1511 // We will differentiate the different uses by wrapping categories in angle brackets
1512 // and save to file categories in square brackets.
1513 // To do this we need to escape these characters if they appear in our tag. 
1514
1515 function file_tag_encode($s) {
1516         return str_replace(array('<','>','[',']'),array('%3c','%3e','%5b','%5d'),$s);
1517 }
1518
1519 function file_tag_decode($s) {
1520         return str_replace(array('%3c','%3e','%5b','%5d'),array('<','>','[',']'),$s);
1521 }
1522
1523 function file_tag_file_query($table,$s,$type = 'file') {
1524
1525         if($type == 'file')
1526                 $str = preg_quote( '[' . str_replace('%','%%',file_tag_encode($s)) . ']' );
1527         else
1528                 $str = preg_quote( '<' . str_replace('%','%%',file_tag_encode($s)) . '>' );
1529         return " AND " . (($table) ? dbesc($table) . '.' : '') . "file regexp '" . dbesc($str) . "' ";
1530 }
1531
1532 // ex. given music,video return <music><video> or [music][video]
1533 function file_tag_list_to_file($list,$type = 'file') {
1534         $tag_list = '';
1535         if(strlen($list)) {
1536                 $list_array = explode(",",$list);
1537                 if($type == 'file') {
1538                         $lbracket = '[';
1539                         $rbracket = ']';
1540                 }
1541                 else {
1542                         $lbracket = '<';
1543                         $rbracket = '>';
1544                 }
1545
1546                 foreach($list_array as $item) {
1547                   if(strlen($item)) {
1548                                 $tag_list .= $lbracket . file_tag_encode(trim($item))  . $rbracket;
1549                         }
1550                 }
1551         }
1552         return $tag_list;
1553 }
1554
1555 // ex. given <music><video>[friends], return music,video or friends
1556 function file_tag_file_to_list($file,$type = 'file') {
1557         $matches = false;
1558         $list = '';
1559         if($type == 'file') {
1560                 $cnt = preg_match_all('/\[(.*?)\]/',$file,$matches,PREG_SET_ORDER);
1561         }
1562         else {
1563                 $cnt = preg_match_all('/<(.*?)>/',$file,$matches,PREG_SET_ORDER);
1564         }
1565         if($cnt) {
1566                 foreach($matches as $mtch) {
1567                         if(strlen($list))
1568                                 $list .= ',';
1569                         $list .= file_tag_decode($mtch[1]);
1570                 }
1571         }
1572
1573         return $list;
1574 }
1575
1576 function file_tag_update_pconfig($uid,$file_old,$file_new,$type = 'file') {
1577         // $file_old - categories previously associated with an item
1578         // $file_new - new list of categories for an item
1579
1580         if(! intval($uid))
1581                 return false;
1582
1583         if($file_old == $file_new)
1584                 return true;
1585
1586         $saved = get_pconfig($uid,'system','filetags');
1587         if(strlen($saved)) {
1588                 if($type == 'file') {
1589                         $lbracket = '[';
1590                         $rbracket = ']';
1591                 }
1592                 else {
1593                         $lbracket = '<';
1594                         $rbracket = '>';
1595                 }
1596
1597                 $filetags_updated = $saved;
1598
1599                 // check for new tags to be added as filetags in pconfig
1600                 $new_tags = array();
1601                 $check_new_tags = explode(",",file_tag_file_to_list($file_new,$type));
1602
1603                 foreach($check_new_tags as $tag) {
1604                         if(! stristr($saved,$lbracket . file_tag_encode($tag) . $rbracket))
1605                                 $new_tags[] = $tag;
1606                 }
1607
1608                 $filetags_updated .= file_tag_list_to_file(implode(",",$new_tags),$type);
1609
1610                 // check for deleted tags to be removed from filetags in pconfig
1611                 $deleted_tags = array();
1612                 $check_deleted_tags = explode(",",file_tag_file_to_list($file_old,$type));
1613
1614                 foreach($check_deleted_tags as $tag) {
1615                         if(! stristr($file_new,$lbracket . file_tag_encode($tag) . $rbracket))
1616                                 $deleted_tags[] = $tag;
1617                 }
1618
1619                 foreach($deleted_tags as $key => $tag) {
1620                         $r = q("select file from item where uid = %d " . file_tag_file_query('item',$tag,$type),
1621                                 intval($uid)
1622                         );
1623
1624                         if(count($r)) {
1625                                 unset($deleted_tags[$key]);
1626                         }
1627                         else {
1628                                 $filetags_updated = str_replace($lbracket . file_tag_encode($tag) . $rbracket,'',$filetags_updated);
1629                         }
1630                 }
1631
1632                 if($saved != $filetags_updated) {
1633                         set_pconfig($uid,'system','filetags', $filetags_updated);
1634                 }
1635                 return true;
1636         }
1637         else
1638                 if(strlen($file_new)) {
1639                         set_pconfig($uid,'system','filetags', $file_new);
1640                 }
1641                 return true;
1642 }
1643
1644 function file_tag_save_file($uid,$item,$file) {
1645         $result = false;
1646         if(! intval($uid))
1647                 return false;
1648         $r = q("select file from item where id = %d and uid = %d limit 1",
1649                 intval($item),
1650                 intval($uid)
1651         );
1652         if(count($r)) {
1653                 if(! stristr($r[0]['file'],'[' . file_tag_encode($file) . ']'))
1654                         q("update item set file = '%s' where id = %d and uid = %d limit 1",
1655                                 dbesc($r[0]['file'] . '[' . file_tag_encode($file) . ']'),
1656                                 intval($item),
1657                                 intval($uid)
1658                         );
1659                 $saved = get_pconfig($uid,'system','filetags');
1660                 if((! strlen($saved)) || (! stristr($saved,'[' . file_tag_encode($file) . ']')))
1661                         set_pconfig($uid,'system','filetags',$saved . '[' . file_tag_encode($file) . ']');
1662                 info( t('Item filed') );
1663         }
1664         return true;
1665 }
1666
1667 function file_tag_unsave_file($uid,$item,$file,$cat = false) {
1668         $result = false;
1669         if(! intval($uid))
1670                 return false;
1671
1672         if($cat == true)
1673                 $pattern = '<' . file_tag_encode($file) . '>' ;
1674         else
1675                 $pattern = '[' . file_tag_encode($file) . ']' ;
1676
1677
1678         $r = q("select file from item where id = %d and uid = %d limit 1",
1679                 intval($item),
1680                 intval($uid)
1681         );
1682         if(! count($r))
1683                 return false;
1684
1685         q("update item set file = '%s' where id = %d and uid = %d limit 1",
1686                 dbesc(str_replace($pattern,'',$r[0]['file'])),
1687                 intval($item),
1688                 intval($uid)
1689         );
1690
1691         $r = q("select file from item where uid = %d and deleted = 0 " . file_tag_file_query('item',$file,(($cat) ? 'category' : 'file')),
1692                 intval($uid)
1693         );
1694
1695         if(! count($r)) {
1696                 $saved = get_pconfig($uid,'system','filetags');
1697                 set_pconfig($uid,'system','filetags',str_replace($pattern,'',$saved));
1698
1699         }
1700         return true;
1701 }
1702
1703 function normalise_openid($s) {
1704         return trim(str_replace(array('http://','https://'),array('',''),$s),'/');
1705 }
1706
1707
1708 function undo_post_tagging($s) {
1709         $matches = null;
1710         $cnt = preg_match_all('/([@#])\[url=(.*?)\](.*?)\[\/url\]/ism',$s,$matches,PREG_SET_ORDER);
1711         if($cnt) {
1712                 foreach($matches as $mtch) {
1713                         $s = str_replace($mtch[0], $mtch[1] . $mtch[3],$s);
1714                 }
1715         }
1716         return $s;
1717 }
1718
1719 function fix_mce_lf($s) {
1720         $s = str_replace("\r\n","\n",$s);
1721 //      $s = str_replace("\n\n","\n",$s);
1722         return $s;
1723 }
1724
1725
1726 function protect_sprintf($s) {
1727         return(str_replace('%','%%',$s));
1728 }
1729
1730
1731 function is_a_date_arg($s) {
1732         $i = intval($s);
1733         if($i > 1900) {
1734                 $y = date('Y');
1735                 if($i <= $y+1 && strpos($s,'-') == 4) {
1736                         $m = intval(substr($s,5));
1737                         if($m > 0 && $m <= 12)
1738                                 return true;
1739                 }
1740         }
1741         return false;
1742 }