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