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