]> git.mxchange.org Git - friendica.git/blob - include/text.php
83639ce85cde0eeaa58317b9e79ebc373b05001a
[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                 $stamp1 = microtime(true);
433                 $content = file_get_contents("$d/$lang/$b");
434                 $a->save_timestamp($stamp1, "file");
435                 return $content;
436         }
437
438         $theme = current_theme();
439
440         if(file_exists("$d/theme/$theme/$b")) {
441                 $stamp1 = microtime(true);
442                 $content = file_get_contents("$d/theme/$theme/$b");
443                 $a->save_timestamp($stamp1, "file");
444                 return $content;
445         }
446
447         $stamp1 = microtime(true);
448         $content = file_get_contents($s);
449         $a->save_timestamp($stamp1, "file");
450         return $content;
451 }}
452
453 if(! function_exists('get_intltext_template')) {
454 function get_intltext_template($s) {
455         global $lang;
456
457         $a = get_app();
458         $engine = '';
459         if($a->theme['template_engine'] === 'smarty3')
460                 $engine = "/smarty3";
461
462         if(! isset($lang))
463                 $lang = 'en';
464
465         if(file_exists("view/$lang$engine/$s")) {
466                 $stamp1 = microtime(true);
467                 $content = file_get_contents("view/$lang$engine/$s");
468                 $a->save_timestamp($stamp1, "file");
469                 return $content;
470         } elseif(file_exists("view/en$engine/$s")) {
471                 $stamp1 = microtime(true);
472                 $content = file_get_contents("view/en$engine/$s");
473                 $a->save_timestamp($stamp1, "file");
474                 return $content;
475         } else {
476                 $stamp1 = microtime(true);
477                 $content = file_get_contents("view$engine/$s");
478                 $a->save_timestamp($stamp1, "file");
479                 return $content;
480         }
481 }}
482
483 if(! function_exists('get_markup_template')) {
484 function get_markup_template($s, $root = '') {
485         $stamp1 = microtime(true);
486
487         $a = get_app();
488
489         if($a->theme['template_engine'] === 'smarty3') {
490                 $template_file = get_template_file($a, 'smarty3/' . $s, $root);
491
492                 $template = new FriendicaSmarty();
493                 $template->filename = $template_file;
494                 $a->save_timestamp($stamp1, "rendering");
495
496                 return $template;
497         }
498         else {
499                 $template_file = get_template_file($a, $s, $root);
500                 $a->save_timestamp($stamp1, "rendering");
501
502                 $stamp1 = microtime(true);
503                 $content = file_get_contents($template_file);
504                 $a->save_timestamp($stamp1, "file");
505                 return $content;
506
507         }
508 }}
509
510 if(! function_exists("get_template_file")) {
511 function get_template_file($a, $filename, $root = '') {
512         $theme = current_theme();
513
514         // Make sure $root ends with a slash /
515         if($root !== '' && $root[strlen($root)-1] !== '/')
516                 $root = $root . '/';
517
518         if(file_exists("{$root}view/theme/$theme/$filename"))
519                 $template_file = "{$root}view/theme/$theme/$filename";
520         elseif (x($a->theme_info,"extends") && file_exists("{$root}view/theme/{$a->theme_info["extends"]}/$filename"))
521                 $template_file = "{$root}view/theme/{$a->theme_info["extends"]}/$filename";
522         else
523                 $template_file = "{$root}view/$filename";
524
525         return $template_file;
526 }}
527
528
529
530
531
532 // for html,xml parsing - let's say you've got
533 // an attribute foobar="class1 class2 class3"
534 // and you want to find out if it contains 'class3'.
535 // you can't use a normal sub string search because you
536 // might match 'notclass3' and a regex to do the job is 
537 // possible but a bit complicated. 
538 // pass the attribute string as $attr and the attribute you 
539 // are looking for as $s - returns true if found, otherwise false
540
541 if(! function_exists('attribute_contains')) {
542 function attribute_contains($attr,$s) {
543         $a = explode(' ', $attr);
544         if(count($a) && in_array($s,$a))
545                 return true;
546         return false;
547 }}
548
549 if(! function_exists('logger')) {
550 function logger($msg,$level = 0) {
551         // turn off logger in install mode
552         global $a;
553         global $db;
554
555         if(($a->module == 'install') || (! ($db && $db->connected))) return;
556
557         $debugging = get_config('system','debugging');
558         $loglevel  = intval(get_config('system','loglevel'));
559         $logfile   = get_config('system','logfile');
560
561         if((! $debugging) || (! $logfile) || ($level > $loglevel))
562                 return;
563
564         $stamp1 = microtime(true);
565         @file_put_contents($logfile, datetime_convert() . ':' . session_id() . ' ' . $msg . "\n", FILE_APPEND);
566         $a->save_timestamp($stamp1, "file");
567         return;
568 }}
569
570
571 if(! function_exists('activity_match')) {
572 function activity_match($haystack,$needle) {
573         if(($haystack === $needle) || ((basename($needle) === $haystack) && strstr($needle,NAMESPACE_ACTIVITY_SCHEMA)))
574                 return true;
575         return false;
576 }}
577
578
579 // Pull out all #hashtags and @person tags from $s;
580 // We also get @person@domain.com - which would make 
581 // the regex quite complicated as tags can also
582 // end a sentence. So we'll run through our results
583 // and strip the period from any tags which end with one.
584 // Returns array of tags found, or empty array.
585
586
587 if(! function_exists('get_tags')) {
588 function get_tags($s) {
589         $ret = array();
590
591         // ignore anything in a code block
592         $s = preg_replace('/\[code\](.*?)\[\/code\]/sm','',$s);
593
594         // Force line feeds at bbtags
595         $s = str_replace(array("[", "]"), array("\n[", "]\n"), $s);
596
597         // ignore anything in a bbtag
598         $s = preg_replace('/\[(.*?)\]/sm','',$s);
599
600         // Match full names against @tags including the space between first and last
601         // We will look these up afterward to see if they are full names or not recognisable.
602
603         if(preg_match_all('/(@[^ \x0D\x0A,:?]+ [^ \x0D\x0A@,:?]+)([ \x0D\x0A@,:?]|$)/',$s,$match)) {
604                 foreach($match[1] as $mtch) {
605                         if(strstr($mtch,"]")) {
606                                 // we might be inside a bbcode color tag - leave it alone
607                                 continue;
608                         }
609                         if(substr($mtch,-1,1) === '.')
610                                 $ret[] = substr($mtch,0,-1);
611                         else
612                                 $ret[] = $mtch;
613                 }
614         }
615
616         // Otherwise pull out single word tags. These can be @nickname, @first_last
617         // and #hash tags.
618
619         if(preg_match_all('/([@#][^ \x0D\x0A,;:?]+)([ \x0D\x0A,;:?]|$)/',$s,$match)) {
620                 foreach($match[1] as $mtch) {
621                         if(strstr($mtch,"]")) {
622                                 // we might be inside a bbcode color tag - leave it alone
623                                 continue;
624                         }
625                         if(substr($mtch,-1,1) === '.')
626                                 $mtch = substr($mtch,0,-1);
627                         // ignore strictly numeric tags like #1
628                         if((strpos($mtch,'#') === 0) && ctype_digit(substr($mtch,1)))
629                                 continue;
630                         // try not to catch url fragments
631                         if(strpos($s,$mtch) && preg_match('/[a-zA-z0-9\/]/',substr($s,strpos($s,$mtch)-1,1)))
632                                 continue;
633                         $ret[] = $mtch;
634                 }
635         }
636         return $ret;
637 }}
638
639
640 // quick and dirty quoted_printable encoding
641
642 if(! function_exists('qp')) {
643 function qp($s) {
644 return str_replace ("%","=",rawurlencode($s));
645 }} 
646
647
648
649 if(! function_exists('get_mentions')) {
650 function get_mentions($item) {
651         $o = '';
652         if(! strlen($item['tag']))
653                 return $o;
654
655         $arr = explode(',',$item['tag']);
656         foreach($arr as $x) {
657                 $matches = null;
658                 if(preg_match('/@\[url=([^\]]*)\]/',$x,$matches)) {
659                         $o .= "\t\t" . '<link rel="mentioned" href="' . $matches[1] . '" />' . "\r\n";
660                         $o .= "\t\t" . '<link rel="ostatus:attention" href="' . $matches[1] . '" />' . "\r\n";
661                 }
662         }
663         return $o;
664 }}
665
666 if(! function_exists('contact_block')) {
667 function contact_block() {
668         $o = '';
669         $a = get_app();
670
671         $shown = get_pconfig($a->profile['uid'],'system','display_friend_count');
672         if($shown === false)
673                 $shown = 24;
674         if($shown == 0)
675                 return;
676
677         if((! is_array($a->profile)) || ($a->profile['hide-friends']))
678                 return $o;
679         $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",
680                         intval($a->profile['uid'])
681         );
682         if(count($r)) {
683                 $total = intval($r[0]['total']);
684         }
685         if(! $total) {
686                 $contacts = t('No contacts');
687                 $micropro = Null;
688                 
689         } else {
690                 $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",
691                                 intval($a->profile['uid']),
692                                 intval($shown)
693                 );
694                 if(count($r)) {
695                         $contacts = sprintf( tt('%d Contact','%d Contacts', $total),$total);
696                         $micropro = Array();
697                         foreach($r as $rr) {
698                                 $micropro[] = micropro($rr,true,'mpfriend');
699                         }
700                 }
701         }
702         
703         $tpl = get_markup_template('contact_block.tpl');
704         $o = replace_macros($tpl, array(
705                 '$contacts' => $contacts,
706                 '$nickname' => $a->profile['nickname'],
707                 '$viewcontacts' => t('View Contacts'),
708                 '$micropro' => $micropro,
709         ));
710
711         $arr = array('contacts' => $r, 'output' => $o);
712
713         call_hooks('contact_block_end', $arr);
714         return $o;
715
716 }}
717
718 if(! function_exists('micropro')) {
719 function micropro($contact, $redirect = false, $class = '', $textmode = false) {
720
721         if($class)
722                 $class = ' ' . $class;
723
724         $url = $contact['url'];
725         $sparkle = '';
726         $redir = false;
727
728         if($redirect) {
729                 $a = get_app();
730                 $redirect_url = $a->get_baseurl() . '/redir/' . $contact['id'];
731                 if(local_user() && ($contact['uid'] == local_user()) && ($contact['network'] === 'dfrn')) {
732                         $redir = true;
733                         $url = $redirect_url;
734                         $sparkle = ' sparkle';
735                 }
736                 else
737                         $url = zrl($url);
738         }
739         $click = ((x($contact,'click')) ? ' onclick="' . $contact['click'] . '" ' : '');
740         if($click)
741                 $url = '';
742         if($textmode) {
743                 return '<div class="contact-block-textdiv' . $class . '"><a class="contact-block-link' . $class . $sparkle 
744                         . (($click) ? ' fakelink' : '') . '" '
745                         . (($redir) ? ' target="redir" ' : '')
746                         . (($url) ? ' href="' . $url . '"' : '') . $click
747                         . '" title="' . $contact['name'] . ' [' . $contact['url'] . ']" alt="' . $contact['name'] 
748                         . '" >'. $contact['name'] . '</a></div>' . "\r\n";
749         }
750         else {
751                 return '<div class="contact-block-div' . $class . '"><a class="contact-block-link' . $class . $sparkle 
752                         . (($click) ? ' fakelink' : '') . '" '
753                         . (($redir) ? ' target="redir" ' : '')
754                         . (($url) ? ' href="' . $url . '"' : '') . $click . ' ><img class="contact-block-img' . $class . $sparkle . '" src="' 
755                         . $contact['micro'] . '" title="' . $contact['name'] . ' [' . $contact['url'] . ']" alt="' . $contact['name'] 
756                         . '" /></a></div>' . "\r\n";
757         }
758 }}
759
760
761
762 if(! function_exists('search')) {
763 function search($s,$id='search-box',$url='/search',$save = false) {
764         $a = get_app();
765         $o  = '<div id="' . $id . '">';
766         $o .= '<form action="' . $a->get_baseurl((stristr($url,'network')) ? true : false) . $url . '" method="get" >';
767         $o .= '<input type="text" name="search" id="search-text" placeholder="' . t('Search') . '" value="' . $s .'" />';
768         $o .= '<input type="submit" name="submit" id="search-submit" value="' . t('Search') . '" />'; 
769         if($save)
770                 $o .= '<input type="submit" name="save" id="search-save" value="' . t('Save') . '" />'; 
771         $o .= '</form></div>';
772         return $o;
773 }}
774
775 if(! function_exists('valid_email')) {
776 function valid_email($x){
777
778         if(get_config('system','disable_email_validation'))
779                 return true;
780
781         if(preg_match('/^[_a-zA-Z0-9\-\+]+(\.[_a-zA-Z0-9\-\+]+)*@[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+)+$/',$x))
782                 return true;
783         return false;
784 }}
785
786
787 /**
788  *
789  * Function: linkify
790  *
791  * Replace naked text hyperlink with HTML formatted hyperlink
792  *
793  */
794
795 if(! function_exists('linkify')) {
796 function linkify($s) {
797         $s = preg_replace("/(https?\:\/\/[a-zA-Z0-9\:\/\-\?\&\;\.\=\_\~\#\'\%\$\!\+]*)/", ' <a href="$1" target="external-link">$1</a>', $s);
798         $s = preg_replace("/\<(.*?)(src|href)=(.*?)\&amp\;(.*?)\>/ism",'<$1$2=$3&$4>',$s);
799         return($s);
800 }}
801
802 function get_poke_verbs() {
803         
804         // index is present tense verb
805         // value is array containing past tense verb, translation of present, translation of past
806
807         $arr = array(
808                 'poke' => array( 'poked', t('poke'), t('poked')),
809                 'ping' => array( 'pinged', t('ping'), t('pinged')),
810                 'prod' => array( 'prodded', t('prod'), t('prodded')),
811                 'slap' => array( 'slapped', t('slap'), t('slapped')),
812                 'finger' => array( 'fingered', t('finger'), t('fingered')),
813                 'rebuff' => array( 'rebuffed', t('rebuff'), t('rebuffed')),
814         );
815         call_hooks('poke_verbs', $arr);
816         return $arr;
817 }
818
819 function get_mood_verbs() {
820         
821         // index is present tense verb
822         // value is array containing past tense verb, translation of present, translation of past
823
824         $arr = array(
825                 'happy'      => t('happy'),
826                 'sad'        => t('sad'),
827                 'mellow'     => t('mellow'),
828                 'tired'      => t('tired'),
829                 'perky'      => t('perky'),
830                 'angry'      => t('angry'),
831                 'stupefied'  => t('stupified'),
832                 'puzzled'    => t('puzzled'),
833                 'interested' => t('interested'),
834                 'bitter'     => t('bitter'),
835                 'cheerful'   => t('cheerful'),
836                 'alive'      => t('alive'),
837                 'annoyed'    => t('annoyed'),
838                 'anxious'    => t('anxious'),
839                 'cranky'     => t('cranky'),
840                 'disturbed'  => t('disturbed'),
841                 'frustrated' => t('frustrated'),
842                 'motivated'  => t('motivated'),
843                 'relaxed'    => t('relaxed'),
844                 'surprised'  => t('surprised'),
845         );
846
847         call_hooks('mood_verbs', $arr);
848         return $arr;
849 }
850
851
852 /**
853  * 
854  * Function: smilies
855  *
856  * Description:
857  * Replaces text emoticons with graphical images
858  *
859  * @Parameter: string $s
860  *
861  * Returns string
862  *
863  * It is expected that this function will be called using HTML text.
864  * We will escape text between HTML pre and code blocks from being 
865  * processed. 
866  * 
867  * At a higher level, the bbcode [nosmile] tag can be used to prevent this 
868  * function from being executed by the prepare_text() routine when preparing
869  * bbcode source for HTML display
870  *
871  */
872
873 if(! function_exists('smilies')) {
874 function smilies($s, $sample = false) {
875
876         $a = get_app();
877
878         if(intval(get_config('system','no_smilies')) 
879                 || (local_user() && intval(get_pconfig(local_user(),'system','no_smilies'))))
880                 return $s;
881
882         $s = preg_replace_callback('/<pre>(.*?)<\/pre>/ism','smile_encode',$s);
883         $s = preg_replace_callback('/<code>(.*?)<\/code>/ism','smile_encode',$s);
884
885         $texts =  array( 
886                 '&lt;3', 
887                 '&lt;/3', 
888                 '&lt;\\3', 
889                 ':-)', 
890                 ';-)', 
891                 ':-(', 
892                 ':-P', 
893                 ':-p', 
894                 ':-"', 
895                 ':-&quot;', 
896                 ':-x', 
897                 ':-X', 
898                 ':-D', 
899                 '8-|', 
900                 '8-O', 
901                 ':-O', 
902                 '\\o/', 
903                 'o.O', 
904                 'O.o', 
905                 'o_O', 
906                 'O_o', 
907                 ":'(", 
908                 ":-!", 
909                 ":-/", 
910                 ":-[", 
911                 "8-)",
912                 ':beer', 
913                 ':homebrew', 
914                 ':coffee', 
915                 ':facepalm',
916                 ':like',
917                 ':dislike',
918                 '~friendica'
919
920         );
921
922         $icons = array(
923                 '<img class="smiley" src="' . $a->get_baseurl() . '/images/smiley-heart.gif" alt="<3" />',
924                 '<img class="smiley" src="' . $a->get_baseurl() . '/images/smiley-brokenheart.gif" alt="</3" />',
925                 '<img class="smiley" src="' . $a->get_baseurl() . '/images/smiley-brokenheart.gif" alt="<\\3" />',
926                 '<img class="smiley" src="' . $a->get_baseurl() . '/images/smiley-smile.gif" alt=":-)" />',
927                 '<img class="smiley" src="' . $a->get_baseurl() . '/images/smiley-wink.gif" alt=";-)" />',
928                 '<img class="smiley" src="' . $a->get_baseurl() . '/images/smiley-frown.gif" alt=":-(" />',
929                 '<img class="smiley" src="' . $a->get_baseurl() . '/images/smiley-tongue-out.gif" alt=":-P" />',
930                 '<img class="smiley" src="' . $a->get_baseurl() . '/images/smiley-tongue-out.gif" alt=":-p" />',
931                 '<img class="smiley" src="' . $a->get_baseurl() . '/images/smiley-kiss.gif" alt=":-\"" />',
932                 '<img class="smiley" src="' . $a->get_baseurl() . '/images/smiley-kiss.gif" alt=":-\"" />',
933                 '<img class="smiley" src="' . $a->get_baseurl() . '/images/smiley-kiss.gif" alt=":-x" />',
934                 '<img class="smiley" src="' . $a->get_baseurl() . '/images/smiley-kiss.gif" alt=":-X" />',
935                 '<img class="smiley" src="' . $a->get_baseurl() . '/images/smiley-laughing.gif" alt=":-D" />',
936                 '<img class="smiley" src="' . $a->get_baseurl() . '/images/smiley-surprised.gif" alt="8-|" />',
937                 '<img class="smiley" src="' . $a->get_baseurl() . '/images/smiley-surprised.gif" alt="8-O" />',
938                 '<img class="smiley" src="' . $a->get_baseurl() . '/images/smiley-surprised.gif" alt=":-O" />',                
939                 '<img class="smiley" src="' . $a->get_baseurl() . '/images/smiley-thumbsup.gif" alt="\\o/" />',
940                 '<img class="smiley" src="' . $a->get_baseurl() . '/images/smiley-Oo.gif" alt="o.O" />',
941                 '<img class="smiley" src="' . $a->get_baseurl() . '/images/smiley-Oo.gif" alt="O.o" />',
942                 '<img class="smiley" src="' . $a->get_baseurl() . '/images/smiley-Oo.gif" alt="o_O" />',
943                 '<img class="smiley" src="' . $a->get_baseurl() . '/images/smiley-Oo.gif" alt="O_o" />',
944                 '<img class="smiley" src="' . $a->get_baseurl() . '/images/smiley-cry.gif" alt=":\'(" />',
945                 '<img class="smiley" src="' . $a->get_baseurl() . '/images/smiley-foot-in-mouth.gif" alt=":-!" />',
946                 '<img class="smiley" src="' . $a->get_baseurl() . '/images/smiley-undecided.gif" alt=":-/" />',
947                 '<img class="smiley" src="' . $a->get_baseurl() . '/images/smiley-embarassed.gif" alt=":-[" />',
948                 '<img class="smiley" src="' . $a->get_baseurl() . '/images/smiley-cool.gif" alt="8-)" />',
949                 '<img class="smiley" src="' . $a->get_baseurl() . '/images/beer_mug.gif" alt=":beer" />',
950                 '<img class="smiley" src="' . $a->get_baseurl() . '/images/beer_mug.gif" alt=":homebrew" />',
951                 '<img class="smiley" src="' . $a->get_baseurl() . '/images/coffee.gif" alt=":coffee" />',
952                 '<img class="smiley" src="' . $a->get_baseurl() . '/images/smiley-facepalm.gif" alt=":facepalm" />',
953                 '<img class="smiley" src="' . $a->get_baseurl() . '/images/like.gif" alt=":like" />',
954                 '<img class="smiley" src="' . $a->get_baseurl() . '/images/dislike.gif" alt=":dislike" />',
955                 '<a href="http://friendica.com">~friendica <img class="smiley" src="' . $a->get_baseurl() . '/images/friendica-16.png" alt="~friendica" /></a>'
956         );
957
958         $params = array('texts' => $texts, 'icons' => $icons, 'string' => $s);
959         call_hooks('smilie', $params);
960
961         if($sample) {
962                 $s = '<div class="smiley-sample">';
963                 for($x = 0; $x < count($params['texts']); $x ++) {
964                         $s .= '<dl><dt>' . $params['texts'][$x] . '</dt><dd>' . $params['icons'][$x] . '</dd></dl>';
965                 }
966         }
967         else {
968                 $params['string'] = preg_replace_callback('/&lt;(3+)/','preg_heart',$params['string']);
969                 $s = str_replace($params['texts'],$params['icons'],$params['string']);
970         }
971
972         $s = preg_replace_callback('/<pre>(.*?)<\/pre>/ism','smile_decode',$s);
973         $s = preg_replace_callback('/<code>(.*?)<\/code>/ism','smile_decode',$s);
974
975         return $s;
976
977 }}
978
979 function smile_encode($m) {
980         return(str_replace($m[1],base64url_encode($m[1]),$m[0]));
981 }
982
983 function smile_decode($m) {
984         return(str_replace($m[1],base64url_decode($m[1]),$m[0]));
985 }
986
987 // expand <3333 to the correct number of hearts
988
989 function preg_heart($x) {
990         $a = get_app();
991         if(strlen($x[1]) == 1)
992                 return $x[0];
993         $t = '';
994         for($cnt = 0; $cnt < strlen($x[1]); $cnt ++)
995                 $t .= '<img class="smiley" src="' . $a->get_baseurl() . '/images/smiley-heart.gif" alt="<3" />';
996         $r =  str_replace($x[0],$t,$x[0]);
997         return $r;
998 }
999
1000
1001 if(! function_exists('day_translate')) {
1002 function day_translate($s) {
1003         $ret = str_replace(array('Monday','Tuesday','Wednesday','Thursday','Friday','Saturday','Sunday'),
1004                 array( t('Monday'), t('Tuesday'), t('Wednesday'), t('Thursday'), t('Friday'), t('Saturday'), t('Sunday')),
1005                 $s);
1006
1007         $ret = str_replace(array('January','February','March','April','May','June','July','August','September','October','November','December'),
1008                 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')),
1009                 $ret);
1010
1011         return $ret;
1012 }}
1013
1014
1015 if(! function_exists('normalise_link')) {
1016 function normalise_link($url) {
1017         $ret = str_replace(array('https:','//www.'), array('http:','//'), $url);
1018         return(rtrim($ret,'/'));
1019 }}
1020
1021 /**
1022  *
1023  * Compare two URLs to see if they are the same, but ignore
1024  * slight but hopefully insignificant differences such as if one 
1025  * is https and the other isn't, or if one is www.something and 
1026  * the other isn't - and also ignore case differences.
1027  *
1028  * Return true if the URLs match, otherwise false.
1029  *
1030  */
1031
1032 if(! function_exists('link_compare')) {
1033 function link_compare($a,$b) {
1034         if(strcasecmp(normalise_link($a),normalise_link($b)) === 0)
1035                 return true;
1036         return false;
1037 }}
1038
1039 // Given an item array, convert the body element from bbcode to html and add smilie icons.
1040 // If attach is true, also add icons for item attachments
1041
1042
1043 if(! function_exists('prepare_body')) {
1044 function prepare_body($item,$attach = false) {
1045
1046         $a = get_app();
1047         call_hooks('prepare_body_init', $item);
1048
1049         //$cachefile = get_cachefile($item["guid"]."-".strtotime($item["edited"])."-".hash("crc32", $item['body']));
1050         $cachefile = get_cachefile($item["guid"]."-".hash("md5", $item['body']));
1051
1052         if (($cachefile != '')) {
1053                 if (file_exists($cachefile)) {
1054                         $stamp1 = microtime(true);
1055                         $s = file_get_contents($cachefile);
1056                         $a->save_timestamp($stamp1, "file");
1057                 } else {
1058                         $s = prepare_text($item['body']);
1059                         $stamp1 = microtime(true);
1060                         file_put_contents($cachefile, $s);
1061                         $a->save_timestamp($stamp1, "file");
1062                         logger('prepare_body: put item '.$item["id"].' into cachefile '.$cachefile);
1063                 }
1064         } else
1065                 $s = prepare_text($item['body']);
1066
1067
1068         $prep_arr = array('item' => $item, 'html' => $s);
1069         call_hooks('prepare_body', $prep_arr);
1070         $s = $prep_arr['html'];
1071
1072         if(! $attach) {
1073                 // Replace the blockquotes with quotes that are used in mails
1074                 $mailquote = '<blockquote type="cite" class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex;">';
1075                 $s = str_replace(array('<blockquote>', '<blockquote class="spoiler">', '<blockquote class="author">'), array($mailquote, $mailquote, $mailquote), $s);
1076                 return $s;
1077         }
1078
1079         $arr = explode('[/attach],',$item['attach']);
1080         if(count($arr)) {
1081                 $s .= '<div class="body-attach">';
1082                 foreach($arr as $r) {
1083                         $matches = false;
1084                         $icon = '';
1085                         $cnt = preg_match_all('|\[attach\]href=\"(.*?)\" length=\"(.*?)\" type=\"(.*?)\" title=\"(.*?)\"|',$r,$matches, PREG_SET_ORDER);
1086                         if($cnt) {
1087                                 foreach($matches as $mtch) {
1088                                         $icontype = strtolower(substr($mtch[3],0,strpos($mtch[3],'/')));
1089                                         switch($icontype) {
1090                                                 case 'video':
1091                                                 case 'audio':
1092                                                 case 'image':
1093                                                 case 'text':
1094                                                         $icon = '<div class="attachtype icon s22 type-' . $icontype . '"></div>';
1095                                                         break;
1096                                                 default:
1097                                                         $icon = '<div class="attachtype icon s22 type-unkn"></div>';
1098                                                         break;
1099                                         }
1100                                         $title = ((strlen(trim($mtch[4]))) ? escape_tags(trim($mtch[4])) : escape_tags($mtch[1]));
1101                                         $title .= ' ' . $mtch[2] . ' ' . t('bytes');
1102                                         if((local_user() == $item['uid']) && ($item['contact-id'] != $a->contact['id']) && ($item['network'] == NETWORK_DFRN))
1103                                                 $the_url = $a->get_baseurl() . '/redir/' . $item['contact-id'] . '?f=1&url=' . $mtch[1];
1104                                         else
1105                                                 $the_url = $mtch[1];
1106
1107                                         $s .= '<a href="' . strip_tags($the_url) . '" title="' . $title . '" class="attachlink" target="external-link" >' . $icon . '</a>';
1108                                 }
1109                         }
1110                 }
1111                 $s .= '<div class="clear"></div></div>';
1112         }
1113
1114
1115         // Look for spoiler
1116         $spoilersearch = '<blockquote class="spoiler">';
1117
1118         // Remove line breaks before the spoiler
1119         while ((strpos($s, "\n".$spoilersearch) !== false))
1120                 $s = str_replace("\n".$spoilersearch, $spoilersearch, $s);
1121         while ((strpos($s, "<br />".$spoilersearch) !== false))
1122                 $s = str_replace("<br />".$spoilersearch, $spoilersearch, $s);
1123
1124         while ((strpos($s, $spoilersearch) !== false)) {
1125
1126                 $pos = strpos($s, $spoilersearch);
1127                 $rnd = random_string(8);
1128                 $spoilerreplace = '<br /> <span id="spoiler-wrap-'.$rnd.'" style="white-space:nowrap;" class="fakelink" onclick="openClose(\'spoiler-'.$rnd.'\');">'.sprintf(t('Click to open/close')).'</span>'.
1129                                         '<blockquote class="spoiler" id="spoiler-'.$rnd.'" style="display: none;">';
1130                 $s = substr($s, 0, $pos).$spoilerreplace.substr($s, $pos+strlen($spoilersearch));
1131         }
1132
1133         // Look for quote with author
1134         $authorsearch = '<blockquote class="author">';
1135
1136         while ((strpos($s, $authorsearch) !== false)) {
1137
1138                 $pos = strpos($s, $authorsearch);
1139                 $rnd = random_string(8);
1140                 $authorreplace = '<br /> <span id="author-wrap-'.$rnd.'" style="white-space:nowrap;" class="fakelink" onclick="openClose(\'author-'.$rnd.'\');">'.sprintf(t('Click to open/close')).'</span>'.
1141                                         '<blockquote class="author" id="author-'.$rnd.'" style="display: block;">';
1142                 $s = substr($s, 0, $pos).$authorreplace.substr($s, $pos+strlen($authorsearch));
1143         }
1144
1145         $prep_arr = array('item' => $item, 'html' => $s);
1146         call_hooks('prepare_body_final', $prep_arr);
1147
1148         return $prep_arr['html'];
1149 }}
1150
1151
1152 // Given a text string, convert from bbcode to html and add smilie icons.
1153
1154 if(! function_exists('prepare_text')) {
1155 function prepare_text($text) {
1156
1157         require_once('include/bbcode.php');
1158
1159         if(stristr($text,'[nosmile]'))
1160                 $s = bbcode($text);
1161         else
1162                 $s = smilies(bbcode($text));
1163
1164         return $s;
1165 }}
1166
1167
1168 /**
1169  * returns 
1170  * [
1171  *    //categories [
1172  *          {
1173  *               'name': 'category name',
1174  *              'removeurl': 'url to remove this category',
1175  *             'first': 'is the first in this array? true/false',
1176  *               'last': 'is the last in this array? true/false',
1177  *           } ,
1178  *           ....
1179  *       ],
1180  *       // folders [
1181  *               'name': 'folder name',
1182  *               'removeurl': 'url to remove this folder',
1183  *               'first': 'is the first in this array? true/false',
1184  *               'last': 'is the last in this array? true/false',
1185  *           } ,
1186  *           ....       
1187  *       ]
1188  *   ]
1189  */
1190 function get_cats_and_terms($item) {
1191     $a = get_app();
1192     $categories = array();
1193     $folders = array();
1194
1195     $matches = false; $first = true;
1196     $cnt = preg_match_all('/<(.*?)>/',$item['file'],$matches,PREG_SET_ORDER);
1197     if($cnt) {
1198         foreach($matches as $mtch) {
1199             $categories[] = array(
1200                 'name' => xmlify(file_tag_decode($mtch[1])),
1201                 'url' =>  "#",
1202                 'removeurl' => ((local_user() == $item['uid'])?$a->get_baseurl() . '/filerm/' . $item['id'] . '?f=&cat=' . xmlify(file_tag_decode($mtch[1])):""),
1203                 'first' => $first,
1204                 'last' => false
1205             );
1206             $first = false;
1207         }
1208     }
1209     if (count($categories)) $categories[count($categories)-1]['last'] = true;
1210     
1211
1212         if(local_user() == $item['uid']) {
1213             $matches = false; $first = true;
1214         $cnt = preg_match_all('/\[(.*?)\]/',$item['file'],$matches,PREG_SET_ORDER);
1215             if($cnt) {
1216             foreach($matches as $mtch) {
1217                     $folders[] = array(
1218                     'name' => xmlify(file_tag_decode($mtch[1])),
1219                          'url' =>  "#",
1220                         'removeurl' => ((local_user() == $item['uid'])?$a->get_baseurl() . '/filerm/' . $item['id'] . '?f=&term=' . xmlify(file_tag_decode($mtch[1])):""),
1221                     'first' => $first,
1222                         'last' => false
1223                 );
1224                     $first = false;
1225                         }
1226         }
1227     }
1228
1229     if (count($folders)) $folders[count($folders)-1]['last'] = true;
1230
1231     return array($categories, $folders);
1232 }
1233
1234
1235 /**
1236  * return atom link elements for all of our hubs
1237  */
1238
1239 if(! function_exists('feed_hublinks')) {
1240 function feed_hublinks() {
1241
1242         $hub = get_config('system','huburl');
1243
1244         $hubxml = '';
1245         if(strlen($hub)) {
1246                 $hubs = explode(',', $hub);
1247                 if(count($hubs)) {
1248                         foreach($hubs as $h) {
1249                                 $h = trim($h);
1250                                 if(! strlen($h))
1251                                         continue;
1252                                 $hubxml .= '<link rel="hub" href="' . xmlify($h) . '" />' . "\n" ;
1253                         }
1254                 }
1255         }
1256         return $hubxml;
1257 }}
1258
1259 /* return atom link elements for salmon endpoints */
1260
1261 if(! function_exists('feed_salmonlinks')) {
1262 function feed_salmonlinks($nick) {
1263
1264         $a = get_app();
1265
1266         $salmon  = '<link rel="salmon" href="' . xmlify($a->get_baseurl() . '/salmon/' . $nick) . '" />' . "\n" ;
1267
1268         // old style links that status.net still needed as of 12/2010 
1269
1270         $salmon .= '  <link rel="http://salmon-protocol.org/ns/salmon-replies" href="' . xmlify($a->get_baseurl() . '/salmon/' . $nick) . '" />' . "\n" ; 
1271         $salmon .= '  <link rel="http://salmon-protocol.org/ns/salmon-mention" href="' . xmlify($a->get_baseurl() . '/salmon/' . $nick) . '" />' . "\n" ; 
1272         return $salmon;
1273 }}
1274
1275 if(! function_exists('get_plink')) {
1276 function get_plink($item) {
1277         $a = get_app(); 
1278         if (x($item,'plink') && ($item['private'] != 1)) {
1279                 return array(
1280                         'href' => $item['plink'],
1281                         'title' => t('link to source'),
1282                 );
1283         } 
1284         else {
1285                 return false;
1286         }
1287 }}
1288
1289 if(! function_exists('unamp')) {
1290 function unamp($s) {
1291         return str_replace('&amp;', '&', $s);
1292 }}
1293
1294
1295
1296
1297 if(! function_exists('lang_selector')) {
1298 function lang_selector() {
1299         global $lang;
1300         
1301         $langs = glob('view/*/strings.php');
1302         
1303         $lang_options = array();
1304         $selected = "";
1305         
1306         if(is_array($langs) && count($langs)) {
1307                 $langs[] = '';
1308                 if(! in_array('view/en/strings.php',$langs))
1309                         $langs[] = 'view/en/';
1310                 asort($langs);
1311                 foreach($langs as $l) {
1312                         if($l == '') {
1313                                 $lang_options[""] = t('default');
1314                                 continue;
1315                         }
1316                         $ll = substr($l,5);
1317                         $ll = substr($ll,0,strrpos($ll,'/'));
1318                         $selected = (($ll === $lang && (x($_SESSION, 'language'))) ? $ll : $selected);
1319                         $lang_options[$ll]=$ll;
1320                 }
1321         }
1322
1323         $tpl = get_markup_template("lang_selector.tpl");        
1324         $o = replace_macros($tpl, array(
1325                 '$title' => t('Select an alternate language'),
1326                 '$langs' => array($lang_options, $selected),
1327                 
1328         ));
1329         return $o;
1330 }}
1331
1332
1333 if(! function_exists('return_bytes')) {
1334 function return_bytes ($size_str) {
1335     switch (substr ($size_str, -1))
1336     {
1337         case 'M': case 'm': return (int)$size_str * 1048576;
1338         case 'K': case 'k': return (int)$size_str * 1024;
1339         case 'G': case 'g': return (int)$size_str * 1073741824;
1340         default: return $size_str;
1341     }
1342 }}
1343
1344 function generate_user_guid() {
1345         $found = true;
1346         do {
1347                 $guid = random_string(16);
1348                 $x = q("SELECT `uid` FROM `user` WHERE `guid` = '%s' LIMIT 1",
1349                         dbesc($guid)
1350                 );
1351                 if(! count($x))
1352                         $found = false;
1353         } while ($found == true );
1354         return $guid;
1355 }
1356
1357
1358
1359 function base64url_encode($s, $strip_padding = false) {
1360
1361         $s = strtr(base64_encode($s),'+/','-_');
1362
1363         if($strip_padding)
1364                 $s = str_replace('=','',$s);
1365
1366         return $s;
1367 }
1368
1369 function base64url_decode($s) {
1370
1371         if(is_array($s)) {
1372                 logger('base64url_decode: illegal input: ' . print_r(debug_backtrace(), true));
1373                 return $s;
1374         }
1375
1376 /*
1377  *  // Placeholder for new rev of salmon which strips base64 padding.
1378  *  // PHP base64_decode handles the un-padded input without requiring this step
1379  *  // Uncomment if you find you need it.
1380  *
1381  *      $l = strlen($s);
1382  *      if(! strpos($s,'=')) {
1383  *              $m = $l % 4;
1384  *              if($m == 2)
1385  *                      $s .= '==';
1386  *              if($m == 3)
1387  *                      $s .= '=';
1388  *      }
1389  *
1390  */
1391
1392         return base64_decode(strtr($s,'-_','+/'));
1393 }
1394
1395
1396 if (!function_exists('str_getcsv')) {
1397     function str_getcsv($input, $delimiter = ',', $enclosure = '"', $escape = '\\', $eol = '\n') {
1398         if (is_string($input) && !empty($input)) {
1399             $output = array();
1400             $tmp    = preg_split("/".$eol."/",$input);
1401             if (is_array($tmp) && !empty($tmp)) {
1402                 while (list($line_num, $line) = each($tmp)) {
1403                     if (preg_match("/".$escape.$enclosure."/",$line)) {
1404                         while ($strlen = strlen($line)) {
1405                             $pos_delimiter       = strpos($line,$delimiter);
1406                             $pos_enclosure_start = strpos($line,$enclosure);
1407                             if (
1408                                 is_int($pos_delimiter) && is_int($pos_enclosure_start)
1409                                 && ($pos_enclosure_start < $pos_delimiter)
1410                                 ) {
1411                                 $enclosed_str = substr($line,1);
1412                                 $pos_enclosure_end = strpos($enclosed_str,$enclosure);
1413                                 $enclosed_str = substr($enclosed_str,0,$pos_enclosure_end);
1414                                 $output[$line_num][] = $enclosed_str;
1415                                 $offset = $pos_enclosure_end+3;
1416                             } else {
1417                                 if (empty($pos_delimiter) && empty($pos_enclosure_start)) {
1418                                     $output[$line_num][] = substr($line,0);
1419                                     $offset = strlen($line);
1420                                 } else {
1421                                     $output[$line_num][] = substr($line,0,$pos_delimiter);
1422                                     $offset = (
1423                                                 !empty($pos_enclosure_start)
1424                                                 && ($pos_enclosure_start < $pos_delimiter)
1425                                                 )
1426                                                 ?$pos_enclosure_start
1427                                                 :$pos_delimiter+1;
1428                                 }
1429                             }
1430                             $line = substr($line,$offset);
1431                         }
1432                     } else {
1433                         $line = preg_split("/".$delimiter."/",$line);
1434    
1435                         /*
1436                          * Validating against pesky extra line breaks creating false rows.
1437                          */
1438                         if (is_array($line) && !empty($line[0])) {
1439                             $output[$line_num] = $line;
1440                         } 
1441                     }
1442                 }
1443                 return $output;
1444             } else {
1445                 return false;
1446             }
1447         } else {
1448             return false;
1449         }
1450     }
1451
1452
1453 function cleardiv() {
1454         return '<div class="clear"></div>';
1455 }
1456
1457
1458 function bb_translate_video($s) {
1459
1460         $matches = null;
1461         $r = preg_match_all("/\[video\](.*?)\[\/video\]/ism",$s,$matches,PREG_SET_ORDER);
1462         if($r) {
1463                 foreach($matches as $mtch) {
1464                         if((stristr($mtch[1],'youtube')) || (stristr($mtch[1],'youtu.be')))
1465                                 $s = str_replace($mtch[0],'[youtube]' . $mtch[1] . '[/youtube]',$s);
1466                         elseif(stristr($mtch[1],'vimeo'))
1467                                 $s = str_replace($mtch[0],'[vimeo]' . $mtch[1] . '[/vimeo]',$s);
1468                 }
1469         }
1470         return $s;      
1471 }
1472
1473 function html2bb_video($s) {
1474
1475         $s = preg_replace('#<object[^>]+>(.*?)https?://www.youtube.com/((?:v|cp)/[A-Za-z0-9\-_=]+)(.*?)</object>#ism',
1476                         '[youtube]$2[/youtube]', $s);
1477
1478         $s = preg_replace('#<iframe[^>](.*?)https?://www.youtube.com/embed/([A-Za-z0-9\-_=]+)(.*?)</iframe>#ism',
1479                         '[youtube]$2[/youtube]', $s);
1480
1481         $s = preg_replace('#<iframe[^>](.*?)https?://player.vimeo.com/video/([0-9]+)(.*?)</iframe>#ism',
1482                         '[vimeo]$2[/vimeo]', $s);
1483
1484         return $s;
1485 }
1486
1487 /**
1488  * apply xmlify() to all values of array $val, recursively
1489  */
1490 function array_xmlify($val){
1491         if (is_bool($val)) return $val?"true":"false";
1492         if (is_array($val)) return array_map('array_xmlify', $val);
1493         return xmlify((string) $val);
1494 }
1495
1496
1497 function reltoabs($text, $base)
1498 {
1499   if (empty($base))
1500     return $text;
1501
1502   $base = rtrim($base,'/');
1503
1504   $base2 = $base . "/";
1505         
1506   // Replace links
1507   $pattern = "/<a([^>]*) href=\"(?!http|https|\/)([^\"]*)\"/";
1508   $replace = "<a\${1} href=\"" . $base2 . "\${2}\"";
1509   $text = preg_replace($pattern, $replace, $text);
1510
1511   $pattern = "/<a([^>]*) href=\"(?!http|https)([^\"]*)\"/";
1512   $replace = "<a\${1} href=\"" . $base . "\${2}\"";
1513   $text = preg_replace($pattern, $replace, $text);
1514
1515   // Replace images
1516   $pattern = "/<img([^>]*) src=\"(?!http|https|\/)([^\"]*)\"/";
1517   $replace = "<img\${1} src=\"" . $base2 . "\${2}\"";
1518   $text = preg_replace($pattern, $replace, $text); 
1519
1520   $pattern = "/<img([^>]*) src=\"(?!http|https)([^\"]*)\"/";
1521   $replace = "<img\${1} src=\"" . $base . "\${2}\"";
1522   $text = preg_replace($pattern, $replace, $text); 
1523
1524
1525   // Done
1526   return $text;
1527 }
1528
1529 function item_post_type($item) {
1530         if(intval($item['event-id']))
1531                 return t('event');
1532         if(strlen($item['resource-id']))
1533                 return t('photo');
1534         if(strlen($item['verb']) && $item['verb'] !== ACTIVITY_POST)
1535                 return t('activity');
1536         if($item['id'] != $item['parent'])
1537                 return t('comment');
1538         return t('post');
1539 }
1540
1541 // post categories and "save to file" use the same item.file table for storage.
1542 // We will differentiate the different uses by wrapping categories in angle brackets
1543 // and save to file categories in square brackets.
1544 // To do this we need to escape these characters if they appear in our tag. 
1545
1546 function file_tag_encode($s) {
1547         return str_replace(array('<','>','[',']'),array('%3c','%3e','%5b','%5d'),$s);
1548 }
1549
1550 function file_tag_decode($s) {
1551         return str_replace(array('%3c','%3e','%5b','%5d'),array('<','>','[',']'),$s);
1552 }
1553
1554 function file_tag_file_query($table,$s,$type = 'file') {
1555
1556         if($type == 'file')
1557                 $str = preg_quote( '[' . str_replace('%','%%',file_tag_encode($s)) . ']' );
1558         else
1559                 $str = preg_quote( '<' . str_replace('%','%%',file_tag_encode($s)) . '>' );
1560         return " AND " . (($table) ? dbesc($table) . '.' : '') . "file regexp '" . dbesc($str) . "' ";
1561 }
1562
1563 // ex. given music,video return <music><video> or [music][video]
1564 function file_tag_list_to_file($list,$type = 'file') {
1565         $tag_list = '';
1566         if(strlen($list)) {
1567                 $list_array = explode(",",$list);
1568                 if($type == 'file') {
1569                         $lbracket = '[';
1570                         $rbracket = ']';
1571                 }
1572                 else {
1573                         $lbracket = '<';
1574                         $rbracket = '>';
1575                 }
1576
1577                 foreach($list_array as $item) {
1578                   if(strlen($item)) {
1579                                 $tag_list .= $lbracket . file_tag_encode(trim($item))  . $rbracket;
1580                         }
1581                 }
1582         }
1583         return $tag_list;
1584 }
1585
1586 // ex. given <music><video>[friends], return music,video or friends
1587 function file_tag_file_to_list($file,$type = 'file') {
1588         $matches = false;
1589         $list = '';
1590         if($type == 'file') {
1591                 $cnt = preg_match_all('/\[(.*?)\]/',$file,$matches,PREG_SET_ORDER);
1592         }
1593         else {
1594                 $cnt = preg_match_all('/<(.*?)>/',$file,$matches,PREG_SET_ORDER);
1595         }
1596         if($cnt) {
1597                 foreach($matches as $mtch) {
1598                         if(strlen($list))
1599                                 $list .= ',';
1600                         $list .= file_tag_decode($mtch[1]);
1601                 }
1602         }
1603
1604         return $list;
1605 }
1606
1607 function file_tag_update_pconfig($uid,$file_old,$file_new,$type = 'file') {
1608         // $file_old - categories previously associated with an item
1609         // $file_new - new list of categories for an item
1610
1611         if(! intval($uid))
1612                 return false;
1613
1614         if($file_old == $file_new)
1615                 return true;
1616
1617         $saved = get_pconfig($uid,'system','filetags');
1618         if(strlen($saved)) {
1619                 if($type == 'file') {
1620                         $lbracket = '[';
1621                         $rbracket = ']';
1622                 }
1623                 else {
1624                         $lbracket = '<';
1625                         $rbracket = '>';
1626                 }
1627
1628                 $filetags_updated = $saved;
1629
1630                 // check for new tags to be added as filetags in pconfig
1631                 $new_tags = array();
1632                 $check_new_tags = explode(",",file_tag_file_to_list($file_new,$type));
1633
1634                 foreach($check_new_tags as $tag) {
1635                         if(! stristr($saved,$lbracket . file_tag_encode($tag) . $rbracket))
1636                                 $new_tags[] = $tag;
1637                 }
1638
1639                 $filetags_updated .= file_tag_list_to_file(implode(",",$new_tags),$type);
1640
1641                 // check for deleted tags to be removed from filetags in pconfig
1642                 $deleted_tags = array();
1643                 $check_deleted_tags = explode(",",file_tag_file_to_list($file_old,$type));
1644
1645                 foreach($check_deleted_tags as $tag) {
1646                         if(! stristr($file_new,$lbracket . file_tag_encode($tag) . $rbracket))
1647                                 $deleted_tags[] = $tag;
1648                 }
1649
1650                 foreach($deleted_tags as $key => $tag) {
1651                         $r = q("select file from item where uid = %d " . file_tag_file_query('item',$tag,$type),
1652                                 intval($uid)
1653                         );
1654
1655                         if(count($r)) {
1656                                 unset($deleted_tags[$key]);
1657                         }
1658                         else {
1659                                 $filetags_updated = str_replace($lbracket . file_tag_encode($tag) . $rbracket,'',$filetags_updated);
1660                         }
1661                 }
1662
1663                 if($saved != $filetags_updated) {
1664                         set_pconfig($uid,'system','filetags', $filetags_updated);
1665                 }
1666                 return true;
1667         }
1668         else
1669                 if(strlen($file_new)) {
1670                         set_pconfig($uid,'system','filetags', $file_new);
1671                 }
1672                 return true;
1673 }
1674
1675 function file_tag_save_file($uid,$item,$file) {
1676         $result = false;
1677         if(! intval($uid))
1678                 return false;
1679         $r = q("select file from item where id = %d and uid = %d limit 1",
1680                 intval($item),
1681                 intval($uid)
1682         );
1683         if(count($r)) {
1684                 if(! stristr($r[0]['file'],'[' . file_tag_encode($file) . ']'))
1685                         q("update item set file = '%s' where id = %d and uid = %d limit 1",
1686                                 dbesc($r[0]['file'] . '[' . file_tag_encode($file) . ']'),
1687                                 intval($item),
1688                                 intval($uid)
1689                         );
1690                 $saved = get_pconfig($uid,'system','filetags');
1691                 if((! strlen($saved)) || (! stristr($saved,'[' . file_tag_encode($file) . ']')))
1692                         set_pconfig($uid,'system','filetags',$saved . '[' . file_tag_encode($file) . ']');
1693                 info( t('Item filed') );
1694         }
1695         return true;
1696 }
1697
1698 function file_tag_unsave_file($uid,$item,$file,$cat = false) {
1699         $result = false;
1700         if(! intval($uid))
1701                 return false;
1702
1703         if($cat == true)
1704                 $pattern = '<' . file_tag_encode($file) . '>' ;
1705         else
1706                 $pattern = '[' . file_tag_encode($file) . ']' ;
1707
1708
1709         $r = q("select file from item where id = %d and uid = %d limit 1",
1710                 intval($item),
1711                 intval($uid)
1712         );
1713         if(! count($r))
1714                 return false;
1715
1716         q("update item set file = '%s' where id = %d and uid = %d limit 1",
1717                 dbesc(str_replace($pattern,'',$r[0]['file'])),
1718                 intval($item),
1719                 intval($uid)
1720         );
1721
1722         $r = q("select file from item where uid = %d and deleted = 0 " . file_tag_file_query('item',$file,(($cat) ? 'category' : 'file')),
1723                 intval($uid)
1724         );
1725
1726         if(! count($r)) {
1727                 $saved = get_pconfig($uid,'system','filetags');
1728                 set_pconfig($uid,'system','filetags',str_replace($pattern,'',$saved));
1729
1730         }
1731         return true;
1732 }
1733
1734 function normalise_openid($s) {
1735         return trim(str_replace(array('http://','https://'),array('',''),$s),'/');
1736 }
1737
1738
1739 function undo_post_tagging($s) {
1740         $matches = null;
1741         $cnt = preg_match_all('/([@#])\[url=(.*?)\](.*?)\[\/url\]/ism',$s,$matches,PREG_SET_ORDER);
1742         if($cnt) {
1743                 foreach($matches as $mtch) {
1744                         $s = str_replace($mtch[0], $mtch[1] . $mtch[3],$s);
1745                 }
1746         }
1747         return $s;
1748 }
1749
1750 function fix_mce_lf($s) {
1751         $s = str_replace("\r\n","\n",$s);
1752 //      $s = str_replace("\n\n","\n",$s);
1753         return $s;
1754 }
1755
1756
1757 function protect_sprintf($s) {
1758         return(str_replace('%','%%',$s));
1759 }
1760
1761
1762 function is_a_date_arg($s) {
1763         $i = intval($s);
1764         if($i > 1900) {
1765                 $y = date('Y');
1766                 if($i <= $y+1 && strpos($s,'-') == 4) {
1767                         $m = intval(substr($s,5));
1768                         if($m > 0 && $m <= 12)
1769                                 return true;
1770                 }
1771         }
1772         return false;
1773 }