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