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