]> git.mxchange.org Git - friendica.git/blob - include/text.php
50e2e81931724a2e29f1c7ce5bb7404a8f8fa488
[friendica.git] / include / text.php
1 <?php
2
3 require_once("include/template_processor.php");
4 require_once("include/friendica_smarty.php");
5 require_once("include/Smilies.php");
6 require_once("include/map.php");
7 require_once("mod/proxy.php");
8
9
10 if(! function_exists('replace_macros')) {
11 /**
12  * This is our template processor
13  *
14  * @param string|FriendicaSmarty $s the string requiring macro substitution,
15  *                                                                      or an instance of FriendicaSmarty
16  * @param array $r key value pairs (search => replace)
17  * @return string substituted string
18  */
19 function replace_macros($s,$r) {
20
21         $stamp1 = microtime(true);
22
23         $a = get_app();
24
25         // pass $baseurl to all templates
26         $r['$baseurl'] = $a->get_baseurl();
27
28
29         $t = $a->template_engine();
30         try {
31                 $output = $t->replace_macros($s,$r);
32         } catch (Exception $e) {
33                 echo "<pre><b>".__function__."</b>: ".$e->getMessage()."</pre>"; killme();
34         }
35
36         $a->save_timestamp($stamp1, "rendering");
37
38         return $output;
39 }}
40
41
42 // random string, there are 86 characters max in text mode, 128 for hex
43 // output is urlsafe
44
45 define('RANDOM_STRING_HEX',  0x00 );
46 define('RANDOM_STRING_TEXT', 0x01 );
47
48 if(! function_exists('random_string')) {
49 function random_string($size = 64,$type = RANDOM_STRING_HEX) {
50         // generate a bit of entropy and run it through the whirlpool
51         $s = hash('whirlpool', (string) rand() . uniqid(rand(),true) . (string) rand(),(($type == RANDOM_STRING_TEXT) ? true : false));
52         $s = (($type == RANDOM_STRING_TEXT) ? str_replace("\n","",base64url_encode($s,true)) : $s);
53         return(substr($s,0,$size));
54 }}
55
56 if(! function_exists('notags')) {
57 /**
58  * This is our primary input filter.
59  *
60  * The high bit hack only involved some old IE browser, forget which (IE5/Mac?)
61  * that had an XSS attack vector due to stripping the high-bit on an 8-bit character
62  * after cleansing, and angle chars with the high bit set could get through as markup.
63  *
64  * This is now disabled because it was interfering with some legitimate unicode sequences
65  * and hopefully there aren't a lot of those browsers left.
66  *
67  * Use this on any text input where angle chars are not valid or permitted
68  * They will be replaced with safer brackets. This may be filtered further
69  * if these are not allowed either.
70  *
71  * @param string $string Input string
72  * @return string Filtered string
73  */
74 function notags($string) {
75
76         return(str_replace(array("<",">"), array('[',']'), $string));
77
78 //  High-bit filter no longer used
79 //      return(str_replace(array("<",">","\xBA","\xBC","\xBE"), array('[',']','','',''), $string));
80 }}
81
82
83
84 if(! function_exists('escape_tags')) {
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  * @param string $string
89  * @return string
90  */
91 function escape_tags($string) {
92
93         return(htmlspecialchars($string, ENT_COMPAT, 'UTF-8', false));
94 }}
95
96
97 // generate a string that's random, but usually pronounceable.
98 // used to generate initial passwords
99
100 if(! function_exists('autoname')) {
101 /**
102  * generate a string that's random, but usually pronounceable.
103  * used to generate initial passwords
104  * @param int $len
105  * @return string
106  */
107 function autoname($len) {
108
109         if($len <= 0)
110                 return '';
111
112         $vowels = array('a','a','ai','au','e','e','e','ee','ea','i','ie','o','ou','u');
113         if(mt_rand(0,5) == 4)
114                 $vowels[] = 'y';
115
116         $cons = array(
117                         'b','bl','br',
118                         'c','ch','cl','cr',
119                         'd','dr',
120                         'f','fl','fr',
121                         'g','gh','gl','gr',
122                         'h',
123                         'j',
124                         'k','kh','kl','kr',
125                         'l',
126                         'm',
127                         'n',
128                         'p','ph','pl','pr',
129                         'qu',
130                         'r','rh',
131                         's','sc','sh','sm','sp','st',
132                         't','th','tr',
133                         'v',
134                         'w','wh',
135                         'x',
136                         'z','zh'
137                         );
138
139         $midcons = array('ck','ct','gn','ld','lf','lm','lt','mb','mm', 'mn','mp',
140                                 'nd','ng','nk','nt','rn','rp','rt');
141
142         $noend = array('bl', 'br', 'cl','cr','dr','fl','fr','gl','gr',
143                                 'kh', 'kl','kr','mn','pl','pr','rh','tr','qu','wh');
144
145         $start = mt_rand(0,2);
146         if($start == 0)
147                 $table = $vowels;
148         else
149                 $table = $cons;
150
151         $word = '';
152
153         for ($x = 0; $x < $len; $x ++) {
154                 $r = mt_rand(0,count($table) - 1);
155                 $word .= $table[$r];
156
157                 if($table == $vowels)
158                         $table = array_merge($cons,$midcons);
159                 else
160                         $table = $vowels;
161
162         }
163
164         $word = substr($word,0,$len);
165
166         foreach($noend as $noe) {
167                 if((strlen($word) > 2) && (substr($word,-2) == $noe)) {
168                         $word = substr($word,0,-1);
169                         break;
170                 }
171         }
172         if(substr($word,-1) == 'q')
173                 $word = substr($word,0,-1);
174         return $word;
175 }}
176
177
178 // escape text ($str) for XML transport
179 // returns escaped text.
180
181 if(! function_exists('xmlify')) {
182 /**
183  * escape text ($str) for XML transport
184  * @param string $str
185  * @return string Escaped text.
186  */
187 function xmlify($str) {
188 /*      $buffer = '';
189
190         $len = mb_strlen($str);
191         for($x = 0; $x < $len; $x ++) {
192                 $char = mb_substr($str,$x,1);
193
194                 switch( $char ) {
195
196                         case "\r" :
197                                 break;
198                         case "&" :
199                                 $buffer .= '&amp;';
200                                 break;
201                         case "'" :
202                                 $buffer .= '&apos;';
203                                 break;
204                         case "\"" :
205                                 $buffer .= '&quot;';
206                                 break;
207                         case '<' :
208                                 $buffer .= '&lt;';
209                                 break;
210                         case '>' :
211                                 $buffer .= '&gt;';
212                                 break;
213                         case "\n" :
214                                 $buffer .= "\n";
215                                 break;
216                         default :
217                                 $buffer .= $char;
218                                 break;
219                 }
220         }*/
221         /*
222         $buffer = mb_ereg_replace("&", "&amp;", $str);
223         $buffer = mb_ereg_replace("'", "&apos;", $buffer);
224         $buffer = mb_ereg_replace('"', "&quot;", $buffer);
225         $buffer = mb_ereg_replace("<", "&lt;", $buffer);
226         $buffer = mb_ereg_replace(">", "&gt;", $buffer);
227         */
228         $buffer = htmlspecialchars($str, ENT_QUOTES, "UTF-8");
229         $buffer = trim($buffer);
230
231         return($buffer);
232 }}
233
234 if(! function_exists('unxmlify')) {
235 /**
236  * undo an xmlify
237  * @param string $s xml escaped text
238  * @return string unescaped text
239  */
240 function unxmlify($s) {
241 //      $ret = str_replace('&amp;','&', $s);
242 //      $ret = str_replace(array('&lt;','&gt;','&quot;','&apos;'),array('<','>','"',"'"),$ret);
243         /*$ret = mb_ereg_replace('&amp;', '&', $s);
244         $ret = mb_ereg_replace('&apos;', "'", $ret);
245         $ret = mb_ereg_replace('&quot;', '"', $ret);
246         $ret = mb_ereg_replace('&lt;', "<", $ret);
247         $ret = mb_ereg_replace('&gt;', ">", $ret);
248         */
249         $ret = htmlspecialchars_decode($s, ENT_QUOTES);
250         return $ret;
251 }}
252
253 if(! function_exists('hex2bin')) {
254 /**
255  * convenience wrapper, reverse the operation "bin2hex"
256  * @param string $s
257  * @return number
258  */
259 function hex2bin($s) {
260         if(! (is_string($s) && strlen($s)))
261                 return '';
262
263         if(! ctype_xdigit($s)) {
264                 return($s);
265         }
266
267         return(pack("H*",$s));
268 }}
269
270
271 if(! function_exists('paginate_data')) {
272 /**
273  * Automatica pagination data.
274  *
275  * @param App $a App instance
276  * @param int $count [optional] item count (used with alt pager)
277  * @return Array data for pagination template
278  */
279 function paginate_data(&$a, $count=null) {
280         $stripped = preg_replace('/([&?]page=[0-9]*)/','',$a->query_string);
281
282         $stripped = str_replace('q=','',$stripped);
283         $stripped = trim($stripped,'/');
284         $pagenum = $a->pager['page'];
285
286         if (($a->page_offset != "") AND !preg_match('/[?&].offset=/', $stripped))
287                 $stripped .= "&offset=".urlencode($a->page_offset);
288
289         $url = $stripped;
290
291         $data = array();
292         function _l(&$d, $name, $url, $text, $class="") {
293                 if (!strpos($url, "?")) {
294                         if ($pos = strpos($url, "&"))
295                                 $url = substr($url, 0, $pos)."?".substr($url, $pos + 1);
296                 }
297
298                 $d[$name] = array('url'=>$url, 'text'=>$text, 'class'=>$class);
299         }
300
301         if (!is_null($count)){
302                 // alt pager
303                 if($a->pager['page']>1)
304                         _l($data,  "prev", $url.'&page='.($a->pager['page'] - 1), t('newer'));
305                 if($count>0)
306                         _l($data,  "next", $url.'&page='.($a->pager['page'] + 1), t('older'));
307         } else {
308                 // full pager
309                 if($a->pager['total'] > $a->pager['itemspage']) {
310                         if($a->pager['page'] != 1)
311                                 _l($data,  "prev", $url.'&page='.($a->pager['page'] - 1), t('prev'));
312
313                         _l($data, "first", $url."&page=1",  t('first'));
314
315
316                         $numpages = $a->pager['total'] / $a->pager['itemspage'];
317
318                         $numstart = 1;
319                         $numstop = $numpages;
320
321                         if($numpages > 14) {
322                                 $numstart = (($pagenum > 7) ? ($pagenum - 7) : 1);
323                                 $numstop = (($pagenum > ($numpages - 7)) ? $numpages : ($numstart + 14));
324                         }
325
326                         $pages = array();
327
328                         for($i = $numstart; $i <= $numstop; $i++){
329                                 if($i == $a->pager['page'])
330                                         _l($pages, $i, "#",  $i, "current");
331                                 else
332                                         _l($pages, $i, $url."&page=$i", $i, "n");
333                         }
334
335                         if(($a->pager['total'] % $a->pager['itemspage']) != 0) {
336                                 if($i == $a->pager['page'])
337                                         _l($pages, $i, "#",  $i, "current");
338                                 else
339                                         _l($pages, $i, $url."&page=$i", $i, "n");
340                         }
341
342                         $data['pages'] = $pages;
343
344                         $lastpage = (($numpages > intval($numpages)) ? intval($numpages)+1 : $numpages);
345                         _l($data, "last", $url."&page=$lastpage", t('last'));
346
347                         if(($a->pager['total'] - ($a->pager['itemspage'] * $a->pager['page'])) > 0)
348                                 _l($data, "next", $url."&page=".($a->pager['page'] + 1), t('next'));
349
350                 }
351         }
352         return $data;
353
354 }}
355
356 if(! function_exists('paginate')) {
357 /**
358  * Automatic pagination.
359  *
360  *  To use, get the count of total items.
361  * Then call $a->set_pager_total($number_items);
362  * Optionally call $a->set_pager_itemspage($n) to the number of items to display on each page
363  * Then call paginate($a) after the end of the display loop to insert the pager block on the page
364  * (assuming there are enough items to paginate).
365  * When using with SQL, the setting LIMIT %d, %d => $a->pager['start'],$a->pager['itemspage']
366  * will limit the results to the correct items for the current page.
367  * The actual page handling is then accomplished at the application layer.
368  *
369  * @param App $a App instance
370  * @return string html for pagination #FIXME remove html
371  */
372 function paginate(&$a) {
373
374         $data = paginate_data($a);
375         $tpl = get_markup_template("paginate.tpl");
376         return replace_macros($tpl, array("pager" => $data));
377
378 }}
379
380 if(! function_exists('alt_pager')) {
381 /**
382  * Alternative pager
383  * @param App $a App instance
384  * @param int $i
385  * @return string html for pagination #FIXME remove html
386  */
387 function alt_pager(&$a, $i) {
388
389         $data = paginate_data($a, $i);
390         $tpl = get_markup_template("paginate.tpl");
391         return replace_macros($tpl, array('pager' => $data));
392
393 }}
394
395 if(! function_exists('scroll_loader')) {
396 /**
397  * Loader for infinite scrolling
398  * @return string html for loader
399  */
400 function scroll_loader() {
401         $tpl = get_markup_template("scroll_loader.tpl");
402         return replace_macros($tpl, array(
403                 'wait' => t('Loading more entries...'),
404                 'end' => t('The end')
405         ));
406 }}
407
408 if(! function_exists('expand_acl')) {
409 /**
410  * Turn user/group ACLs stored as angle bracketed text into arrays
411  *
412  * @param string $s
413  * @return array
414  */
415 function expand_acl($s) {
416         // turn string array of angle-bracketed elements into numeric array
417         // e.g. "<1><2><3>" => array(1,2,3);
418         $ret = array();
419
420         if(strlen($s)) {
421                 $t = str_replace('<','',$s);
422                 $a = explode('>',$t);
423                 foreach($a as $aa) {
424                         if(intval($aa))
425                                 $ret[] = intval($aa);
426                 }
427         }
428         return $ret;
429 }}
430
431 if(! function_exists('sanitise_acl')) {
432 /**
433  * Wrap ACL elements in angle brackets for storage
434  * @param string $item
435  */
436 function sanitise_acl(&$item) {
437         if(intval($item))
438                 $item = '<' . intval(notags(trim($item))) . '>';
439         else
440                 unset($item);
441 }}
442
443
444 if(! function_exists('perms2str')) {
445 /**
446  * Convert an ACL array to a storable string
447  *
448  * Normally ACL permissions will be an array.
449  * We'll also allow a comma-separated string.
450  *
451  * @param string|array $p
452  * @return string
453  */
454 function perms2str($p) {
455         $ret = '';
456         if(is_array($p))
457                 $tmp = $p;
458         else
459                 $tmp = explode(',',$p);
460
461         if(is_array($tmp)) {
462                 array_walk($tmp,'sanitise_acl');
463                 $ret = implode('',$tmp);
464         }
465         return $ret;
466 }}
467
468
469 if(! function_exists('item_new_uri')) {
470 /**
471  * generate a guaranteed unique (for this domain) item ID for ATOM
472  * safe from birthday paradox
473  *
474  * @param string $hostname
475  * @param int $uid
476  * @return string
477  */
478 function item_new_uri($hostname,$uid, $guid = "") {
479
480         do {
481                 $dups = false;
482
483                 if ($guid == "")
484                         $hash = get_guid(32);
485                 else {
486                         $hash = $guid;
487                         $guid = "";
488                 }
489
490                 $uri = "urn:X-dfrn:" . $hostname . ':' . $uid . ':' . $hash;
491
492                 $r = q("SELECT `id` FROM `item` WHERE `uri` = '%s' LIMIT 1",
493                         dbesc($uri));
494                 if(count($r))
495                         $dups = true;
496         } while($dups == true);
497         return $uri;
498 }}
499
500 // Generate a guaranteed unique photo ID.
501 // safe from birthday paradox
502
503 if(! function_exists('photo_new_resource')) {
504 /**
505  * Generate a guaranteed unique photo ID.
506  * safe from birthday paradox
507  *
508  * @return string
509  */
510 function photo_new_resource() {
511
512         do {
513                 $found = false;
514                 $resource = hash('md5',uniqid(mt_rand(),true));
515                 $r = q("SELECT `id` FROM `photo` WHERE `resource-id` = '%s' LIMIT 1",
516                         dbesc($resource)
517                 );
518                 if(count($r))
519                         $found = true;
520         } while($found == true);
521         return $resource;
522 }}
523
524
525 if(! function_exists('load_view_file')) {
526 /**
527  * @deprecated
528  * wrapper to load a view template, checking for alternate
529  * languages before falling back to the default
530  *
531  * @global string $lang
532  * @global App $a
533  * @param string $s view name
534  * @return string
535  */
536 function load_view_file($s) {
537         global $lang, $a;
538         if(! isset($lang))
539                 $lang = 'en';
540         $b = basename($s);
541         $d = dirname($s);
542         if(file_exists("$d/$lang/$b")) {
543                 $stamp1 = microtime(true);
544                 $content = file_get_contents("$d/$lang/$b");
545                 $a->save_timestamp($stamp1, "file");
546                 return $content;
547         }
548
549         $theme = current_theme();
550
551         if(file_exists("$d/theme/$theme/$b")) {
552                 $stamp1 = microtime(true);
553                 $content = file_get_contents("$d/theme/$theme/$b");
554                 $a->save_timestamp($stamp1, "file");
555                 return $content;
556         }
557
558         $stamp1 = microtime(true);
559         $content = file_get_contents($s);
560         $a->save_timestamp($stamp1, "file");
561         return $content;
562 }}
563
564 if(! function_exists('get_intltext_template')) {
565 /**
566  * load a view template, checking for alternate
567  * languages before falling back to the default
568  *
569  * @global string $lang
570  * @param string $s view path
571  * @return string
572  */
573 function get_intltext_template($s) {
574         global $lang;
575
576         $a = get_app();
577         $engine = '';
578         if($a->theme['template_engine'] === 'smarty3')
579                 $engine = "/smarty3";
580
581         if(! isset($lang))
582                 $lang = 'en';
583
584         if(file_exists("view/lang/$lang$engine/$s")) {
585                 $stamp1 = microtime(true);
586                 $content = file_get_contents("view/lang/$lang$engine/$s");
587                 $a->save_timestamp($stamp1, "file");
588                 return $content;
589         } elseif(file_exists("view/lang/en$engine/$s")) {
590                 $stamp1 = microtime(true);
591                 $content = file_get_contents("view/lang/en$engine/$s");
592                 $a->save_timestamp($stamp1, "file");
593                 return $content;
594         } else {
595                 $stamp1 = microtime(true);
596                 $content = file_get_contents("view$engine/$s");
597                 $a->save_timestamp($stamp1, "file");
598                 return $content;
599         }
600 }}
601
602 if(! function_exists('get_markup_template')) {
603 /**
604  * load template $s
605  *
606  * @param string $s
607  * @param string $root
608  * @return string
609  */
610 function get_markup_template($s, $root = '') {
611         $stamp1 = microtime(true);
612
613         $a = get_app();
614         $t = $a->template_engine();
615         try {
616                 $template = $t->get_template_file($s, $root);
617         } catch (Exception $e) {
618                 echo "<pre><b>".__function__."</b>: ".$e->getMessage()."</pre>"; killme();
619         }
620
621         $a->save_timestamp($stamp1, "file");
622
623         return $template;
624 }}
625
626 if(! function_exists("get_template_file")) {
627 /**
628  *
629  * @param App $a
630  * @param string $filename
631  * @param string $root
632  * @return string
633  */
634 function get_template_file($a, $filename, $root = '') {
635         $theme = current_theme();
636
637         // Make sure $root ends with a slash /
638         if($root !== '' && $root[strlen($root)-1] !== '/')
639                 $root = $root . '/';
640
641         if(file_exists("{$root}view/theme/$theme/$filename"))
642                 $template_file = "{$root}view/theme/$theme/$filename";
643         elseif (x($a->theme_info,"extends") && file_exists("{$root}view/theme/{$a->theme_info["extends"]}/$filename"))
644                 $template_file = "{$root}view/theme/{$a->theme_info["extends"]}/$filename";
645         elseif (file_exists("{$root}/$filename"))
646                 $template_file = "{$root}/$filename";
647         else
648                 $template_file = "{$root}view/$filename";
649
650         return $template_file;
651 }}
652
653
654
655
656
657
658
659 if(! function_exists('attribute_contains')) {
660 /**
661  *  for html,xml parsing - let's say you've got
662  *  an attribute foobar="class1 class2 class3"
663  *  and you want to find out if it contains 'class3'.
664  *  you can't use a normal sub string search because you
665  *  might match 'notclass3' and a regex to do the job is
666  *  possible but a bit complicated.
667  *  pass the attribute string as $attr and the attribute you
668  *  are looking for as $s - returns true if found, otherwise false
669  *
670  * @param string $attr attribute value
671  * @param string $s string to search
672  * @return boolean True if found, False otherwise
673  */
674 function attribute_contains($attr,$s) {
675         $a = explode(' ', $attr);
676         if(count($a) && in_array($s,$a))
677                 return true;
678         return false;
679 }}
680
681 if (! function_exists('logger')) {
682 /* setup int->string log level map */
683 $LOGGER_LEVELS = array();
684
685 /**
686  * @brief Logs the given message at the given log level
687  *
688  * log levels:
689  * LOGGER_NORMAL (default)
690  * LOGGER_TRACE
691  * LOGGER_DEBUG
692  * LOGGER_DATA
693  * LOGGER_ALL
694  *
695  * @global App $a
696  * @global dba $db
697  * @global array $LOGGER_LEVELS
698  * @param string $msg
699  * @param int $level
700  */
701 function logger($msg, $level = 0) {
702         global $a;
703         global $db;
704         global $LOGGER_LEVELS;
705
706         // turn off logger in install mode
707         if (
708                 $a->module == 'install'
709                 || ! ($db && $db->connected)
710         ) {
711                 return;
712         }
713
714         $debugging = get_config('system','debugging');
715         $logfile   = get_config('system','logfile');
716         $loglevel = intval(get_config('system','loglevel'));
717
718         if (
719                 ! $debugging
720                 || ! $logfile
721                 || $level > $loglevel
722         ) {
723                 return;
724         }
725
726         if (count($LOGGER_LEVELS) == 0) {
727                 foreach (get_defined_constants() as $k => $v) {
728                         if (substr($k, 0, 7) == "LOGGER_") {
729                                 $LOGGER_LEVELS[$v] = substr($k, 7, 7);
730                         }
731                 }
732         }
733
734         $process_id = session_id();
735
736         if ($process_id == '') {
737                 $process_id = get_app()->process_id;
738         }
739
740         $callers = debug_backtrace();
741         $logline = sprintf("%s@%s\t[%s]:%s:%s:%s\t%s\n",
742                         datetime_convert(),
743                         $process_id,
744                         $LOGGER_LEVELS[$level],
745                         basename($callers[0]['file']),
746                         $callers[0]['line'],
747                         $callers[1]['function'],
748                         $msg
749                 );
750
751         $stamp1 = microtime(true);
752         @file_put_contents($logfile, $logline, FILE_APPEND);
753         $a->save_timestamp($stamp1, "file");
754 }}
755
756
757 if(! function_exists('activity_match')) {
758 /**
759  * Compare activity uri. Knows about activity namespace.
760  *
761  * @param string $haystack
762  * @param string $needle
763  * @return boolean
764  */
765 function activity_match($haystack,$needle) {
766         if(($haystack === $needle) || ((basename($needle) === $haystack) && strstr($needle,NAMESPACE_ACTIVITY_SCHEMA)))
767                 return true;
768         return false;
769 }}
770
771
772 /**
773  * Pull out all #hashtags and @person tags from $string;
774  * We also get @person@domain.com - which would make
775  * the regex quite complicated as tags can also
776  * end a sentence. So we'll run through our results
777  * and strip the period from any tags which end with one.
778  * Returns array of tags found, or empty array.
779  *
780  * @param string $string Post content
781  * @return array List of tag and person names
782  */
783 function get_tags($string) {
784         $ret = array();
785
786         // Convert hashtag links to hashtags
787         $string = preg_replace('/#\[url\=([^\[\]]*)\](.*?)\[\/url\]/ism', '#$2', $string);
788
789         // ignore anything in a code block
790         $string = preg_replace('/\[code\](.*?)\[\/code\]/sm', '', $string);
791
792         // Force line feeds at bbtags
793         $string = str_replace(array('[', ']'), array("\n[", "]\n"), $string);
794
795         // ignore anything in a bbtag
796         $string = preg_replace('/\[(.*?)\]/sm', '', $string);
797
798         // Match full names against @tags including the space between first and last
799         // We will look these up afterward to see if they are full names or not recognisable.
800
801         if (preg_match_all('/(@[^ \x0D\x0A,:?]+ [^ \x0D\x0A@,:?]+)([ \x0D\x0A@,:?]|$)/', $string, $matches)) {
802                 foreach ($matches[1] as $match) {
803                         if (strstr($match, ']')) {
804                                 // we might be inside a bbcode color tag - leave it alone
805                                 continue;
806                         }
807                         if (substr($match, -1, 1) === '.') {
808                                 $ret[] = substr($match, 0, -1);
809                         } else {
810                                 $ret[] = $match;
811                         }
812                 }
813         }
814
815         // Otherwise pull out single word tags. These can be @nickname, @first_last
816         // and #hash tags.
817
818         if (preg_match_all('/([!#@][^\^ \x0D\x0A,;:?]+)([ \x0D\x0A,;:?]|$)/', $string, $matches)) {
819                 foreach($matches[1] as $match) {
820                         if (strstr($match, ']')) {
821                                 // we might be inside a bbcode color tag - leave it alone
822                                 continue;
823                         }
824                         if (substr($match, -1, 1) === '.') {
825                                 $match = substr($match,0,-1);
826                         }
827                         // ignore strictly numeric tags like #1
828                         if ((strpos($match, '#') === 0) && ctype_digit(substr($match, 1))) {
829                                 continue;
830                         }
831                         // try not to catch url fragments
832                         if (strpos($string, $match) && preg_match('/[a-zA-z0-9\/]/', substr($string, strpos($string, $match) - 1, 1))) {
833                                 continue;
834                         }
835                         $ret[] = $match;
836                 }
837         }
838         return $ret;
839 }
840
841
842 //
843
844 if(! function_exists('qp')) {
845 /**
846  * quick and dirty quoted_printable encoding
847  *
848  * @param string $s
849  * @return string
850  */
851 function qp($s) {
852 return str_replace ("%","=",rawurlencode($s));
853 }}
854
855 if(! function_exists('contact_block')) {
856 /**
857  * Get html for contact block.
858  *
859  * @template contact_block.tpl
860  * @hook contact_block_end (contacts=>array, output=>string)
861  * @return string
862  */
863 function contact_block() {
864         $o = '';
865         $a = get_app();
866
867         $shown = get_pconfig($a->profile['uid'],'system','display_friend_count');
868         if($shown === false)
869                 $shown = 24;
870         if($shown == 0)
871                 return;
872
873         if((! is_array($a->profile)) || ($a->profile['hide-friends']))
874                 return $o;
875         $r = q("SELECT COUNT(*) AS `total` FROM `contact`
876                         WHERE `uid` = %d AND `self` = 0 AND `blocked` = 0 and `pending` = 0
877                                 AND `hidden` = 0 AND `archive` = 0
878                                 AND `network` IN ('%s', '%s', '%s')",
879                         intval($a->profile['uid']),
880                         dbesc(NETWORK_DFRN),
881                         dbesc(NETWORK_OSTATUS),
882                         dbesc(NETWORK_DIASPORA)
883         );
884         if(count($r)) {
885                 $total = intval($r[0]['total']);
886         }
887         if(! $total) {
888                 $contacts = t('No contacts');
889                 $micropro = Null;
890
891         } else {
892                 // Splitting the query in two parts makes it much faster
893                 $r = q("SELECT `id` FROM `contact`
894                                 WHERE `uid` = %d AND NOT `self` AND NOT `blocked` AND NOT `pending`
895                                         AND NOT `hidden` AND NOT `archive`
896                                 AND `network` IN ('%s', '%s', '%s') ORDER BY RAND() LIMIT %d",
897                                 intval($a->profile['uid']),
898                                 dbesc(NETWORK_DFRN),
899                                 dbesc(NETWORK_OSTATUS),
900                                 dbesc(NETWORK_DIASPORA),
901                                 intval($shown)
902                 );
903                 if ($r) {
904                         $contacts = "";
905                         foreach ($r AS $contact)
906                                 $contacts[] = $contact["id"];
907
908                         $r = q("SELECT `id`, `uid`, `addr`, `url`, `name`, `thumb`, `network` FROM `contact` WHERE `id` IN (%s)",
909                                 dbesc(implode(",", $contacts)));
910                         if(count($r)) {
911                                 $contacts = sprintf( tt('%d Contact','%d Contacts', $total),$total);
912                                 $micropro = Array();
913                                 foreach($r as $rr) {
914                                         $micropro[] = micropro($rr,true,'mpfriend');
915                                 }
916                         }
917                 }
918         }
919
920         $tpl = get_markup_template('contact_block.tpl');
921         $o = replace_macros($tpl, array(
922                 '$contacts' => $contacts,
923                 '$nickname' => $a->profile['nickname'],
924                 '$viewcontacts' => t('View Contacts'),
925                 '$micropro' => $micropro,
926         ));
927
928         $arr = array('contacts' => $r, 'output' => $o);
929
930         call_hooks('contact_block_end', $arr);
931         return $o;
932
933 }}
934
935 /**
936  * @brief Format contacts as picture links or as texxt links
937  *
938  * @param array $contact Array with contacts which contains an array with
939  *      int 'id' => The ID of the contact
940  *      int 'uid' => The user ID of the user who owns this data
941  *      string 'name' => The name of the contact
942  *      string 'url' => The url to the profile page of the contact
943  *      string 'addr' => The webbie of the contact (e.g.) username@friendica.com
944  *      string 'network' => The network to which the contact belongs to
945  *      string 'thumb' => The contact picture
946  *      string 'click' => js code which is performed when clicking on the contact
947  * @param boolean $redirect If true try to use the redir url if it's possible
948  * @param string $class CSS class for the
949  * @param boolean $textmode If true display the contacts as text links
950  *      if false display the contacts as picture links
951
952  * @return string Formatted html
953  */
954 function micropro($contact, $redirect = false, $class = '', $textmode = false) {
955
956         // Use the contact URL if no address is available
957         if ($contact["addr"] == "")
958                 $contact["addr"] = $contact["url"];
959
960         $url = $contact['url'];
961         $sparkle = '';
962         $redir = false;
963
964         if($redirect) {
965                 $a = get_app();
966                 $redirect_url = 'redir/' . $contact['id'];
967                 if(local_user() && ($contact['uid'] == local_user()) && ($contact['network'] === NETWORK_DFRN)) {
968                         $redir = true;
969                         $url = $redirect_url;
970                         $sparkle = ' sparkle';
971                 }
972                 else
973                         $url = zrl($url);
974         }
975
976         // If there is some js available we don't need the url
977         if(x($contact,'click'))
978                 $url = '';
979
980         return replace_macros(get_markup_template(($textmode)?'micropro_txt.tpl':'micropro_img.tpl'),array(
981                 '$click' => (($contact['click']) ? $contact['click'] : ''),
982                 '$class' => $class,
983                 '$url' => $url,
984                 '$photo' => proxy_url($contact['thumb'], false, PROXY_SIZE_THUMB),
985                 '$name' => $contact['name'],
986                 'title' => $contact['name'] . ' [' . $contact['addr'] . ']',
987                 '$parkle' => $sparkle,
988                 '$redir' => $redir,
989
990         ));
991 }
992
993
994
995 if(! function_exists('search')) {
996 /**
997  * search box
998  *
999  * @param string $s search query
1000  * @param string $id html id
1001  * @param string $url search url
1002  * @param boolean $savedsearch show save search button
1003  */
1004 function search($s,$id='search-box',$url='search',$save = false, $aside = true) {
1005         $a = get_app();
1006
1007         $values = array(
1008                         '$s' => htmlspecialchars($s),
1009                         '$id' => $id,
1010                         '$action_url' => $url,
1011                         '$search_label' => t('Search'),
1012                         '$save_label' => t('Save'),
1013                         '$savedsearch' => feature_enabled(local_user(),'savedsearch'),
1014                         '$search_hint' => t('@name, !forum, #tags, content'),
1015                 );
1016
1017         if (!$aside) {
1018                 $values['$searchoption'] = array(
1019                                         t("Full Text"),
1020                                         t("Tags"),
1021                                         t("Contacts"));
1022
1023                 if (get_config('system','poco_local_search'))
1024                         $values['$searchoption'][] = t("Forums");
1025         }
1026
1027         return replace_macros(get_markup_template('searchbox.tpl'), $values);
1028 }}
1029
1030 if(! function_exists('valid_email')) {
1031 /**
1032  * Check if $x is a valid email string
1033  *
1034  * @param string $x
1035  * @return boolean
1036  */
1037 function valid_email($x){
1038
1039         // Removed because Fabio told me so.
1040         //if(get_config('system','disable_email_validation'))
1041         //      return true;
1042
1043         if(preg_match('/^[_a-zA-Z0-9\-\+]+(\.[_a-zA-Z0-9\-\+]+)*@[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+)+$/',$x))
1044                 return true;
1045         return false;
1046 }}
1047
1048
1049 if(! function_exists('linkify')) {
1050 /**
1051  * Replace naked text hyperlink with HTML formatted hyperlink
1052  *
1053  * @param string $s
1054  */
1055 function linkify($s) {
1056         $s = preg_replace("/(https?\:\/\/[a-zA-Z0-9\:\/\-\?\&\;\.\=\_\~\#\'\%\$\!\+]*)/", ' <a href="$1" target="_blank">$1</a>', $s);
1057         $s = preg_replace("/\<(.*?)(src|href)=(.*?)\&amp\;(.*?)\>/ism",'<$1$2=$3&$4>',$s);
1058         return($s);
1059 }}
1060
1061
1062 /**
1063  * Load poke verbs
1064  *
1065  * @return array index is present tense verb
1066                                  value is array containing past tense verb, translation of present, translation of past
1067  * @hook poke_verbs pokes array
1068  */
1069 function get_poke_verbs() {
1070
1071         // index is present tense verb
1072         // value is array containing past tense verb, translation of present, translation of past
1073
1074         $arr = array(
1075                 'poke' => array( 'poked', t('poke'), t('poked')),
1076                 'ping' => array( 'pinged', t('ping'), t('pinged')),
1077                 'prod' => array( 'prodded', t('prod'), t('prodded')),
1078                 'slap' => array( 'slapped', t('slap'), t('slapped')),
1079                 'finger' => array( 'fingered', t('finger'), t('fingered')),
1080                 'rebuff' => array( 'rebuffed', t('rebuff'), t('rebuffed')),
1081         );
1082         call_hooks('poke_verbs', $arr);
1083         return $arr;
1084 }
1085
1086 /**
1087  * Load moods
1088  * @return array index is mood, value is translated mood
1089  * @hook mood_verbs moods array
1090  */
1091 function get_mood_verbs() {
1092
1093         $arr = array(
1094                 'happy'      => t('happy'),
1095                 'sad'        => t('sad'),
1096                 'mellow'     => t('mellow'),
1097                 'tired'      => t('tired'),
1098                 'perky'      => t('perky'),
1099                 'angry'      => t('angry'),
1100                 'stupefied'  => t('stupified'),
1101                 'puzzled'    => t('puzzled'),
1102                 'interested' => t('interested'),
1103                 'bitter'     => t('bitter'),
1104                 'cheerful'   => t('cheerful'),
1105                 'alive'      => t('alive'),
1106                 'annoyed'    => t('annoyed'),
1107                 'anxious'    => t('anxious'),
1108                 'cranky'     => t('cranky'),
1109                 'disturbed'  => t('disturbed'),
1110                 'frustrated' => t('frustrated'),
1111                 'motivated'  => t('motivated'),
1112                 'relaxed'    => t('relaxed'),
1113                 'surprised'  => t('surprised'),
1114         );
1115
1116         call_hooks('mood_verbs', $arr);
1117         return $arr;
1118 }
1119
1120 if(! function_exists('day_translate')) {
1121 /**
1122  * Translate days and months names
1123  *
1124  * @param string $s
1125  * @return string
1126  */
1127 function day_translate($s) {
1128         $ret = str_replace(array('Monday','Tuesday','Wednesday','Thursday','Friday','Saturday','Sunday'),
1129                 array( t('Monday'), t('Tuesday'), t('Wednesday'), t('Thursday'), t('Friday'), t('Saturday'), t('Sunday')),
1130                 $s);
1131
1132         $ret = str_replace(array('January','February','March','April','May','June','July','August','September','October','November','December'),
1133                 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')),
1134                 $ret);
1135
1136         return $ret;
1137 }}
1138
1139
1140 if(! function_exists('normalise_link')) {
1141 /**
1142  * Normalize url
1143  *
1144  * @param string $url
1145  * @return string
1146  */
1147 function normalise_link($url) {
1148         $ret = str_replace(array('https:','//www.'), array('http:','//'), $url);
1149         return(rtrim($ret,'/'));
1150 }}
1151
1152
1153
1154 if(! function_exists('link_compare')) {
1155 /**
1156  * Compare two URLs to see if they are the same, but ignore
1157  * slight but hopefully insignificant differences such as if one
1158  * is https and the other isn't, or if one is www.something and
1159  * the other isn't - and also ignore case differences.
1160  *
1161  * @param string $a first url
1162  * @param string $b second url
1163  * @return boolean True if the URLs match, otherwise False
1164  *
1165  */
1166 function link_compare($a,$b) {
1167         if(strcasecmp(normalise_link($a),normalise_link($b)) === 0)
1168                 return true;
1169         return false;
1170 }}
1171
1172
1173 if(! function_exists('redir_private_images')) {
1174 /**
1175  * Find any non-embedded images in private items and add redir links to them
1176  *
1177  * @param App $a
1178  * @param array $item
1179  */
1180 function redir_private_images($a, &$item) {
1181
1182         $matches = false;
1183         $cnt = preg_match_all('|\[img\](http[^\[]*?/photo/[a-fA-F0-9]+?(-[0-9]\.[\w]+?)?)\[\/img\]|', $item['body'], $matches, PREG_SET_ORDER);
1184         if($cnt) {
1185                 //logger("redir_private_images: matches = " . print_r($matches, true));
1186                 foreach($matches as $mtch) {
1187                         if(strpos($mtch[1], '/redir') !== false)
1188                                 continue;
1189
1190                         if((local_user() == $item['uid']) && ($item['private'] != 0) && ($item['contact-id'] != $a->contact['id']) && ($item['network'] == NETWORK_DFRN)) {
1191                                 //logger("redir_private_images: redir");
1192                                 $img_url = 'redir?f=1&quiet=1&url=' . $mtch[1] . '&conurl=' . $item['author-link'];
1193                                 $item['body'] = str_replace($mtch[0], "[img]".$img_url."[/img]", $item['body']);
1194                         }
1195                 }
1196         }
1197
1198 }}
1199
1200 function put_item_in_cache(&$item, $update = false) {
1201
1202         if (($item["rendered-hash"] != hash("md5", $item["body"])) OR ($item["rendered-hash"] == "") OR
1203                 ($item["rendered-html"] == "") OR get_config("system", "ignore_cache")) {
1204
1205                 // The function "redir_private_images" changes the body.
1206                 // I'm not sure if we should store it permanently, so we save the old value.
1207                 $body = $item["body"];
1208
1209                 $a = get_app();
1210                 redir_private_images($a, $item);
1211
1212                 $item["rendered-html"] = prepare_text($item["body"]);
1213                 $item["rendered-hash"] = hash("md5", $item["body"]);
1214                 $item["body"] = $body;
1215
1216                 if ($update AND ($item["id"] != 0)) {
1217                         q("UPDATE `item` SET `rendered-html` = '%s', `rendered-hash` = '%s' WHERE `id` = %d",
1218                                 dbesc($item["rendered-html"]), dbesc($item["rendered-hash"]), intval($item["id"]));
1219                 }
1220         }
1221 }
1222
1223 // Given an item array, convert the body element from bbcode to html and add smilie icons.
1224 // If attach is true, also add icons for item attachments
1225
1226 if(! function_exists('prepare_body')) {
1227 /**
1228  * Given an item array, convert the body element from bbcode to html and add smilie icons.
1229  * If attach is true, also add icons for item attachments
1230  *
1231  * @param array $item
1232  * @param boolean $attach
1233  * @return string item body html
1234  * @hook prepare_body_init item array before any work
1235  * @hook prepare_body ('item'=>item array, 'html'=>body string) after first bbcode to html
1236  * @hook prepare_body_final ('item'=>item array, 'html'=>body string) after attach icons and blockquote special case handling (spoiler, author)
1237  */
1238 function prepare_body(&$item,$attach = false, $preview = false) {
1239
1240         $a = get_app();
1241         call_hooks('prepare_body_init', $item);
1242
1243         $searchpath = z_root()."/search?tag=";
1244
1245         $tags=array();
1246         $hashtags = array();
1247         $mentions = array();
1248
1249         if (!get_config('system','suppress_tags')) {
1250                 $taglist = q("SELECT `type`, `term`, `url` FROM `term` WHERE `otype` = %d AND `oid` = %d AND `type` IN (%d, %d) ORDER BY `tid`",
1251                                 intval(TERM_OBJ_POST), intval($item['id']), intval(TERM_HASHTAG), intval(TERM_MENTION));
1252
1253                 foreach($taglist as $tag) {
1254
1255                         if ($tag["url"] == "")
1256                                 $tag["url"] = $searchpath.strtolower($tag["term"]);
1257
1258                         if ($tag["type"] == TERM_HASHTAG) {
1259                                 $hashtags[] = "#<a href=\"".$tag["url"]."\" target=\"_blank\">".$tag["term"]."</a>";
1260                                 $prefix = "#";
1261                         } elseif ($tag["type"] == TERM_MENTION) {
1262                                 $mentions[] = "@<a href=\"".$tag["url"]."\" target=\"_blank\">".$tag["term"]."</a>";
1263                                 $prefix = "@";
1264                         }
1265                         $tags[] = $prefix."<a href=\"".$tag["url"]."\" target=\"_blank\">".$tag["term"]."</a>";
1266                 }
1267         }
1268
1269         $item['tags'] = $tags;
1270         $item['hashtags'] = $hashtags;
1271         $item['mentions'] = $mentions;
1272
1273         // Update the cached values if there is no "zrl=..." on the links
1274         $update = (!local_user() and !remote_user() and ($item["uid"] == 0));
1275
1276         // Or update it if the current viewer is the intented viewer
1277         if (($item["uid"] == local_user()) AND ($item["uid"] != 0))
1278                 $update = true;
1279
1280         put_item_in_cache($item, $update);
1281         $s = $item["rendered-html"];
1282
1283         $prep_arr = array('item' => $item, 'html' => $s, 'preview' => $preview);
1284         call_hooks('prepare_body', $prep_arr);
1285         $s = $prep_arr['html'];
1286
1287         if(! $attach) {
1288                 // Replace the blockquotes with quotes that are used in mails
1289                 $mailquote = '<blockquote type="cite" class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex;">';
1290                 $s = str_replace(array('<blockquote>', '<blockquote class="spoiler">', '<blockquote class="author">'), array($mailquote, $mailquote, $mailquote), $s);
1291                 return $s;
1292         }
1293
1294         $as = '';
1295         $vhead = false;
1296         $arr = explode('[/attach],',$item['attach']);
1297         if(count($arr)) {
1298                 $as .= '<div class="body-attach">';
1299                 foreach($arr as $r) {
1300                         $matches = false;
1301                         $icon = '';
1302                         $cnt = preg_match_all('|\[attach\]href=\"(.*?)\" length=\"(.*?)\" type=\"(.*?)\" title=\"(.*?)\"|',$r,$matches, PREG_SET_ORDER);
1303                         if($cnt) {
1304                                 foreach($matches as $mtch) {
1305                                         $mime = $mtch[3];
1306
1307                                         if((local_user() == $item['uid']) && ($item['contact-id'] != $a->contact['id']) && ($item['network'] == NETWORK_DFRN))
1308                                                 $the_url = 'redir/' . $item['contact-id'] . '?f=1&url=' . $mtch[1];
1309                                         else
1310                                                 $the_url = $mtch[1];
1311
1312                                         if(strpos($mime, 'video') !== false) {
1313                                                 if(!$vhead) {
1314                                                         $vhead = true;
1315                                                         $a->page['htmlhead'] .= replace_macros(get_markup_template('videos_head.tpl'), array(
1316                                                                 '$baseurl' => z_root(),
1317                                                         ));
1318                                                         $a->page['end'] .= replace_macros(get_markup_template('videos_end.tpl'), array(
1319                                                                 '$baseurl' => z_root(),
1320                                                         ));
1321                                                 }
1322
1323                                                 $id = end(explode('/', $the_url));
1324                                                 $as .= replace_macros(get_markup_template('video_top.tpl'), array(
1325                                                         '$video'        => array(
1326                                                                 'id'       => $id,
1327                                                                 'title'         => t('View Video'),
1328                                                                 'src'           => $the_url,
1329                                                                 'mime'          => $mime,
1330                                                         ),
1331                                                 ));
1332                                         }
1333
1334                                         $filetype = strtolower(substr( $mime, 0, strpos($mime,'/') ));
1335                                         if($filetype) {
1336                                                 $filesubtype = strtolower(substr( $mime, strpos($mime,'/') + 1 ));
1337                                                 $filesubtype = str_replace('.', '-', $filesubtype);
1338                                         }
1339                                         else {
1340                                                 $filetype = 'unkn';
1341                                                 $filesubtype = 'unkn';
1342                                         }
1343
1344                                         $icon = '<div class="attachtype icon s22 type-' . $filetype . ' subtype-' . $filesubtype . '"></div>';
1345                                         /*$icontype = strtolower(substr($mtch[3],0,strpos($mtch[3],'/')));
1346                                         switch($icontype) {
1347                                                 case 'video':
1348                                                 case 'audio':
1349                                                 case 'image':
1350                                                 case 'text':
1351                                                         $icon = '<div class="attachtype icon s22 type-' . $icontype . '"></div>';
1352                                                         break;
1353                                                 default:
1354                                                         $icon = '<div class="attachtype icon s22 type-unkn"></div>';
1355                                                         break;
1356                                         }*/
1357
1358                                         $title = ((strlen(trim($mtch[4]))) ? escape_tags(trim($mtch[4])) : escape_tags($mtch[1]));
1359                                         $title .= ' ' . $mtch[2] . ' ' . t('bytes');
1360
1361                                         $as .= '<a href="' . strip_tags($the_url) . '" title="' . $title . '" class="attachlink" target="_blank" >' . $icon . '</a>';
1362                                 }
1363                         }
1364                 }
1365                 $as .= '<div class="clear"></div></div>';
1366         }
1367         $s = $s . $as;
1368
1369         // map
1370         if(strpos($s,'<div class="map">') !== false && $item['coord']) {
1371                 $x = generate_map(trim($item['coord']));
1372                 if($x) {
1373                         $s = preg_replace('/\<div class\=\"map\"\>/','$0' . $x,$s);
1374                 }
1375         }
1376
1377
1378         // Look for spoiler
1379         $spoilersearch = '<blockquote class="spoiler">';
1380
1381         // Remove line breaks before the spoiler
1382         while ((strpos($s, "\n".$spoilersearch) !== false))
1383                 $s = str_replace("\n".$spoilersearch, $spoilersearch, $s);
1384         while ((strpos($s, "<br />".$spoilersearch) !== false))
1385                 $s = str_replace("<br />".$spoilersearch, $spoilersearch, $s);
1386
1387         while ((strpos($s, $spoilersearch) !== false)) {
1388
1389                 $pos = strpos($s, $spoilersearch);
1390                 $rnd = random_string(8);
1391                 $spoilerreplace = '<br /> <span id="spoiler-wrap-'.$rnd.'" class="spoiler-wrap fakelink" onclick="openClose(\'spoiler-'.$rnd.'\');">'.sprintf(t('Click to open/close')).'</span>'.
1392                                         '<blockquote class="spoiler" id="spoiler-'.$rnd.'" style="display: none;">';
1393                 $s = substr($s, 0, $pos).$spoilerreplace.substr($s, $pos+strlen($spoilersearch));
1394         }
1395
1396         // Look for quote with author
1397         $authorsearch = '<blockquote class="author">';
1398
1399         while ((strpos($s, $authorsearch) !== false)) {
1400
1401                 $pos = strpos($s, $authorsearch);
1402                 $rnd = random_string(8);
1403                 $authorreplace = '<br /> <span id="author-wrap-'.$rnd.'" class="author-wrap fakelink" onclick="openClose(\'author-'.$rnd.'\');">'.sprintf(t('Click to open/close')).'</span>'.
1404                                         '<blockquote class="author" id="author-'.$rnd.'" style="display: block;">';
1405                 $s = substr($s, 0, $pos).$authorreplace.substr($s, $pos+strlen($authorsearch));
1406         }
1407
1408         // replace friendica image url size with theme preference
1409         if (x($a->theme_info,'item_image_size')){
1410             $ps = $a->theme_info['item_image_size'];
1411
1412             $s = preg_replace('|(<img[^>]+src="[^"]+/photo/[0-9a-f]+)-[0-9]|',"$1-".$ps, $s);
1413         }
1414
1415         $prep_arr = array('item' => $item, 'html' => $s);
1416         call_hooks('prepare_body_final', $prep_arr);
1417
1418         return $prep_arr['html'];
1419 }}
1420
1421
1422 if(! function_exists('prepare_text')) {
1423 /**
1424  * Given a text string, convert from bbcode to html and add smilie icons.
1425  *
1426  * @param string $text
1427  * @return string
1428  */
1429 function prepare_text($text) {
1430
1431         require_once('include/bbcode.php');
1432
1433         if(stristr($text,'[nosmile]'))
1434                 $s = bbcode($text);
1435         else
1436                 $s = Smilies::replace(bbcode($text));
1437
1438         return trim($s);
1439 }}
1440
1441
1442
1443 /**
1444  * return array with details for categories and folders for an item
1445  *
1446  * @param array $item
1447  * @return array
1448  *
1449   * [
1450  *      [ // categories array
1451  *          {
1452  *               'name': 'category name',
1453  *               'removeurl': 'url to remove this category',
1454  *               'first': 'is the first in this array? true/false',
1455  *               'last': 'is the last in this array? true/false',
1456  *           } ,
1457  *           ....
1458  *       ],
1459  *       [ //folders array
1460  *                      {
1461  *               'name': 'folder name',
1462  *               'removeurl': 'url to remove this folder',
1463  *               'first': 'is the first in this array? true/false',
1464  *               'last': 'is the last in this array? true/false',
1465  *           } ,
1466  *           ....
1467  *       ]
1468  *  ]
1469  */
1470 function get_cats_and_terms($item) {
1471
1472         $a = get_app();
1473         $categories = array();
1474         $folders = array();
1475
1476         $matches = false; $first = true;
1477         $cnt = preg_match_all('/<(.*?)>/',$item['file'],$matches,PREG_SET_ORDER);
1478         if($cnt) {
1479                 foreach($matches as $mtch) {
1480                         $categories[] = array(
1481                                 'name' => xmlify(file_tag_decode($mtch[1])),
1482                                 'url' =>  "#",
1483                                 'removeurl' => ((local_user() == $item['uid'])?'filerm/' . $item['id'] . '?f=&cat=' . xmlify(file_tag_decode($mtch[1])):""),
1484                                 'first' => $first,
1485                                 'last' => false
1486                         );
1487                         $first = false;
1488                 }
1489         }
1490         if (count($categories)) $categories[count($categories)-1]['last'] = true;
1491
1492
1493         if(local_user() == $item['uid']) {
1494                 $matches = false; $first = true;
1495                 $cnt = preg_match_all('/\[(.*?)\]/',$item['file'],$matches,PREG_SET_ORDER);
1496                 if($cnt) {
1497                         foreach($matches as $mtch) {
1498                                 $folders[] = array(
1499                                         'name' => xmlify(file_tag_decode($mtch[1])),
1500                                         'url' =>  "#",
1501                                         'removeurl' => ((local_user() == $item['uid'])?'filerm/' . $item['id'] . '?f=&term=' . xmlify(file_tag_decode($mtch[1])):""),
1502                                         'first' => $first,
1503                                         'last' => false
1504                                 );
1505                                 $first = false;
1506                         }
1507                 }
1508         }
1509
1510         if (count($folders)) $folders[count($folders)-1]['last'] = true;
1511
1512         return array($categories, $folders);
1513 }
1514
1515 if(! function_exists('get_plink')) {
1516 /**
1517  * get private link for item
1518  * @param array $item
1519  * @return boolean|array False if item has not plink, otherwise array('href'=>plink url, 'title'=>translated title)
1520  */
1521 function get_plink($item) {
1522         $a = get_app();
1523
1524         if ($a->user['nickname'] != "") {
1525                 $ret = array(
1526                                 //'href' => "display/".$a->user['nickname']."/".$item['id'],
1527                                 'href' => "display/".$item['guid'],
1528                                 'orig' => "display/".$item['guid'],
1529                                 'title' => t('View on separate page'),
1530                                 'orig_title' => t('view on separate page'),
1531                         );
1532
1533                 if (x($item,'plink')) {
1534                         $ret["href"] = $a->remove_baseurl($item['plink']);
1535                         $ret["title"] = t('link to source');
1536                 }
1537
1538         } elseif (x($item,'plink') && ($item['private'] != 1))
1539                 $ret = array(
1540                                 'href' => $item['plink'],
1541                                 'orig' => $item['plink'],
1542                                 'title' => t('link to source'),
1543                         );
1544         else
1545                 $ret = array();
1546
1547         //if (x($item,'plink') && ($item['private'] != 1))
1548
1549         return($ret);
1550 }}
1551
1552 if(! function_exists('unamp')) {
1553 /**
1554  * replace html amp entity with amp char
1555  * @param string $s
1556  * @return string
1557  */
1558 function unamp($s) {
1559         return str_replace('&amp;', '&', $s);
1560 }}
1561
1562
1563 if(! function_exists('return_bytes')) {
1564 /**
1565  * return number of bytes in size (K, M, G)
1566  * @param string $size_str
1567  * @return number
1568  */
1569 function return_bytes ($size_str) {
1570         switch (substr ($size_str, -1)) {
1571                 case 'M': case 'm': return (int)$size_str * 1048576;
1572                 case 'K': case 'k': return (int)$size_str * 1024;
1573                 case 'G': case 'g': return (int)$size_str * 1073741824;
1574                 default: return $size_str;
1575         }
1576 }}
1577
1578 /**
1579  * @return string
1580  */
1581 function generate_user_guid() {
1582         $found = true;
1583         do {
1584                 $guid = get_guid(32);
1585                 $x = q("SELECT `uid` FROM `user` WHERE `guid` = '%s' LIMIT 1",
1586                         dbesc($guid)
1587                 );
1588                 if(! count($x))
1589                         $found = false;
1590         } while ($found == true );
1591         return $guid;
1592 }
1593
1594
1595 /**
1596  * @param string $s
1597  * @param boolean $strip_padding
1598  * @return string
1599  */
1600 function base64url_encode($s, $strip_padding = false) {
1601
1602         $s = strtr(base64_encode($s),'+/','-_');
1603
1604         if($strip_padding)
1605                 $s = str_replace('=','',$s);
1606
1607         return $s;
1608 }
1609
1610 /**
1611  * @param string $s
1612  * @return string
1613  */
1614 function base64url_decode($s) {
1615
1616         if(is_array($s)) {
1617                 logger('base64url_decode: illegal input: ' . print_r(debug_backtrace(), true));
1618                 return $s;
1619         }
1620
1621 /*
1622  *  // Placeholder for new rev of salmon which strips base64 padding.
1623  *  // PHP base64_decode handles the un-padded input without requiring this step
1624  *  // Uncomment if you find you need it.
1625  *
1626  *      $l = strlen($s);
1627  *      if(! strpos($s,'=')) {
1628  *              $m = $l % 4;
1629  *              if($m == 2)
1630  *                      $s .= '==';
1631  *              if($m == 3)
1632  *                      $s .= '=';
1633  *      }
1634  *
1635  */
1636
1637         return base64_decode(strtr($s,'-_','+/'));
1638 }
1639
1640
1641 if (!function_exists('str_getcsv')) {
1642         /**
1643          * Parse csv string
1644          *
1645          * @param string $input
1646          * @param string $delimiter
1647          * @param string $enclosure
1648          * @param string $escape
1649          * @param string $eol
1650          * @return boolean|array False on error, otherwise array[row][column]
1651          */
1652 function str_getcsv($input, $delimiter = ',', $enclosure = '"', $escape = '\\', $eol = '\n') {
1653         if (is_string($input) && !empty($input)) {
1654                 $output = array();
1655                 $tmp    = preg_split("/".$eol."/",$input);
1656                 if (is_array($tmp) && !empty($tmp)) {
1657                         while (list($line_num, $line) = each($tmp)) {
1658                                 if (preg_match("/".$escape.$enclosure."/",$line)) {
1659                                         while ($strlen = strlen($line)) {
1660                                                 $pos_delimiter       = strpos($line,$delimiter);
1661                                                 $pos_enclosure_start = strpos($line,$enclosure);
1662                                                 if (
1663                                                         is_int($pos_delimiter) && is_int($pos_enclosure_start)
1664                                                         && ($pos_enclosure_start < $pos_delimiter)
1665                                                         ) {
1666                                                         $enclosed_str = substr($line,1);
1667                                                         $pos_enclosure_end = strpos($enclosed_str,$enclosure);
1668                                                         $enclosed_str = substr($enclosed_str,0,$pos_enclosure_end);
1669                                                         $output[$line_num][] = $enclosed_str;
1670                                                         $offset = $pos_enclosure_end+3;
1671                                                 } else {
1672                                                         if (empty($pos_delimiter) && empty($pos_enclosure_start)) {
1673                                                                 $output[$line_num][] = substr($line,0);
1674                                                                 $offset = strlen($line);
1675                                                         } else {
1676                                                                 $output[$line_num][] = substr($line,0,$pos_delimiter);
1677                                                                 $offset = (
1678                                                                         !empty($pos_enclosure_start)
1679                                                                         && ($pos_enclosure_start < $pos_delimiter)
1680                                                                         )
1681                                                                         ?$pos_enclosure_start
1682                                                                         :$pos_delimiter+1;
1683                                                         }
1684                                                 }
1685                                                 $line = substr($line,$offset);
1686                                         }
1687                                 } else {
1688                                         $line = preg_split("/".$delimiter."/",$line);
1689
1690                                         /*
1691                                          * Validating against pesky extra line breaks creating false rows.
1692                                          */
1693                                         if (is_array($line) && !empty($line[0])) {
1694                                                 $output[$line_num] = $line;
1695                                 }
1696                                 }
1697                         }
1698                         return $output;
1699                 } else {
1700                 return false;
1701                 }
1702         } else {
1703                 return false;
1704         }
1705 }
1706 }
1707
1708 /**
1709  * return div element with class 'clear'
1710  * @return string
1711  * @deprecated
1712  */
1713 function cleardiv() {
1714         return '<div class="clear"></div>';
1715 }
1716
1717
1718 function bb_translate_video($s) {
1719
1720         $matches = null;
1721         $r = preg_match_all("/\[video\](.*?)\[\/video\]/ism",$s,$matches,PREG_SET_ORDER);
1722         if($r) {
1723                 foreach($matches as $mtch) {
1724                         if((stristr($mtch[1],'youtube')) || (stristr($mtch[1],'youtu.be')))
1725                                 $s = str_replace($mtch[0],'[youtube]' . $mtch[1] . '[/youtube]',$s);
1726                         elseif(stristr($mtch[1],'vimeo'))
1727                                 $s = str_replace($mtch[0],'[vimeo]' . $mtch[1] . '[/vimeo]',$s);
1728                 }
1729         }
1730         return $s;
1731 }
1732
1733 function html2bb_video($s) {
1734
1735         $s = preg_replace('#<object[^>]+>(.*?)https?://www.youtube.com/((?:v|cp)/[A-Za-z0-9\-_=]+)(.*?)</object>#ism',
1736                         '[youtube]$2[/youtube]', $s);
1737
1738         $s = preg_replace('#<iframe[^>](.*?)https?://www.youtube.com/embed/([A-Za-z0-9\-_=]+)(.*?)</iframe>#ism',
1739                         '[youtube]$2[/youtube]', $s);
1740
1741         $s = preg_replace('#<iframe[^>](.*?)https?://player.vimeo.com/video/([0-9]+)(.*?)</iframe>#ism',
1742                         '[vimeo]$2[/vimeo]', $s);
1743
1744         return $s;
1745 }
1746
1747 /**
1748  * apply xmlify() to all values of array $val, recursively
1749  * @param array $val
1750  * @return array
1751  */
1752 function array_xmlify($val){
1753         if (is_bool($val)) return $val?"true":"false";
1754         if (is_array($val)) return array_map('array_xmlify', $val);
1755         return xmlify((string) $val);
1756 }
1757
1758
1759 /**
1760  * transorm link href and img src from relative to absolute
1761  *
1762  * @param string $text
1763  * @param string $base base url
1764  * @return string
1765  */
1766 function reltoabs($text, $base) {
1767         if (empty($base))
1768             return $text;
1769
1770         $base = rtrim($base,'/');
1771
1772         $base2 = $base . "/";
1773
1774         // Replace links
1775         $pattern = "/<a([^>]*) href=\"(?!http|https|\/)([^\"]*)\"/";
1776         $replace = "<a\${1} href=\"" . $base2 . "\${2}\"";
1777         $text = preg_replace($pattern, $replace, $text);
1778
1779         $pattern = "/<a([^>]*) href=\"(?!http|https)([^\"]*)\"/";
1780         $replace = "<a\${1} href=\"" . $base . "\${2}\"";
1781         $text = preg_replace($pattern, $replace, $text);
1782
1783         // Replace images
1784         $pattern = "/<img([^>]*) src=\"(?!http|https|\/)([^\"]*)\"/";
1785         $replace = "<img\${1} src=\"" . $base2 . "\${2}\"";
1786         $text = preg_replace($pattern, $replace, $text);
1787
1788         $pattern = "/<img([^>]*) src=\"(?!http|https)([^\"]*)\"/";
1789         $replace = "<img\${1} src=\"" . $base . "\${2}\"";
1790         $text = preg_replace($pattern, $replace, $text);
1791
1792
1793         // Done
1794         return $text;
1795 }
1796
1797 /**
1798  * get translated item type
1799  *
1800  * @param array $itme
1801  * @return string
1802  */
1803 function item_post_type($item) {
1804         if(intval($item['event-id']))
1805                 return t('event');
1806         if(strlen($item['resource-id']))
1807                 return t('photo');
1808         if(strlen($item['verb']) && $item['verb'] !== ACTIVITY_POST)
1809                 return t('activity');
1810         if($item['id'] != $item['parent'])
1811                 return t('comment');
1812         return t('post');
1813 }
1814
1815 // post categories and "save to file" use the same item.file table for storage.
1816 // We will differentiate the different uses by wrapping categories in angle brackets
1817 // and save to file categories in square brackets.
1818 // To do this we need to escape these characters if they appear in our tag.
1819
1820 function file_tag_encode($s) {
1821         return str_replace(array('<','>','[',']'),array('%3c','%3e','%5b','%5d'),$s);
1822 }
1823
1824 function file_tag_decode($s) {
1825         return str_replace(array('%3c','%3e','%5b','%5d'),array('<','>','[',']'),$s);
1826 }
1827
1828 function file_tag_file_query($table,$s,$type = 'file') {
1829
1830         if($type == 'file')
1831                 $str = preg_quote( '[' . str_replace('%','%%',file_tag_encode($s)) . ']' );
1832         else
1833                 $str = preg_quote( '<' . str_replace('%','%%',file_tag_encode($s)) . '>' );
1834         return " AND " . (($table) ? dbesc($table) . '.' : '') . "file regexp '" . dbesc($str) . "' ";
1835 }
1836
1837 // ex. given music,video return <music><video> or [music][video]
1838 function file_tag_list_to_file($list,$type = 'file') {
1839         $tag_list = '';
1840         if(strlen($list)) {
1841                 $list_array = explode(",",$list);
1842                 if($type == 'file') {
1843                         $lbracket = '[';
1844                         $rbracket = ']';
1845                 }
1846                 else {
1847                         $lbracket = '<';
1848                         $rbracket = '>';
1849                 }
1850
1851                 foreach($list_array as $item) {
1852                   if(strlen($item)) {
1853                                 $tag_list .= $lbracket . file_tag_encode(trim($item))  . $rbracket;
1854                         }
1855                 }
1856         }
1857         return $tag_list;
1858 }
1859
1860 // ex. given <music><video>[friends], return music,video or friends
1861 function file_tag_file_to_list($file,$type = 'file') {
1862         $matches = false;
1863         $list = '';
1864         if($type == 'file') {
1865                 $cnt = preg_match_all('/\[(.*?)\]/',$file,$matches,PREG_SET_ORDER);
1866         }
1867         else {
1868                 $cnt = preg_match_all('/<(.*?)>/',$file,$matches,PREG_SET_ORDER);
1869         }
1870         if($cnt) {
1871                 foreach($matches as $mtch) {
1872                         if(strlen($list))
1873                                 $list .= ',';
1874                         $list .= file_tag_decode($mtch[1]);
1875                 }
1876         }
1877
1878         return $list;
1879 }
1880
1881 function file_tag_update_pconfig($uid,$file_old,$file_new,$type = 'file') {
1882         // $file_old - categories previously associated with an item
1883         // $file_new - new list of categories for an item
1884
1885         if(! intval($uid))
1886                 return false;
1887
1888         if($file_old == $file_new)
1889                 return true;
1890
1891         $saved = get_pconfig($uid,'system','filetags');
1892         if(strlen($saved)) {
1893                 if($type == 'file') {
1894                         $lbracket = '[';
1895                         $rbracket = ']';
1896                         $termtype = TERM_FILE;
1897                 }
1898                 else {
1899                         $lbracket = '<';
1900                         $rbracket = '>';
1901                         $termtype = TERM_CATEGORY;
1902                 }
1903
1904                 $filetags_updated = $saved;
1905
1906                 // check for new tags to be added as filetags in pconfig
1907                 $new_tags = array();
1908                 $check_new_tags = explode(",",file_tag_file_to_list($file_new,$type));
1909
1910                 foreach($check_new_tags as $tag) {
1911                         if(! stristr($saved,$lbracket . file_tag_encode($tag) . $rbracket))
1912                                 $new_tags[] = $tag;
1913                 }
1914
1915                 $filetags_updated .= file_tag_list_to_file(implode(",",$new_tags),$type);
1916
1917                 // check for deleted tags to be removed from filetags in pconfig
1918                 $deleted_tags = array();
1919                 $check_deleted_tags = explode(",",file_tag_file_to_list($file_old,$type));
1920
1921                 foreach($check_deleted_tags as $tag) {
1922                         if(! stristr($file_new,$lbracket . file_tag_encode($tag) . $rbracket))
1923                                 $deleted_tags[] = $tag;
1924                 }
1925
1926                 foreach($deleted_tags as $key => $tag) {
1927                         $r = q("SELECT `oid` FROM `term` WHERE `term` = '%s' AND `otype` = %d AND `type` = %d AND `uid` = %d",
1928                                 dbesc($tag),
1929                                 intval(TERM_OBJ_POST),
1930                                 intval($termtype),
1931                                 intval($uid));
1932
1933                         //$r = q("select file from item where uid = %d " . file_tag_file_query('item',$tag,$type),
1934                         //      intval($uid)
1935                         //);
1936
1937                         if(count($r)) {
1938                                 unset($deleted_tags[$key]);
1939                         }
1940                         else {
1941                                 $filetags_updated = str_replace($lbracket . file_tag_encode($tag) . $rbracket,'',$filetags_updated);
1942                         }
1943                 }
1944
1945                 if($saved != $filetags_updated) {
1946                         set_pconfig($uid,'system','filetags', $filetags_updated);
1947                 }
1948                 return true;
1949         }
1950         else
1951                 if(strlen($file_new)) {
1952                         set_pconfig($uid,'system','filetags', $file_new);
1953                 }
1954                 return true;
1955 }
1956
1957 function file_tag_save_file($uid,$item,$file) {
1958         require_once("include/files.php");
1959
1960         $result = false;
1961         if(! intval($uid))
1962                 return false;
1963         $r = q("SELECT `file` FROM `item` WHERE `id` = %d AND `uid` = %d LIMIT 1",
1964                 intval($item),
1965                 intval($uid)
1966         );
1967         if(count($r)) {
1968                 if(! stristr($r[0]['file'],'[' . file_tag_encode($file) . ']'))
1969                         q("UPDATE `item` SET `file` = '%s' WHERE `id` = %d AND `uid` = %d",
1970                                 dbesc($r[0]['file'] . '[' . file_tag_encode($file) . ']'),
1971                                 intval($item),
1972                                 intval($uid)
1973                         );
1974
1975                 create_files_from_item($item);
1976
1977                 $saved = get_pconfig($uid,'system','filetags');
1978                 if((! strlen($saved)) || (! stristr($saved,'[' . file_tag_encode($file) . ']')))
1979                         set_pconfig($uid,'system','filetags',$saved . '[' . file_tag_encode($file) . ']');
1980                 info( t('Item filed') );
1981         }
1982         return true;
1983 }
1984
1985 function file_tag_unsave_file($uid,$item,$file,$cat = false) {
1986         require_once("include/files.php");
1987
1988         $result = false;
1989         if(! intval($uid))
1990                 return false;
1991
1992         if($cat == true) {
1993                 $pattern = '<' . file_tag_encode($file) . '>' ;
1994                 $termtype = TERM_CATEGORY;
1995         } else {
1996                 $pattern = '[' . file_tag_encode($file) . ']' ;
1997                 $termtype = TERM_FILE;
1998         }
1999
2000
2001         $r = q("SELECT `file` FROM `item` WHERE `id` = %d AND `uid` = %d LIMIT 1",
2002                 intval($item),
2003                 intval($uid)
2004         );
2005         if(! count($r))
2006                 return false;
2007
2008         q("UPDATE `item` SET `file` = '%s' WHERE `id` = %d AND `uid` = %d",
2009                 dbesc(str_replace($pattern,'',$r[0]['file'])),
2010                 intval($item),
2011                 intval($uid)
2012         );
2013
2014         create_files_from_item($item);
2015
2016         $r = q("SELECT `oid` FROM `term` WHERE `term` = '%s' AND `otype` = %d AND `type` = %d AND `uid` = %d",
2017                 dbesc($file),
2018                 intval(TERM_OBJ_POST),
2019                 intval($termtype),
2020                 intval($uid));
2021
2022         //$r = q("select file from item where uid = %d and deleted = 0 " . file_tag_file_query('item',$file,(($cat) ? 'category' : 'file')),
2023         //);
2024
2025         if(! count($r)) {
2026                 $saved = get_pconfig($uid,'system','filetags');
2027                 set_pconfig($uid,'system','filetags',str_replace($pattern,'',$saved));
2028
2029         }
2030         return true;
2031 }
2032
2033 function normalise_openid($s) {
2034         return trim(str_replace(array('http://','https://'),array('',''),$s),'/');
2035 }
2036
2037
2038 function undo_post_tagging($s) {
2039         $matches = null;
2040         $cnt = preg_match_all('/([!#@])\[url=(.*?)\](.*?)\[\/url\]/ism',$s,$matches,PREG_SET_ORDER);
2041         if($cnt) {
2042                 foreach($matches as $mtch) {
2043                         $s = str_replace($mtch[0], $mtch[1] . $mtch[3],$s);
2044                 }
2045         }
2046         return $s;
2047 }
2048
2049 function fix_mce_lf($s) {
2050         $s = str_replace("\r\n","\n",$s);
2051 //      $s = str_replace("\n\n","\n",$s);
2052         return $s;
2053 }
2054
2055
2056 function protect_sprintf($s) {
2057         return(str_replace('%','%%',$s));
2058 }
2059
2060
2061 function is_a_date_arg($s) {
2062         $i = intval($s);
2063         if($i > 1900) {
2064                 $y = date('Y');
2065                 if($i <= $y+1 && strpos($s,'-') == 4) {
2066                         $m = intval(substr($s,5));
2067                         if($m > 0 && $m <= 12)
2068                                 return true;
2069                 }
2070         }
2071         return false;
2072 }
2073
2074 /**
2075  * remove intentation from a text
2076  */
2077 function deindent($text, $chr="[\t ]", $count=NULL) {
2078         $text = fix_mce_lf($text);
2079         $lines = explode("\n", $text);
2080         if (is_null($count)) {
2081                 $m = array();
2082                 $k=0; while($k<count($lines) && strlen($lines[$k])==0) $k++;
2083                 preg_match("|^".$chr."*|", $lines[$k], $m);
2084                 $count = strlen($m[0]);
2085         }
2086         for ($k=0; $k<count($lines); $k++){
2087                 $lines[$k] = preg_replace("|^".$chr."{".$count."}|", "", $lines[$k]);
2088         }
2089
2090         return implode("\n", $lines);
2091 }
2092
2093 function formatBytes($bytes, $precision = 2) {
2094          $units = array('B', 'KB', 'MB', 'GB', 'TB');
2095
2096         $bytes = max($bytes, 0);
2097         $pow = floor(($bytes ? log($bytes) : 0) / log(1024));
2098         $pow = min($pow, count($units) - 1);
2099
2100         $bytes /= pow(1024, $pow);
2101
2102         return round($bytes, $precision) . ' ' . $units[$pow];
2103 }
2104
2105 /**
2106  * @brief translate and format the networkname of a contact
2107  *
2108  * @param string $network
2109  *      Networkname of the contact (e.g. dfrn, rss and so on)
2110  * @param sting $url
2111  *      The contact url
2112  * @return string
2113  */
2114 function format_network_name($network, $url = 0) {
2115         if ($network != "") {
2116                 require_once('include/contact_selectors.php');
2117                 if ($url != "")
2118                         $network_name = '<a href="'.$url.'">'.network_to_name($network, $url)."</a>";
2119                 else
2120                         $network_name = network_to_name($network);
2121
2122                 return $network_name;
2123         }
2124
2125 }
2126
2127 /**
2128  * @brief Syntax based code highlighting for popular languages.
2129  * @param string $s Code block
2130  * @param string $lang Programming language
2131  * @return string Formated html
2132  */
2133 function text_highlight($s,$lang) {
2134         if($lang === 'js')
2135                 $lang = 'javascript';
2136
2137         if(! strpos('Text_Highlighter',get_include_path())) {
2138                 set_include_path(get_include_path() . PATH_SEPARATOR . 'library/Text_Highlighter');
2139         }
2140
2141         require_once('library/Text_Highlighter/Text/Highlighter.php');
2142         require_once('library/Text_Highlighter/Text/Highlighter/Renderer/Html.php');
2143         $options = array(
2144                 'numbers' => HL_NUMBERS_LI,
2145                 'tabsize' => 4,
2146                 );
2147
2148         $tag_added = false;
2149         $s = trim(html_entity_decode($s,ENT_COMPAT));
2150         $s = str_replace("    ","\t",$s);
2151
2152         // The highlighter library insists on an opening php tag for php code blocks. If
2153         // it isn't present, nothing is highlighted. So we're going to see if it's present.
2154         // If not, we'll add it, and then quietly remove it after we get the processed output back.
2155
2156         if($lang === 'php') {
2157                 if(strpos('<?php',$s) !== 0) {
2158                         $s = '<?php' . "\n" . $s;
2159                         $tag_added = true;
2160                 }
2161         }
2162
2163         $renderer = new Text_Highlighter_Renderer_HTML($options);
2164         $hl = Text_Highlighter::factory($lang);
2165         $hl->setRenderer($renderer);
2166         $o = $hl->highlight($s);
2167         $o = str_replace(["    ","\n"],["&nbsp;&nbsp;&nbsp;&nbsp;",''],$o);
2168
2169         if($tag_added) {
2170                 $b = substr($o,0,strpos($o,'<li>'));
2171                 $e = substr($o,strpos($o,'</li>'));
2172                 $o = $b . $e;
2173         }
2174
2175         return('<code>' . $o . '</code>');
2176 }