Friendica Communications Platform (please note that this is a clone of the repository at github, issues are handled there) https://friendi.ca
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2284 lines
63 KiB

8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
10 years ago
10 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
10 years ago
8 years ago
9 years ago
9 years ago
9 years ago
9 years ago
8 years ago
9 years ago
9 years ago
8 years ago
8 years ago
10 years ago
10 years ago
10 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
10 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
10 years ago
10 years ago
8 years ago
8 years ago
10 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
  1. <?php
  2. require_once("include/template_processor.php");
  3. require_once("include/friendica_smarty.php");
  4. require_once("include/map.php");
  5. require_once("mod/proxy.php");
  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. $stamp1 = microtime(true);
  17. $a = get_app();
  18. $t = $a->template_engine();
  19. try {
  20. $output = $t->replace_macros($s,$r);
  21. } catch (Exception $e) {
  22. echo "<pre><b>".__function__."</b>: ".$e->getMessage()."</pre>"; killme();
  23. }
  24. $a->save_timestamp($stamp1, "rendering");
  25. return $output;
  26. }}
  27. // random string, there are 86 characters max in text mode, 128 for hex
  28. // output is urlsafe
  29. define('RANDOM_STRING_HEX', 0x00 );
  30. define('RANDOM_STRING_TEXT', 0x01 );
  31. if(! function_exists('random_string')) {
  32. function random_string($size = 64,$type = RANDOM_STRING_HEX) {
  33. // generate a bit of entropy and run it through the whirlpool
  34. $s = hash('whirlpool', (string) rand() . uniqid(rand(),true) . (string) rand(),(($type == RANDOM_STRING_TEXT) ? true : false));
  35. $s = (($type == RANDOM_STRING_TEXT) ? str_replace("\n","",base64url_encode($s,true)) : $s);
  36. return(substr($s,0,$size));
  37. }}
  38. if(! function_exists('notags')) {
  39. /**
  40. * This is our primary input filter.
  41. *
  42. * The high bit hack only involved some old IE browser, forget which (IE5/Mac?)
  43. * that had an XSS attack vector due to stripping the high-bit on an 8-bit character
  44. * after cleansing, and angle chars with the high bit set could get through as markup.
  45. *
  46. * This is now disabled because it was interfering with some legitimate unicode sequences
  47. * and hopefully there aren't a lot of those browsers left.
  48. *
  49. * Use this on any text input where angle chars are not valid or permitted
  50. * They will be replaced with safer brackets. This may be filtered further
  51. * if these are not allowed either.
  52. *
  53. * @param string $string Input string
  54. * @return string Filtered string
  55. */
  56. function notags($string) {
  57. return(str_replace(array("<",">"), array('[',']'), $string));
  58. // High-bit filter no longer used
  59. // return(str_replace(array("<",">","\xBA","\xBC","\xBE"), array('[',']','','',''), $string));
  60. }}
  61. if(! function_exists('escape_tags')) {
  62. /**
  63. * use this on "body" or "content" input where angle chars shouldn't be removed,
  64. * and allow them to be safely displayed.
  65. * @param string $string
  66. * @return string
  67. */
  68. function escape_tags($string) {
  69. return(htmlspecialchars($string, ENT_COMPAT, 'UTF-8', false));
  70. }}
  71. // generate a string that's random, but usually pronounceable.
  72. // used to generate initial passwords
  73. if(! function_exists('autoname')) {
  74. /**
  75. * generate a string that's random, but usually pronounceable.
  76. * used to generate initial passwords
  77. * @param int $len
  78. * @return string
  79. */
  80. function autoname($len) {
  81. if($len <= 0)
  82. return '';
  83. $vowels = array('a','a','ai','au','e','e','e','ee','ea','i','ie','o','ou','u');
  84. if(mt_rand(0,5) == 4)
  85. $vowels[] = 'y';
  86. $cons = array(
  87. 'b','bl','br',
  88. 'c','ch','cl','cr',
  89. 'd','dr',
  90. 'f','fl','fr',
  91. 'g','gh','gl','gr',
  92. 'h',
  93. 'j',
  94. 'k','kh','kl','kr',
  95. 'l',
  96. 'm',
  97. 'n',
  98. 'p','ph','pl','pr',
  99. 'qu',
  100. 'r','rh',
  101. 's','sc','sh','sm','sp','st',
  102. 't','th','tr',
  103. 'v',
  104. 'w','wh',
  105. 'x',
  106. 'z','zh'
  107. );
  108. $midcons = array('ck','ct','gn','ld','lf','lm','lt','mb','mm', 'mn','mp',
  109. 'nd','ng','nk','nt','rn','rp','rt');
  110. $noend = array('bl', 'br', 'cl','cr','dr','fl','fr','gl','gr',
  111. 'kh', 'kl','kr','mn','pl','pr','rh','tr','qu','wh');
  112. $start = mt_rand(0,2);
  113. if($start == 0)
  114. $table = $vowels;
  115. else
  116. $table = $cons;
  117. $word = '';
  118. for ($x = 0; $x < $len; $x ++) {
  119. $r = mt_rand(0,count($table) - 1);
  120. $word .= $table[$r];
  121. if($table == $vowels)
  122. $table = array_merge($cons,$midcons);
  123. else
  124. $table = $vowels;
  125. }
  126. $word = substr($word,0,$len);
  127. foreach($noend as $noe) {
  128. if((strlen($word) > 2) && (substr($word,-2) == $noe)) {
  129. $word = substr($word,0,-1);
  130. break;
  131. }
  132. }
  133. if(substr($word,-1) == 'q')
  134. $word = substr($word,0,-1);
  135. return $word;
  136. }}
  137. // escape text ($str) for XML transport
  138. // returns escaped text.
  139. if(! function_exists('xmlify')) {
  140. /**
  141. * escape text ($str) for XML transport
  142. * @param string $str
  143. * @return string Escaped text.
  144. */
  145. function xmlify($str) {
  146. /* $buffer = '';
  147. $len = mb_strlen($str);
  148. for($x = 0; $x < $len; $x ++) {
  149. $char = mb_substr($str,$x,1);
  150. switch( $char ) {
  151. case "\r" :
  152. break;
  153. case "&" :
  154. $buffer .= '&amp;';
  155. break;
  156. case "'" :
  157. $buffer .= '&apos;';
  158. break;
  159. case "\"" :
  160. $buffer .= '&quot;';
  161. break;
  162. case '<' :
  163. $buffer .= '&lt;';
  164. break;
  165. case '>' :
  166. $buffer .= '&gt;';
  167. break;
  168. case "\n" :
  169. $buffer .= "\n";
  170. break;
  171. default :
  172. $buffer .= $char;
  173. break;
  174. }
  175. }*/
  176. /*
  177. $buffer = mb_ereg_replace("&", "&amp;", $str);
  178. $buffer = mb_ereg_replace("'", "&apos;", $buffer);
  179. $buffer = mb_ereg_replace('"', "&quot;", $buffer);
  180. $buffer = mb_ereg_replace("<", "&lt;", $buffer);
  181. $buffer = mb_ereg_replace(">", "&gt;", $buffer);
  182. */
  183. $buffer = htmlspecialchars($str, ENT_QUOTES);
  184. $buffer = trim($buffer);
  185. return($buffer);
  186. }}
  187. if(! function_exists('unxmlify')) {
  188. /**
  189. * undo an xmlify
  190. * @param string $s xml escaped text
  191. * @return string unescaped text
  192. */
  193. function unxmlify($s) {
  194. // $ret = str_replace('&amp;','&', $s);
  195. // $ret = str_replace(array('&lt;','&gt;','&quot;','&apos;'),array('<','>','"',"'"),$ret);
  196. /*$ret = mb_ereg_replace('&amp;', '&', $s);
  197. $ret = mb_ereg_replace('&apos;', "'", $ret);
  198. $ret = mb_ereg_replace('&quot;', '"', $ret);
  199. $ret = mb_ereg_replace('&lt;', "<", $ret);
  200. $ret = mb_ereg_replace('&gt;', ">", $ret);
  201. */
  202. $ret = htmlspecialchars_decode($s, ENT_QUOTES);
  203. return $ret;
  204. }}
  205. if(! function_exists('hex2bin')) {
  206. /**
  207. * convenience wrapper, reverse the operation "bin2hex"
  208. * @param string $s
  209. * @return number
  210. */
  211. function hex2bin($s) {
  212. if(! (is_string($s) && strlen($s)))
  213. return '';
  214. if(! ctype_xdigit($s)) {
  215. return($s);
  216. }
  217. return(pack("H*",$s));
  218. }}
  219. if(! function_exists('paginate_data')) {
  220. /**
  221. * Automatica pagination data.
  222. *
  223. * @param App $a App instance
  224. * @param int $count [optional] item count (used with alt pager)
  225. * @return Array data for pagination template
  226. */
  227. function paginate_data(&$a, $count=null) {
  228. $stripped = preg_replace('/([&?]page=[0-9]*)/','',$a->query_string);
  229. $stripped = str_replace('q=','',$stripped);
  230. $stripped = trim($stripped,'/');
  231. $pagenum = $a->pager['page'];
  232. if (($a->page_offset != "") AND !preg_match('/[?&].offset=/', $stripped))
  233. $stripped .= "&offset=".urlencode($a->page_offset);
  234. $url = $a->get_baseurl() . '/' . $stripped;
  235. $data = array();
  236. function _l(&$d, $name, $url, $text, $class="") {
  237. if (!strpos($url, "?")) {
  238. if ($pos = strpos($url, "&"))
  239. $url = substr($url, 0, $pos)."?".substr($url, $pos + 1);
  240. }
  241. $d[$name] = array('url'=>$url, 'text'=>$text, 'class'=>$class);
  242. }
  243. if (!is_null($count)){
  244. // alt pager
  245. if($a->pager['page']>1)
  246. _l($data, "prev", $url.'&page='.($a->pager['page'] - 1), t('newer'));
  247. if($count>0)
  248. _l($data, "next", $url.'&page='.($a->pager['page'] + 1), t('older'));
  249. } else {
  250. // full pager
  251. if($a->pager['total'] > $a->pager['itemspage']) {
  252. if($a->pager['page'] != 1)
  253. _l($data, "prev", $url.'&page='.($a->pager['page'] - 1), t('prev'));
  254. _l($data, "first", $url."&page=1", t('first'));
  255. $numpages = $a->pager['total'] / $a->pager['itemspage'];
  256. $numstart = 1;
  257. $numstop = $numpages;
  258. if($numpages > 14) {
  259. $numstart = (($pagenum > 7) ? ($pagenum - 7) : 1);
  260. $numstop = (($pagenum > ($numpages - 7)) ? $numpages : ($numstart + 14));
  261. }
  262. $pages = array();
  263. for($i = $numstart; $i <= $numstop; $i++){
  264. if($i == $a->pager['page'])
  265. _l($pages, $i, "#", $i, "current");
  266. else
  267. _l($pages, $i, $url."&page=$i", $i, "n");
  268. }
  269. if(($a->pager['total'] % $a->pager['itemspage']) != 0) {
  270. if($i == $a->pager['page'])
  271. _l($pages, $i, "#", $i, "current");
  272. else
  273. _l($pages, $i, $url."&page=$i", $i, "n");
  274. }
  275. $data['pages'] = $pages;
  276. $lastpage = (($numpages > intval($numpages)) ? intval($numpages)+1 : $numpages);
  277. _l($data, "last", $url."&page=$lastpage", t('last'));
  278. if(($a->pager['total'] - ($a->pager['itemspage'] * $a->pager['page'])) > 0)
  279. _l($data, "next", $url."&page=".($a->pager['page'] + 1), t('next'));
  280. }
  281. }
  282. return $data;
  283. }}
  284. if(! function_exists('paginate')) {
  285. /**
  286. * Automatic pagination.
  287. *
  288. * To use, get the count of total items.
  289. * Then call $a->set_pager_total($number_items);
  290. * Optionally call $a->set_pager_itemspage($n) to the number of items to display on each page
  291. * Then call paginate($a) after the end of the display loop to insert the pager block on the page
  292. * (assuming there are enough items to paginate).
  293. * When using with SQL, the setting LIMIT %d, %d => $a->pager['start'],$a->pager['itemspage']
  294. * will limit the results to the correct items for the current page.
  295. * The actual page handling is then accomplished at the application layer.
  296. *
  297. * @param App $a App instance
  298. * @return string html for pagination #FIXME remove html
  299. */
  300. function paginate(&$a) {
  301. $data = paginate_data($a);
  302. $tpl = get_markup_template("paginate.tpl");
  303. return replace_macros($tpl, array("pager" => $data));
  304. }}
  305. if(! function_exists('alt_pager')) {
  306. /**
  307. * Alternative pager
  308. * @param App $a App instance
  309. * @param int $i
  310. * @return string html for pagination #FIXME remove html
  311. */
  312. function alt_pager(&$a, $i) {
  313. $data = paginate_data($a, $i);
  314. $tpl = get_markup_template("paginate.tpl");
  315. return replace_macros($tpl, array('pager' => $data));
  316. }}
  317. if(! function_exists('scroll_loader')) {
  318. /**
  319. * Loader for infinite scrolling
  320. * @return string html for loader
  321. */
  322. function scroll_loader() {
  323. $tpl = get_markup_template("scroll_loader.tpl");
  324. return replace_macros($tpl, array(
  325. 'wait' => t('Loading more entries...'),
  326. 'end' => t('The end')
  327. ));
  328. }}
  329. if(! function_exists('expand_acl')) {
  330. /**
  331. * Turn user/group ACLs stored as angle bracketed text into arrays
  332. *
  333. * @param string $s
  334. * @return array
  335. */
  336. function expand_acl($s) {
  337. // turn string array of angle-bracketed elements into numeric array
  338. // e.g. "<1><2><3>" => array(1,2,3);
  339. $ret = array();
  340. if(strlen($s)) {
  341. $t = str_replace('<','',$s);
  342. $a = explode('>',$t);
  343. foreach($a as $aa) {
  344. if(intval($aa))
  345. $ret[] = intval($aa);
  346. }
  347. }
  348. return $ret;
  349. }}
  350. if(! function_exists('sanitise_acl')) {
  351. /**
  352. * Wrap ACL elements in angle brackets for storage
  353. * @param string $item
  354. */
  355. function sanitise_acl(&$item) {
  356. if(intval($item))
  357. $item = '<' . intval(notags(trim($item))) . '>';
  358. else
  359. unset($item);
  360. }}
  361. if(! function_exists('perms2str')) {
  362. /**
  363. * Convert an ACL array to a storable string
  364. *
  365. * Normally ACL permissions will be an array.
  366. * We'll also allow a comma-separated string.
  367. *
  368. * @param string|array $p
  369. * @return string
  370. */
  371. function perms2str($p) {
  372. $ret = '';
  373. if(is_array($p))
  374. $tmp = $p;
  375. else
  376. $tmp = explode(',',$p);
  377. if(is_array($tmp)) {
  378. array_walk($tmp,'sanitise_acl');
  379. $ret = implode('',$tmp);
  380. }
  381. return $ret;
  382. }}
  383. if(! function_exists('item_new_uri')) {
  384. /**
  385. * generate a guaranteed unique (for this domain) item ID for ATOM
  386. * safe from birthday paradox
  387. *
  388. * @param string $hostname
  389. * @param int $uid
  390. * @return string
  391. */
  392. function item_new_uri($hostname,$uid) {
  393. do {
  394. $dups = false;
  395. $hash = random_string();
  396. $uri = "urn:X-dfrn:" . $hostname . ':' . $uid . ':' . $hash;
  397. $r = q("SELECT `id` FROM `item` WHERE `uri` = '%s' LIMIT 1",
  398. dbesc($uri));
  399. if(count($r))
  400. $dups = true;
  401. } while($dups == true);
  402. return $uri;
  403. }}
  404. // Generate a guaranteed unique photo ID.
  405. // safe from birthday paradox
  406. if(! function_exists('photo_new_resource')) {
  407. /**
  408. * Generate a guaranteed unique photo ID.
  409. * safe from birthday paradox
  410. *
  411. * @return string
  412. */
  413. function photo_new_resource() {
  414. do {
  415. $found = false;
  416. $resource = hash('md5',uniqid(mt_rand(),true));
  417. $r = q("SELECT `id` FROM `photo` WHERE `resource-id` = '%s' LIMIT 1",
  418. dbesc($resource)
  419. );
  420. if(count($r))
  421. $found = true;
  422. } while($found == true);
  423. return $resource;
  424. }}
  425. if(! function_exists('load_view_file')) {
  426. /**
  427. * @deprecated
  428. * wrapper to load a view template, checking for alternate
  429. * languages before falling back to the default
  430. *
  431. * @global string $lang
  432. * @global App $a
  433. * @param string $s view name
  434. * @return string
  435. */
  436. function load_view_file($s) {
  437. global $lang, $a;
  438. if(! isset($lang))
  439. $lang = 'en';
  440. $b = basename($s);
  441. $d = dirname($s);
  442. if(file_exists("$d/$lang/$b")) {
  443. $stamp1 = microtime(true);
  444. $content = file_get_contents("$d/$lang/$b");
  445. $a->save_timestamp($stamp1, "file");
  446. return $content;
  447. }
  448. $theme = current_theme();
  449. if(file_exists("$d/theme/$theme/$b")) {
  450. $stamp1 = microtime(true);
  451. $content = file_get_contents("$d/theme/$theme/$b");
  452. $a->save_timestamp($stamp1, "file");
  453. return $content;
  454. }
  455. $stamp1 = microtime(true);
  456. $content = file_get_contents($s);
  457. $a->save_timestamp($stamp1, "file");
  458. return $content;
  459. }}
  460. if(! function_exists('get_intltext_template')) {
  461. /**
  462. * load a view template, checking for alternate
  463. * languages before falling back to the default
  464. *
  465. * @global string $lang
  466. * @param string $s view path
  467. * @return string
  468. */
  469. function get_intltext_template($s) {
  470. global $lang;
  471. $a = get_app();
  472. $engine = '';
  473. if($a->theme['template_engine'] === 'smarty3')
  474. $engine = "/smarty3";
  475. if(! isset($lang))
  476. $lang = 'en';
  477. if(file_exists("view/$lang$engine/$s")) {
  478. $stamp1 = microtime(true);
  479. $content = file_get_contents("view/$lang$engine/$s");
  480. $a->save_timestamp($stamp1, "file");
  481. return $content;
  482. } elseif(file_exists("view/en$engine/$s")) {
  483. $stamp1 = microtime(true);
  484. $content = file_get_contents("view/en$engine/$s");
  485. $a->save_timestamp($stamp1, "file");
  486. return $content;
  487. } else {
  488. $stamp1 = microtime(true);
  489. $content = file_get_contents("view$engine/$s");
  490. $a->save_timestamp($stamp1, "file");
  491. return $content;
  492. }
  493. }}
  494. if(! function_exists('get_markup_template')) {
  495. /**
  496. * load template $s
  497. *
  498. * @param string $s
  499. * @param string $root
  500. * @return string
  501. */
  502. function get_markup_template($s, $root = '') {
  503. $stamp1 = microtime(true);
  504. $a = get_app();
  505. $t = $a->template_engine();
  506. try {
  507. $template = $t->get_template_file($s, $root);
  508. } catch (Exception $e) {
  509. echo "<pre><b>".__function__."</b>: ".$e->getMessage()."</pre>"; killme();
  510. }
  511. $a->save_timestamp($stamp1, "file");
  512. return $template;
  513. }}
  514. if(! function_exists("get_template_file")) {
  515. /**
  516. *
  517. * @param App $a
  518. * @param string $filename
  519. * @param string $root
  520. * @return string
  521. */
  522. function get_template_file($a, $filename, $root = '') {
  523. $theme = current_theme();
  524. // Make sure $root ends with a slash /
  525. if($root !== '' && $root[strlen($root)-1] !== '/')
  526. $root = $root . '/';
  527. if(file_exists("{$root}view/theme/$theme/$filename"))
  528. $template_file = "{$root}view/theme/$theme/$filename";
  529. elseif (x($a->theme_info,"extends") && file_exists("{$root}view/theme/{$a->theme_info["extends"]}/$filename"))
  530. $template_file = "{$root}view/theme/{$a->theme_info["extends"]}/$filename";
  531. elseif (file_exists("{$root}/$filename"))
  532. $template_file = "{$root}/$filename";
  533. else
  534. $template_file = "{$root}view/$filename";
  535. return $template_file;
  536. }}
  537. if(! function_exists('attribute_contains')) {
  538. /**
  539. * for html,xml parsing - let's say you've got
  540. * an attribute foobar="class1 class2 class3"
  541. * and you want to find out if it contains 'class3'.
  542. * you can't use a normal sub string search because you
  543. * might match 'notclass3' and a regex to do the job is
  544. * possible but a bit complicated.
  545. * pass the attribute string as $attr and the attribute you
  546. * are looking for as $s - returns true if found, otherwise false
  547. *
  548. * @param string $attr attribute value
  549. * @param string $s string to search
  550. * @return boolean True if found, False otherwise
  551. */
  552. function attribute_contains($attr,$s) {
  553. $a = explode(' ', $attr);
  554. if(count($a) && in_array($s,$a))
  555. return true;
  556. return false;
  557. }}
  558. if(! function_exists('logger')) {
  559. /* setup int->string log level map */
  560. $LOGGER_LEVELS = array();
  561. /**
  562. * log levels:
  563. * LOGGER_NORMAL (default)
  564. * LOGGER_TRACE
  565. * LOGGER_DEBUG
  566. * LOGGER_DATA
  567. * LOGGER_ALL
  568. *
  569. * @global App $a
  570. * @global dba $db
  571. * @param string $msg
  572. * @param int $level
  573. */
  574. function logger($msg,$level = 0) {
  575. // turn off logger in install mode
  576. global $a;
  577. global $db;
  578. global $LOGGER_LEVELS;
  579. if(($a->module == 'install') || (! ($db && $db->connected))) return;
  580. if (count($LOGGER_LEVELS)==0){
  581. foreach (get_defined_constants() as $k=>$v){
  582. if (substr($k,0,7)=="LOGGER_")
  583. $LOGGER_LEVELS[$v] = substr($k,7,7);
  584. }
  585. }
  586. $debugging = get_config('system','debugging');
  587. $loglevel = intval(get_config('system','loglevel'));
  588. $logfile = get_config('system','logfile');
  589. if((! $debugging) || (! $logfile) || ($level > $loglevel))
  590. return;
  591. $callers = debug_backtrace();
  592. $logline = sprintf("%s@%s\t[%s]:%s:%s:%s\t%s\n",
  593. datetime_convert(),
  594. session_id(),
  595. $LOGGER_LEVELS[$level],
  596. basename($callers[0]['file']),
  597. $callers[0]['line'],
  598. $callers[1]['function'],
  599. $msg
  600. );
  601. $stamp1 = microtime(true);
  602. @file_put_contents($logfile, $logline, FILE_APPEND);
  603. $a->save_timestamp($stamp1, "file");
  604. return;
  605. }}
  606. if(! function_exists('activity_match')) {
  607. /**
  608. * Compare activity uri. Knows about activity namespace.
  609. *
  610. * @param string $haystack
  611. * @param string $needle
  612. * @return boolean
  613. */
  614. function activity_match($haystack,$needle) {
  615. if(($haystack === $needle) || ((basename($needle) === $haystack) && strstr($needle,NAMESPACE_ACTIVITY_SCHEMA)))
  616. return true;
  617. return false;
  618. }}
  619. if(! function_exists('get_tags')) {
  620. /**
  621. * Pull out all #hashtags and @person tags from $s;
  622. * We also get @person@domain.com - which would make
  623. * the regex quite complicated as tags can also
  624. * end a sentence. So we'll run through our results
  625. * and strip the period from any tags which end with one.
  626. * Returns array of tags found, or empty array.
  627. *
  628. * @param string $s
  629. * @return array
  630. */
  631. function get_tags($s) {
  632. $ret = array();
  633. // Convert hashtag links to hashtags
  634. $s = preg_replace("/#\[url\=([^\[\]]*)\](.*?)\[\/url\]/ism", "#$2", $s);
  635. // ignore anything in a code block
  636. $s = preg_replace('/\[code\](.*?)\[\/code\]/sm','',$s);
  637. // Force line feeds at bbtags
  638. $s = str_replace(array("[", "]"), array("\n[", "]\n"), $s);
  639. // ignore anything in a bbtag
  640. $s = preg_replace('/\[(.*?)\]/sm','',$s);
  641. // Match full names against @tags including the space between first and last
  642. // We will look these up afterward to see if they are full names or not recognisable.
  643. if(preg_match_all('/(@[^ \x0D\x0A,:?]+ [^ \x0D\x0A@,:?]+)([ \x0D\x0A@,:?]|$)/',$s,$match)) {
  644. foreach($match[1] as $mtch) {
  645. if(strstr($mtch,"]")) {
  646. // we might be inside a bbcode color tag - leave it alone
  647. continue;
  648. }
  649. if(substr($mtch,-1,1) === '.')
  650. $ret[] = substr($mtch,0,-1);
  651. else
  652. $ret[] = $mtch;
  653. }
  654. }
  655. // Otherwise pull out single word tags. These can be @nickname, @first_last
  656. // and #hash tags.
  657. if(preg_match_all('/([!#@][^ \x0D\x0A,;:?]+)([ \x0D\x0A,;:?]|$)/',$s,$match)) {
  658. foreach($match[1] as $mtch) {
  659. if(strstr($mtch,"]")) {
  660. // we might be inside a bbcode color tag - leave it alone
  661. continue;
  662. }
  663. if(substr($mtch,-1,1) === '.')
  664. $mtch = substr($mtch,0,-1);
  665. // ignore strictly numeric tags like #1
  666. if((strpos($mtch,'#') === 0) && ctype_digit(substr($mtch,1)))
  667. continue;
  668. // try not to catch url fragments
  669. if(strpos($s,$mtch) && preg_match('/[a-zA-z0-9\/]/',substr($s,strpos($s,$mtch)-1,1)))
  670. continue;
  671. $ret[] = $mtch;
  672. }
  673. }
  674. return $ret;
  675. }}
  676. //
  677. if(! function_exists('qp')) {
  678. /**
  679. * quick and dirty quoted_printable encoding
  680. *
  681. * @param string $s
  682. * @return string
  683. */
  684. function qp($s) {
  685. return str_replace ("%","=",rawurlencode($s));
  686. }}
  687. if(! function_exists('get_mentions')) {
  688. /**
  689. * @param array $item
  690. * @return string html for mentions #FIXME: remove html
  691. */
  692. function get_mentions($item) {
  693. $o = '';
  694. if(! strlen($item['tag']))
  695. return $o;
  696. $arr = explode(',',$item['tag']);
  697. foreach($arr as $x) {
  698. $matches = null;
  699. if(preg_match('/@\[url=([^\]]*)\]/',$x,$matches)) {
  700. $o .= "\t\t" . '<link rel="ostatus:attention" href="' . $matches[1] . '" />' . "\r\n";
  701. $o .= "\t\t" . '<link rel="mentioned" href="' . $matches[1] . '" />' . "\r\n";
  702. }
  703. }
  704. if (!$item['private']) {
  705. $o .= "\t\t".'<link rel="ostatus:attention" href="http://activityschema.org/collection/public"/>'."\r\n";
  706. $o .= "\t\t".'<link rel="mentioned" href="http://activityschema.org/collection/public"/>'."\r\n";
  707. }
  708. return $o;
  709. }}
  710. if(! function_exists('contact_block')) {
  711. /**
  712. * Get html for contact block.
  713. *
  714. * @template contact_block.tpl
  715. * @hook contact_block_end (contacts=>array, output=>string)
  716. * @return string
  717. */
  718. function contact_block() {
  719. $o = '';
  720. $a = get_app();
  721. $shown = get_pconfig($a->profile['uid'],'system','display_friend_count');
  722. if($shown === false)
  723. $shown = 24;
  724. if($shown == 0)
  725. return;
  726. if((! is_array($a->profile)) || ($a->profile['hide-friends']))
  727. return $o;
  728. $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",
  729. intval($a->profile['uid'])
  730. );
  731. if(count($r)) {
  732. $total = intval($r[0]['total']);
  733. }
  734. if(! $total) {
  735. $contacts = t('No contacts');
  736. $micropro = Null;
  737. } else {
  738. $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",
  739. intval($a->profile['uid']),
  740. intval($shown)
  741. );
  742. if(count($r)) {
  743. $contacts = sprintf( tt('%d Contact','%d Contacts', $total),$total);
  744. $micropro = Array();
  745. foreach($r as $rr) {
  746. $micropro[] = micropro($rr,true,'mpfriend');
  747. }
  748. }
  749. }
  750. $tpl = get_markup_template('contact_block.tpl');
  751. $o = replace_macros($tpl, array(
  752. '$contacts' => $contacts,
  753. '$nickname' => $a->profile['nickname'],
  754. '$viewcontacts' => t('View Contacts'),
  755. '$micropro' => $micropro,
  756. ));
  757. $arr = array('contacts' => $r, 'output' => $o);
  758. call_hooks('contact_block_end', $arr);
  759. return $o;
  760. }}
  761. if(! function_exists('micropro')) {
  762. /**
  763. *
  764. * @param array $contact
  765. * @param boolean $redirect
  766. * @param string $class
  767. * @param boolean $textmode
  768. * @return string #FIXME: remove html
  769. */
  770. function micropro($contact, $redirect = false, $class = '', $textmode = false) {
  771. if($class)
  772. $class = ' ' . $class;
  773. $url = $contact['url'];
  774. $sparkle = '';
  775. $redir = false;
  776. if($redirect) {
  777. $a = get_app();
  778. $redirect_url = $a->get_baseurl() . '/redir/' . $contact['id'];
  779. if(local_user() && ($contact['uid'] == local_user()) && ($contact['network'] === NETWORK_DFRN)) {
  780. $redir = true;
  781. $url = $redirect_url;
  782. $sparkle = ' sparkle';
  783. }
  784. else
  785. $url = zrl($url);
  786. }
  787. $click = ((x($contact,'click')) ? ' onclick="' . $contact['click'] . '" ' : '');
  788. if($click)
  789. $url = '';
  790. if($textmode) {
  791. return '<div class="contact-block-textdiv' . $class . '"><a class="contact-block-link' . $class . $sparkle
  792. . (($click) ? ' fakelink' : '') . '" '
  793. . (($redir) ? ' target="redir" ' : '')
  794. . (($url) ? ' href="' . $url . '"' : '') . $click
  795. . '" title="' . $contact['name'] . ' [' . $contact['url'] . ']" alt="' . $contact['name']
  796. . '" >'. $contact['name'] . '</a></div>' . "\r\n";
  797. }
  798. else {
  799. return '<div class="contact-block-div' . $class . '"><a class="contact-block-link' . $class . $sparkle
  800. . (($click) ? ' fakelink' : '') . '" '
  801. . (($redir) ? ' target="redir" ' : '')
  802. . (($url) ? ' href="' . $url . '"' : '') . $click . ' ><img class="contact-block-img' . $class . $sparkle . '" src="'
  803. . proxy_url($contact['micro']) . '" title="' . $contact['name'] . ' [' . $contact['url'] . ']" alt="' . $contact['name']
  804. . '" /></a></div>' . "\r\n";
  805. }
  806. }}
  807. if(! function_exists('search')) {
  808. /**
  809. * search box
  810. *
  811. * @param string $s search query
  812. * @param string $id html id
  813. * @param string $url search url
  814. * @param boolean $savedsearch show save search button
  815. */
  816. function search($s,$id='search-box',$url='/search',$save = false) {
  817. $a = get_app();
  818. return replace_macros(get_markup_template('searchbox.tpl'), array(
  819. '$s' => $s,
  820. '$id' => $id,
  821. '$action_url' => $a->get_baseurl((stristr($url,'network')) ? true : false) . $url,
  822. '$search_label' => t('Search'),
  823. '$save_label' => t('Save'),
  824. '$savedsearch' => feature_enabled(local_user(),'savedsearch'),
  825. ));
  826. }}
  827. if(! function_exists('valid_email')) {
  828. /**
  829. * Check if $x is a valid email string
  830. *
  831. * @param string $x
  832. * @return boolean
  833. */
  834. function valid_email($x){
  835. if(get_config('system','disable_email_validation'))
  836. return true;
  837. if(preg_match('/^[_a-zA-Z0-9\-\+]+(\.[_a-zA-Z0-9\-\+]+)*@[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+)+$/',$x))
  838. return true;
  839. return false;
  840. }}
  841. if(! function_exists('linkify')) {
  842. /**
  843. * Replace naked text hyperlink with HTML formatted hyperlink
  844. *
  845. * @param string $s
  846. */
  847. function linkify($s) {
  848. $s = preg_replace("/(https?\:\/\/[a-zA-Z0-9\:\/\-\?\&\;\.\=\_\~\#\'\%\$\!\+]*)/", ' <a href="$1" target="_blank">$1</a>', $s);
  849. $s = preg_replace("/\<(.*?)(src|href)=(.*?)\&amp\;(.*?)\>/ism",'<$1$2=$3&$4>',$s);
  850. return($s);
  851. }}
  852. /**
  853. * Load poke verbs
  854. *
  855. * @return array index is present tense verb
  856. value is array containing past tense verb, translation of present, translation of past
  857. * @hook poke_verbs pokes array
  858. */
  859. function get_poke_verbs() {
  860. // index is present tense verb
  861. // value is array containing past tense verb, translation of present, translation of past
  862. $arr = array(
  863. 'poke' => array( 'poked', t('poke'), t('poked')),
  864. 'ping' => array( 'pinged', t('ping'), t('pinged')),
  865. 'prod' => array( 'prodded', t('prod'), t('prodded')),
  866. 'slap' => array( 'slapped', t('slap'), t('slapped')),
  867. 'finger' => array( 'fingered', t('finger'), t('fingered')),
  868. 'rebuff' => array( 'rebuffed', t('rebuff'), t('rebuffed')),
  869. );
  870. call_hooks('poke_verbs', $arr);
  871. return $arr;
  872. }
  873. /**
  874. * Load moods
  875. * @return array index is mood, value is translated mood
  876. * @hook mood_verbs moods array
  877. */
  878. function get_mood_verbs() {
  879. $arr = array(
  880. 'happy' => t('happy'),
  881. 'sad' => t('sad'),
  882. 'mellow' => t('mellow'),
  883. 'tired' => t('tired'),
  884. 'perky' => t('perky'),
  885. 'angry' => t('angry'),
  886. 'stupefied' => t('stupified'),
  887. 'puzzled' => t('puzzled'),
  888. 'interested' => t('interested'),
  889. 'bitter' => t('bitter'),
  890. 'cheerful' => t('cheerful'),
  891. 'alive' => t('alive'),
  892. 'annoyed' => t('annoyed'),
  893. 'anxious' => t('anxious'),
  894. 'cranky' => t('cranky'),
  895. 'disturbed' => t('disturbed'),
  896. 'frustrated' => t('frustrated'),
  897. 'motivated' => t('motivated'),
  898. 'relaxed' => t('relaxed'),
  899. 'surprised' => t('surprised'),
  900. );
  901. call_hooks('mood_verbs', $arr);
  902. return $arr;
  903. }
  904. if(! function_exists('smilies')) {
  905. /**
  906. * Replaces text emoticons with graphical images
  907. *
  908. * It is expected that this function will be called using HTML text.
  909. * We will escape text between HTML pre and code blocks from being
  910. * processed.
  911. *
  912. * At a higher level, the bbcode [nosmile] tag can be used to prevent this
  913. * function from being executed by the prepare_text() routine when preparing
  914. * bbcode source for HTML display
  915. *
  916. * @param string $s
  917. * @param boolean $sample
  918. * @return string
  919. * @hook smilie ('texts' => smilies texts array, 'icons' => smilies html array, 'string' => $s)
  920. */
  921. function smilies($s, $sample = false) {
  922. $a = get_app();
  923. if(intval(get_config('system','no_smilies'))
  924. || (local_user() && intval(get_pconfig(local_user(),'system','no_smilies'))))
  925. return $s;
  926. $s = preg_replace_callback('/<pre>(.*?)<\/pre>/ism','smile_encode',$s);
  927. $s = preg_replace_callback('/<code>(.*?)<\/code>/ism','smile_encode',$s);
  928. $texts = array(
  929. '&lt;3',
  930. '&lt;/3',
  931. '&lt;\\3',
  932. ':-)',
  933. ';-)',
  934. ':-(',
  935. ':-P',
  936. ':-p',
  937. ':-"',
  938. ':-&quot;',
  939. ':-x',
  940. ':-X',
  941. ':-D',
  942. '8-|',
  943. '8-O',
  944. ':-O',
  945. '\\o/',
  946. 'o.O',
  947. 'O.o',
  948. 'o_O',
  949. 'O_o',
  950. ":'(",
  951. ":-!",
  952. ":-/",
  953. ":-[",
  954. "8-)",
  955. ':beer',
  956. ':homebrew',
  957. ':coffee',
  958. ':facepalm',
  959. ':like',
  960. ':dislike',
  961. '~friendica',
  962. 'red#',
  963. 'red#matrix'
  964. );
  965. $icons = array(
  966. '<img class="smiley" src="' . $a->get_baseurl() . '/images/smiley-heart.gif" alt="&lt;3" />',
  967. '<img class="smiley" src="' . $a->get_baseurl() . '/images/smiley-brokenheart.gif" alt="&lt;/3" />',
  968. '<img class="smiley" src="' . $a->get_baseurl() . '/images/smiley-brokenheart.gif" alt="&lt;\\3" />',
  969. '<img class="smiley" src="' . $a->get_baseurl() . '/images/smiley-smile.gif" alt=":-)" />',
  970. '<img class="smiley" src="' . $a->get_baseurl() . '/images/smiley-wink.gif" alt=";-)" />',
  971. '<img class="smiley" src="' . $a->get_baseurl() . '/images/smiley-frown.gif" alt=":-(" />',
  972. '<img class="smiley" src="' . $a->get_baseurl() . '/images/smiley-tongue-out.gif" alt=":-P" />',
  973. '<img class="smiley" src="' . $a->get_baseurl() . '/images/smiley-tongue-out.gif" alt=":-p" />',
  974. '<img class="smiley" src="' . $a->get_baseurl() . '/images/smiley-kiss.gif" alt=":-\"" />',
  975. '<img class="smiley" src="' . $a->get_baseurl() . '/images/smiley-kiss.gif" alt=":-\"" />',
  976. '<img class="smiley" src="' . $a->get_baseurl() . '/images/smiley-kiss.gif" alt=":-x" />',
  977. '<img class="smiley" src="' . $a->get_baseurl() . '/images/smiley-kiss.gif" alt=":-X" />',
  978. '<img class="smiley" src="' . $a->get_baseurl() . '/images/smiley-laughing.gif" alt=":-D" />',
  979. '<img class="smiley" src="' . $a->get_baseurl() . '/images/smiley-surprised.gif" alt="8-|" />',
  980. '<img class="smiley" src="' . $a->get_baseurl() . '/images/smiley-surprised.gif" alt="8-O" />',
  981. '<img class="smiley" src="' . $a->get_baseurl() . '/images/smiley-surprised.gif" alt=":-O" />',
  982. '<img class="smiley" src="' . $a->get_baseurl() . '/images/smiley-thumbsup.gif" alt="\\o/" />',
  983. '<img class="smiley" src="' . $a->get_baseurl() . '/images/smiley-Oo.gif" alt="o.O" />',
  984. '<img class="smiley" src="' . $a->get_baseurl() . '/images/smiley-Oo.gif" alt="O.o" />',
  985. '<img class="smiley" src="' . $a->get_baseurl() . '/images/smiley-Oo.gif" alt="o_O" />',
  986. '<img class="smiley" src="' . $a->get_baseurl() . '/images/smiley-Oo.gif" alt="O_o" />',
  987. '<img class="smiley" src="' . $a->get_baseurl() . '/images/smiley-cry.gif" alt=":\'(" />',
  988. '<img class="smiley" src="' . $a->get_baseurl() . '/images/smiley-foot-in-mouth.gif" alt=":-!" />',
  989. '<img class="smiley" src="' . $a->get_baseurl() . '/images/smiley-undecided.gif" alt=":-/" />',
  990. '<img class="smiley" src="' . $a->get_baseurl() . '/images/smiley-embarassed.gif" alt=":-[" />',
  991. '<img class="smiley" src="' . $a->get_baseurl() . '/images/smiley-cool.gif" alt="8-)" />',
  992. '<img class="smiley" src="' . $a->get_baseurl() . '/images/beer_mug.gif" alt=":beer" />',
  993. '<img class="smiley" src="' . $a->get_baseurl() . '/images/beer_mug.gif" alt=":homebrew" />',
  994. '<img class="smiley" src="' . $a->get_baseurl() . '/images/coffee.gif" alt=":coffee" />',
  995. '<img class="smiley" src="' . $a->get_baseurl() . '/images/smiley-facepalm.gif" alt=":facepalm" />',
  996. '<img class="smiley" src="' . $a->get_baseurl() . '/images/like.gif" alt=":like" />',
  997. '<img class="smiley" src="' . $a->get_baseurl() . '/images/dislike.gif" alt=":dislike" />',
  998. '<a href="http://friendica.com">~friendica <img class="smiley" src="' . $a->get_baseurl() . '/images/friendica-16.png" alt="~friendica" /></a>',
  999. '<a href="http://redmatrix.me/">red<img class="smiley" src="' . $a->get_baseurl() . '/images/rm-16.png" alt="red" />matrix</a>',
  1000. '<a href="http://redmatrix.me/">red<img class="smiley" src="' . $a->get_baseurl() . '/images/rm-16.png" alt="red" />matrix</a>'
  1001. );
  1002. $params = array('texts' => $texts, 'icons' => $icons, 'string' => $s);
  1003. call_hooks('smilie', $params);
  1004. if($sample) {
  1005. $s = '<div class="smiley-sample">';
  1006. for($x = 0; $x < count($params['texts']); $x ++) {
  1007. $s .= '<dl><dt>' . $params['texts'][$x] . '</dt><dd>' . $params['icons'][$x] . '</dd></dl>';
  1008. }
  1009. }
  1010. else {
  1011. $params['string'] = preg_replace_callback('/&lt;(3+)/','preg_heart',$params['string']);
  1012. $s = str_replace($params['texts'],$params['icons'],$params['string']);
  1013. }
  1014. $s = preg_replace_callback('/<pre>(.*?)<\/pre>/ism','smile_decode',$s);
  1015. $s = preg_replace_callback('/<code>(.*?)<\/code>/ism','smile_decode',$s);
  1016. return $s;
  1017. }}
  1018. function smile_encode($m) {
  1019. return(str_replace($m[1],base64url_encode($m[1]),$m[0]));
  1020. }
  1021. function smile_decode($m) {
  1022. return(str_replace($m[1],base64url_decode($m[1]),$m[0]));
  1023. }
  1024. /**
  1025. * expand <3333 to the correct number of hearts
  1026. *
  1027. * @param string $x
  1028. * @return string
  1029. */
  1030. function preg_heart($x) {
  1031. $a = get_app();
  1032. if(strlen($x[1]) == 1)
  1033. return $x[0];
  1034. $t = '';
  1035. for($cnt = 0; $cnt < strlen($x[1]); $cnt ++)
  1036. $t .= '<img class="smiley" src="' . $a->get_baseurl() . '/images/smiley-heart.gif" alt="&lt;3" />';
  1037. $r = str_replace($x[0],$t,$x[0]);
  1038. return $r;
  1039. }
  1040. if(! function_exists('day_translate')) {
  1041. /**
  1042. * Translate days and months names
  1043. *
  1044. * @param string $s
  1045. * @return string
  1046. */
  1047. function day_translate($s) {
  1048. $ret = str_replace(array('Monday','Tuesday','Wednesday','Thursday','Friday','Saturday','Sunday'),
  1049. array( t('Monday'), t('Tuesday'), t('Wednesday'), t('Thursday'), t('Friday'), t('Saturday'), t('Sunday')),
  1050. $s);
  1051. $ret = str_replace(array('January','February','March','April','May','June','July','August','September','October','November','December'),
  1052. 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')),
  1053. $ret);
  1054. return $ret;
  1055. }}
  1056. if(! function_exists('normalise_link')) {
  1057. /**
  1058. * Normalize url
  1059. *
  1060. * @param string $url
  1061. * @return string
  1062. */
  1063. function normalise_link($url) {
  1064. $ret = str_replace(array('https:','//www.'), array('http:','//'), $url);
  1065. return(rtrim($ret,'/'));
  1066. }}
  1067. if(! function_exists('link_compare')) {
  1068. /**
  1069. * Compare two URLs to see if they are the same, but ignore
  1070. * slight but hopefully insignificant differences such as if one
  1071. * is https and the other isn't, or if one is www.something and
  1072. * the other isn't - and also ignore case differences.
  1073. *
  1074. * @param string $a first url
  1075. * @param string $b second url
  1076. * @return boolean True if the URLs match, otherwise False
  1077. *
  1078. */
  1079. function link_compare($a,$b) {
  1080. if(strcasecmp(normalise_link($a),normalise_link($b)) === 0)
  1081. return true;
  1082. return false;
  1083. }}
  1084. if(! function_exists('redir_private_images')) {
  1085. /**
  1086. * Find any non-embedded images in private items and add redir links to them
  1087. *
  1088. * @param App $a
  1089. * @param array $item
  1090. */
  1091. function redir_private_images($a, &$item) {
  1092. $matches = false;
  1093. $cnt = preg_match_all('|\[img\](http[^\[]*?/photo/[a-fA-F0-9]+?(-[0-9]\.[\w]+?)?)\[\/img\]|', $item['body'], $matches, PREG_SET_ORDER);
  1094. if($cnt) {
  1095. //logger("redir_private_images: matches = " . print_r($matches, true));
  1096. foreach($matches as $mtch) {
  1097. if(strpos($mtch[1], '/redir') !== false)
  1098. continue;
  1099. if((local_user() == $item['uid']) && ($item['private'] != 0) && ($item['contact-id'] != $a->contact['id']) && ($item['network'] == NETWORK_DFRN)) {
  1100. //logger("redir_private_images: redir");
  1101. $img_url = $a->get_baseurl() . '/redir?f=1&quiet=1&url=' . $mtch[1] . '&conurl=' . $item['author-link'];
  1102. $item['body'] = str_replace($mtch[0], "[img]".$img_url."[/img]", $item['body']);
  1103. }
  1104. }
  1105. }
  1106. }}
  1107. function put_item_in_cache(&$item, $update = false) {
  1108. if (($item["rendered-hash"] != hash("md5", $item["body"])) OR ($item["rendered-hash"] == "") OR
  1109. ($item["rendered-html"] == "") OR get_config("system", "ignore_cache")) {
  1110. // The function "redir_private_images" changes the body.
  1111. // I'm not sure if we should store it permanently, so we save the old value.
  1112. $body = $item["body"];
  1113. $a = get_app();
  1114. redir_private_images($a, $item);
  1115. $item["rendered-html"] = prepare_text($item["body"]);
  1116. $item["rendered-hash"] = hash("md5", $item["body"]);
  1117. $item["body"] = $body;
  1118. if ($update AND ($item["id"] != 0)) {
  1119. q("UPDATE `item` SET `rendered-html` = '%s', `rendered-hash` = '%s' WHERE `id` = %d",
  1120. dbesc($item["rendered-html"]), dbesc($item["rendered-hash"]), intval($item["id"]));
  1121. }
  1122. }
  1123. }
  1124. // Given an item array, convert the body element from bbcode to html and add smilie icons.
  1125. // If attach is true, also add icons for item attachments
  1126. if(! function_exists('prepare_body')) {
  1127. /**
  1128. * Given an item array, convert the body element from bbcode to html and add smilie icons.
  1129. * If attach is true, also add icons for item attachments
  1130. *
  1131. * @param array $item
  1132. * @param boolean $attach
  1133. * @return string item body html
  1134. * @hook prepare_body_init item array before any work
  1135. * @hook prepare_body ('item'=>item array, 'html'=>body string) after first bbcode to html
  1136. * @hook prepare_body_final ('item'=>item array, 'html'=>body string) after attach icons and blockquote special case handling (spoiler, author)
  1137. */
  1138. function prepare_body(&$item,$attach = false, $preview = false) {
  1139. $a = get_app();
  1140. call_hooks('prepare_body_init', $item);
  1141. $searchpath = $a->get_baseurl()."/search?tag=";
  1142. $tags=array();
  1143. $hashtags = array();
  1144. $mentions = array();
  1145. if (!get_config('system','suppress_tags')) {
  1146. $taglist = q("SELECT `type`, `term`, `url` FROM `term` WHERE `otype` = %d AND `oid` = %d AND `type` IN (%d, %d) ORDER BY `tid`",
  1147. intval(TERM_OBJ_POST), intval($item['id']), intval(TERM_HASHTAG), intval(TERM_MENTION));
  1148. foreach($taglist as $tag) {
  1149. if ($tag["url"] == "")
  1150. $tag["url"] = $searchpath.strtolower($tag["term"]);
  1151. if ($tag["type"] == TERM_HASHTAG) {
  1152. $hashtags[] = "#<a href=\"".$tag["url"]."\" target=\"_blank\">".$tag["term"]."</a>";
  1153. $prefix = "#";
  1154. } elseif ($tag["type"] == TERM_MENTION) {
  1155. $mentions[] = "@<a href=\"".$tag["url"]."\" target=\"_blank\">".$tag["term"]."</a>";
  1156. $prefix = "@";
  1157. }
  1158. $tags[] = $prefix."<a href=\"".$tag["url"]."\" target=\"_blank\">".$tag["term"]."</a>";
  1159. }
  1160. }
  1161. $item['tags'] = $tags;
  1162. $item['hashtags'] = $hashtags;
  1163. $item['mentions'] = $mentions;
  1164. put_item_in_cache($item, true);
  1165. $s = $item["rendered-html"];
  1166. require_once("mod/proxy.php");
  1167. $s = proxy_parse_html($s);
  1168. $prep_arr = array('item' => $item, 'html' => $s, 'preview' => $preview);
  1169. call_hooks('prepare_body', $prep_arr);
  1170. $s = $prep_arr['html'];
  1171. if(! $attach) {
  1172. // Replace the blockquotes with quotes that are used in mails
  1173. $mailquote = '<blockquote type="cite" class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex;">';
  1174. $s = str_replace(array('<blockquote>', '<blockquote class="spoiler">', '<blockquote class="author">'), array($mailquote, $mailquote, $mailquote), $s);
  1175. return $s;
  1176. }
  1177. $as = '';
  1178. $vhead = false;
  1179. $arr = explode('[/attach],',$item['attach']);
  1180. if(count($arr)) {
  1181. $as .= '<div class="body-attach">';
  1182. foreach($arr as $r) {
  1183. $matches = false;
  1184. $icon = '';
  1185. $cnt = preg_match_all('|\[attach\]href=\"(.*?)\" length=\"(.*?)\" type=\"(.*?)\" title=\"(.*?)\"|',$r,$matches, PREG_SET_ORDER);
  1186. if($cnt) {
  1187. foreach($matches as $mtch) {
  1188. $mime = $mtch[3];
  1189. if((local_user() == $item['uid']) && ($item['contact-id'] != $a->contact['id']) && ($item['network'] == NETWORK_DFRN))
  1190. $the_url = $a->get_baseurl() . '/redir/' . $item['contact-id'] . '?f=1&url=' . $mtch[1];
  1191. else
  1192. $the_url = $mtch[1];
  1193. if(strpos($mime, 'video') !== false) {
  1194. if(!$vhead) {
  1195. $vhead = true;
  1196. $a->page['htmlhead'] .= replace_macros(get_markup_template('videos_head.tpl'), array(
  1197. '$baseurl' => $a->get_baseurl(),
  1198. ));
  1199. $a->page['end'] .= replace_macros(get_markup_template('videos_end.tpl'), array(
  1200. '$baseurl' => $a->get_baseurl(),
  1201. ));
  1202. }
  1203. $id = end(explode('/', $the_url));
  1204. $as .= replace_macros(get_markup_template('video_top.tpl'), array(
  1205. '$video' => array(
  1206. 'id' => $id,
  1207. 'title' => t('View Video'),
  1208. 'src' => $the_url,
  1209. 'mime' => $mime,
  1210. ),
  1211. ));
  1212. }
  1213. $filetype = strtolower(substr( $mime, 0, strpos($mime,'/') ));
  1214. if($filetype) {
  1215. $filesubtype = strtolower(substr( $mime, strpos($mime,'/') + 1 ));
  1216. $filesubtype = str_replace('.', '-', $filesubtype);
  1217. }
  1218. else {
  1219. $filetype = 'unkn';
  1220. $filesubtype = 'unkn';
  1221. }
  1222. $icon = '<div class="attachtype icon s22 type-' . $filetype . ' subtype-' . $filesubtype . '"></div>';
  1223. /*$icontype = strtolower(substr($mtch[3],0,strpos($mtch[3],'/')));
  1224. switch($icontype) {
  1225. case 'video':
  1226. case 'audio':
  1227. case 'image':
  1228. case 'text':
  1229. $icon = '<div class="attachtype icon s22 type-' . $icontype . '"></div>';
  1230. break;
  1231. default:
  1232. $icon = '<div class="attachtype icon s22 type-unkn"></div>';
  1233. break;
  1234. }*/
  1235. $title = ((strlen(trim($mtch[4]))) ? escape_tags(trim($mtch[4])) : escape_tags($mtch[1]));
  1236. $title .= ' ' . $mtch[2] . ' ' . t('bytes');
  1237. $as .= '<a href="' . strip_tags($the_url) . '" title="' . $title . '" class="attachlink" target="_blank" >' . $icon . '</a>';
  1238. }
  1239. }
  1240. }
  1241. $as .= '<div class="clear"></div></div>';
  1242. }
  1243. $s = $s . $as;
  1244. // map
  1245. if(strpos($s,'<div class="map">') !== false && $item['coord']) {
  1246. $x = generate_map(trim($item['coord']));
  1247. if($x) {
  1248. $s = preg_replace('/\<div class\=\"map\"\>/','$0' . $x,$s);
  1249. }
  1250. }
  1251. // Look for spoiler
  1252. $spoilersearch = '<blockquote class="spoiler">';
  1253. // Remove line breaks before the spoiler
  1254. while ((strpos($s, "\n".$spoilersearch) !== false))
  1255. $s = str_replace("\n".$spoilersearch, $spoilersearch, $s);
  1256. while ((strpos($s, "<br />".$spoilersearch) !== false))
  1257. $s = str_replace("<br />".$spoilersearch, $spoilersearch, $s);
  1258. while ((strpos($s, $spoilersearch) !== false)) {
  1259. $pos = strpos($s, $spoilersearch);
  1260. $rnd = random_string(8);
  1261. $spoilerreplace = '<br /> <span id="spoiler-wrap-'.$rnd.'" style="white-space:nowrap;" class="fakelink" onclick="openClose(\'spoiler-'.$rnd.'\');">'.sprintf(t('Click to open/close')).'</span>'.
  1262. '<blockquote class="spoiler" id="spoiler-'.$rnd.'" style="display: none;">';
  1263. $s = substr($s, 0, $pos).$spoilerreplace.substr($s, $pos+strlen($spoilersearch));
  1264. }
  1265. // Look for quote with author
  1266. $authorsearch = '<blockquote class="author">';
  1267. while ((strpos($s, $authorsearch) !== false)) {
  1268. $pos = strpos($s, $authorsearch);
  1269. $rnd = random_string(8);
  1270. $authorreplace = '<br /> <span id="author-wrap-'.$rnd.'" style="white-space:nowrap;" class="fakelink" onclick="openClose(\'author-'.$rnd.'\');">'.sprintf(t('Click to open/close')).'</span>'.