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