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