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