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