PageRenderTime 37ms CodeModel.GetById 1ms RepoModel.GetById 0ms app.codeStats 0ms

/includes/render.php

https://code.google.com/p/enanocms/
PHP | 1406 lines | 977 code | 201 blank | 228 comment | 109 complexity | e75a636a4319e554fc0487cb59e4697e MD5 | raw file
Possible License(s): GPL-2.0
  1. <?php
  2. /*
  3. * Enano - an open-source CMS capable of wiki functions, Drupal-like sidebar blocks, and everything in between
  4. * Copyright (C) 2006-2009 Dan Fuhry
  5. * render.php - handles fetching pages and parsing them into HTML
  6. *
  7. * This program is Free Software; you can redistribute and/or modify it under the terms of the GNU General Public License
  8. * as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
  11. * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
  12. */
  13. class RenderMan {
  14. public static function strToPageID($string)
  15. {
  16. global $db, $session, $paths, $template, $plugins; // Common objects
  17. $k = array_keys($paths->nslist);
  18. $proj_alt = 'Project:';
  19. if ( substr($string, 0, (strlen($proj_alt))) == $proj_alt )
  20. {
  21. $ns = 'Project';
  22. $pg = substr($string, strlen($proj_alt), strlen($string));
  23. return Array($pg, $ns);
  24. }
  25. for($i=0;$i<sizeof($paths->nslist);$i++)
  26. {
  27. $ln = strlen($paths->nslist[$k[$i]]);
  28. if(substr($string, 0, $ln) == $paths->nslist[$k[$i]])
  29. {
  30. $ns = $k[$i];
  31. $pg = substr($string, strlen($paths->nslist[$ns]), strlen($string));
  32. }
  33. }
  34. return Array($pg, $ns);
  35. }
  36. public static function getPage($page_id, $namespace, $wiki = 1, $smilies = true, $filter_links = true, $redir = true, $render = true)
  37. {
  38. global $db, $session, $paths, $template, $plugins; // Common objects
  39. $page = new PageProcessor($page_id, $namespace);
  40. $text = $page->fetch_text();
  41. if ( !$render )
  42. {
  43. $code = $plugins->setHook('render_getpage_norender');
  44. foreach ( $code as $cmd )
  45. {
  46. eval($cmd);
  47. }
  48. return $text;
  49. }
  50. $text = self::render($text, $wiki, $smilies, $filter_links);
  51. return $text;
  52. }
  53. public static function getTemplate($id, $parms)
  54. {
  55. global $db, $session, $paths, $template, $plugins; // Common objects
  56. // Verify target exists -- if not, double-bracket it to convert it to a redlink
  57. if ( !isPage($paths->get_pathskey($id, 'Template')) )
  58. {
  59. return '[['.$paths->nslist['Template'].$id.']]';
  60. }
  61. // fetch from DB or template cache
  62. if(isset($paths->template_cache[$id]))
  63. {
  64. $text = $paths->template_cache[$id];
  65. }
  66. else
  67. {
  68. $page = new PageProcessor($id, 'Template');
  69. $text = $page->fetch_text();
  70. $paths->template_cache[$id] = $text;
  71. }
  72. // noinclude is not shown within the included template, only on the template's page when you load it
  73. // nodisplay is not shown on the template's page, only in the included template
  74. $text = preg_replace('/<noinclude>(.*?)<\/noinclude>/is', '', $text);
  75. $text = preg_replace('/<nodisplay>(.*?)<\/nodisplay>/is', '\\1', $text);
  76. preg_match_all('#\(_([0-9]+)_\)#', $text, $matchlist);
  77. foreach($matchlist[1] as $m)
  78. {
  79. if(isset($parms[((int)$m)+1]))
  80. {
  81. $p = $parms[((int)$m)+1];
  82. }
  83. else
  84. {
  85. $p = '<b>Notice:</b> RenderMan::getTemplate(): Parameter '.$m.' is not set';
  86. }
  87. $text = str_replace('(_'.$m.'_)', $p, $text);
  88. $text = str_replace('{{' . ( $m + 1 ) . '}}', $p, $text);
  89. }
  90. $text = RenderMan::include_templates($text);
  91. return $text;
  92. }
  93. public static function fetch_template_text($id, $params)
  94. {
  95. global $db, $session, $paths, $template, $plugins; // Common objects
  96. $fetch_ns = 'Template';
  97. if ( !isPage($paths->get_pathskey($id, 'Template')) )
  98. {
  99. // Transclusion of another page
  100. // 1.1.5: Now You, Too, Can Be A Template, Even If You're Just A Plain Old Article! (TM)
  101. $nssep = substr($paths->nslist['Special'], -1);
  102. $nslist = $paths->nslist;
  103. foreach ( $nslist as &$ns )
  104. {
  105. if ( $ns == '' )
  106. $ns = $nssep;
  107. }
  108. $prefixlist = array_flip($nslist);
  109. foreach ( $nslist as &$ns )
  110. {
  111. $ns = preg_quote($ns);
  112. }
  113. $nslist = implode('|', $nslist);
  114. if ( preg_match("/^($nslist)(.*?)$/", $id, $match) )
  115. {
  116. // in practice this should always be true but just to be safe...
  117. if ( isset($prefixlist[$match[1]]) )
  118. {
  119. $new_id = $paths->nslist[ $prefixlist[$match[1]] ] . sanitize_page_id($match[2]);
  120. if ( !isPage($new_id) )
  121. {
  122. return "[[$new_id]]";
  123. }
  124. $fetch_ns = $prefixlist[$match[1]];
  125. $id = sanitize_page_id($match[2]);
  126. }
  127. }
  128. else
  129. {
  130. return '[['.$paths->nslist['Template'].$id.']]';
  131. }
  132. }
  133. $template->context_push();
  134. $template->assign_vars($params);
  135. if(isset($paths->template_cache[$id]))
  136. {
  137. $text = $paths->template_cache[$id];
  138. }
  139. else
  140. {
  141. $text = RenderMan::getPage($id, $fetch_ns, 0, false, false, false, false);
  142. $paths->template_cache[$id] = $text;
  143. }
  144. $template->context_pop();
  145. if ( is_string($text) )
  146. {
  147. $text = preg_replace('/<noinclude>(.*?)<\/noinclude>/is', '', $text);
  148. $text = preg_replace('/<nodisplay>(.*?)<\/nodisplay>/is', '\\1', $text);
  149. }
  150. return $text;
  151. }
  152. /**
  153. * Renders a glob of text. Note that this is PHP-safe, so if returned text (or rather, "?>" . $returned) has PHP it can be eval'ed.
  154. * @param string Text to render
  155. * @param int Render parameters - see constants.php
  156. * @return string Rendered text
  157. */
  158. public static function render($text, $flags = RENDER_WIKI_DEFAULT, $smilies = true)
  159. {
  160. global $db, $session, $paths, $template, $plugins; // Common objects
  161. if ( !$smilies )
  162. $flags |= RENDER_NOSMILIES;
  163. if ( !($flags & RENDER_NOSMILIES) )
  164. {
  165. $text = RenderMan::smilieyize($text);
  166. }
  167. if ( $flags & RENDER_WIKI_DEFAULT )
  168. {
  169. $text = RenderMan::next_gen_wiki_format($text, $flags);
  170. }
  171. else if ( $flags & RENDER_WIKI_TEMPLATE )
  172. {
  173. $text = $template->tplWikiFormat($text);
  174. }
  175. return $text;
  176. }
  177. private static function next_gen_wiki_format($text, $flags = 0)
  178. {
  179. global $db, $session, $paths, $template, $plugins; // Common objects
  180. global $lang;
  181. profiler_log("RenderMan: starting wikitext render");
  182. require_once( ENANO_ROOT . '/includes/wikiformat.php' );
  183. require_once( ENANO_ROOT . '/includes/wikiengine/TagSanitizer.php' );
  184. require_once( ENANO_ROOT . '/includes/wikiengine/Tables.php' );
  185. // this is still needed by parser plugins
  186. $random_id = md5( time() . mt_rand() );
  187. // Strip out <nowiki> sections
  188. self::nowiki_strip($text, $nowiki_stripped);
  189. // Run early parsing plugins
  190. $code = $plugins->setHook('render_wikiformat_veryearly');
  191. foreach ( $code as $cmd )
  192. {
  193. eval($cmd);
  194. }
  195. // Strip out embedded PHP
  196. self::php_strip($text, $php_stripped);
  197. // Convert newlines for the parser
  198. $text = str_replace("\r\n", "\n", $text);
  199. // Perform render through the engine
  200. $carpenter = new Carpenter();
  201. $carpenter->flags = $flags;
  202. $carpenter->hook(array(__CLASS__, 'hook_pre'), PO_AFTER, 'lang');
  203. $carpenter->hook(array(__CLASS__, 'hook_posttemplates'), PO_AFTER, 'templates');
  204. if ( $flags & RENDER_WIKI_TEMPLATE )
  205. {
  206. // FIXME: Where is noinclude/nodisplay being processed in the pipeline? (Seems to be processed, but not here)
  207. }
  208. //
  209. // Set rules for the rendering process
  210. //
  211. if ( $flags & RENDER_BLOCK && !($flags & RENDER_INLINE) )
  212. {
  213. // block only
  214. $carpenter->disable_all_rules();
  215. foreach ( array('blockquote', 'tables', 'heading', 'hr', 'multilist', 'bold', 'italic', 'underline', 'paragraph', 'blockquotepost') as $rule )
  216. {
  217. $carpenter->enable_rule($rule);
  218. }
  219. $code = $plugins->setHook('render_block_only');
  220. foreach ( $code as $cmd )
  221. {
  222. eval($cmd);
  223. }
  224. }
  225. else if ( $flags & RENDER_INLINE && !($flags & RENDER_BLOCK) )
  226. {
  227. // inline only
  228. $carpenter->disable_all_rules();
  229. foreach ( array('heading', 'bold', 'italic', 'underline', 'externalwithtext', 'externalnotext', 'image', 'internallink') as $rule )
  230. {
  231. $carpenter->enable_rule($rule);
  232. }
  233. $code = $plugins->setHook('render_inline_only');
  234. foreach ( $code as $cmd )
  235. {
  236. eval($cmd);
  237. }
  238. }
  239. else
  240. {
  241. // full render
  242. $code = $plugins->setHook('render_full');
  243. foreach ( $code as $cmd )
  244. {
  245. eval($cmd);
  246. }
  247. }
  248. $text = $carpenter->render($text);
  249. // For plugin compat
  250. $result =& $text;
  251. // Post processing hook
  252. $code = $plugins->setHook('render_wikiformat_post');
  253. foreach ( $code as $cmd )
  254. {
  255. eval($cmd);
  256. }
  257. // Add PHP and nowiki back in
  258. self::nowiki_unstrip($text, $nowiki_stripped);
  259. self::php_unstrip($text, $php_stripped);
  260. profiler_log("RenderMan: finished wikitext render");
  261. return $text;
  262. }
  263. public static function hook_pre($text)
  264. {
  265. global $db, $session, $paths, $template, $plugins; // Common objects
  266. $code = $plugins->setHook('render_wikiformat_pre');
  267. foreach ( $code as $cmd )
  268. {
  269. eval($cmd);
  270. }
  271. return $text;
  272. }
  273. public static function hook_posttemplates($text)
  274. {
  275. global $db, $session, $paths, $template, $plugins; // Common objects
  276. $code = $plugins->setHook('render_wikiformat_posttemplates');
  277. foreach ( $code as $cmd )
  278. {
  279. eval($cmd);
  280. }
  281. return $text;
  282. }
  283. /**
  284. * Strip out <nowiki> tags (to bypass parsing on them)
  285. * @access private
  286. */
  287. private static function nowiki_strip(&$text, &$stripdata)
  288. {
  289. self::tag_strip('nowiki', $text, $stripdata);
  290. }
  291. /**
  292. * Restore stripped <nowiki> tags.
  293. * @access private
  294. */
  295. public static function nowiki_unstrip(&$text, &$stripdata)
  296. {
  297. self::tag_unstrip('nowiki', $text, $stripdata);
  298. }
  299. /**
  300. * Strip out an arbitrary HTML tag.
  301. * @access private
  302. */
  303. public static function tag_strip($tag, &$text, &$stripdata)
  304. {
  305. $random_id = md5( time() . mt_rand() );
  306. preg_match_all("#<$tag>(.*?)</$tag>#is", $text, $blocks);
  307. foreach ( $blocks[0] as $i => $match )
  308. {
  309. $text = str_replace($match, "{{$tag}:{$random_id}:{$i}}", $text);
  310. }
  311. $stripdata = array(
  312. 'random_id' => $random_id,
  313. 'blocks' => $blocks[1]
  314. );
  315. }
  316. /**
  317. * Strip out an arbitrary HTML tag, pushing on to the existing list of stripped data.
  318. * @access private
  319. */
  320. public static function tag_strip_push($tag, &$text, &$stripdata)
  321. {
  322. if ( !is_array($stripdata) )
  323. {
  324. $stripdata = array(
  325. 'random_id' => md5( time() . mt_rand() ),
  326. 'blocks' => array()
  327. );
  328. }
  329. $random_id =& $stripdata['random_id'];
  330. preg_match_all("#<$tag>(.*?)</$tag>#is", $text, $blocks);
  331. foreach ( $blocks[0] as $i => $match )
  332. {
  333. $j = count($stripdata['blocks']);
  334. $stripdata['blocks'][] = $blocks[1][$i];
  335. $text = str_replace($match, "{{$tag}:{$random_id}:{$j}}", $text);
  336. }
  337. }
  338. /**
  339. * Restore stripped <nowiki> tags.
  340. * @access private
  341. */
  342. public static function tag_unstrip($tag, &$text, &$stripdata, $keep = false)
  343. {
  344. $random_id = $stripdata['random_id'];
  345. foreach ( $stripdata['blocks'] as $i => $block )
  346. {
  347. $block = $keep ? "<$tag>$block</$tag>" : $block;
  348. $text = str_replace("{{$tag}:{$random_id}:{$i}}", $block, $text);
  349. }
  350. $stripdata = array();
  351. }
  352. /**
  353. * Strip out PHP code (to prevent it from being sent through the parser). Private because it does not do what you think it does. (The method you are looking for is strip_php.)
  354. * @access private
  355. */
  356. private static function php_strip(&$text, &$stripdata)
  357. {
  358. $random_id = md5( time() . mt_rand() );
  359. preg_match_all('#<\?(?:php)?[\s=].+?\?>#is', $text, $blocks);
  360. foreach ( $blocks[0] as $i => $match )
  361. {
  362. $text = str_replace($match, "{PHP:$random_id:$i}", $text);
  363. }
  364. $stripdata = array(
  365. 'random_id' => $random_id,
  366. 'blocks' => $blocks[0]
  367. );
  368. }
  369. /**
  370. * Restore stripped PHP code
  371. * @access private
  372. */
  373. private static function php_unstrip(&$text, &$stripdata)
  374. {
  375. $random_id = $stripdata['random_id'];
  376. foreach ( $stripdata['blocks'] as $i => $block )
  377. {
  378. $text = str_replace("{PHP:$random_id:$i}", $block, $text);
  379. }
  380. $stripdata = array();
  381. }
  382. /**
  383. * Deprecated.
  384. */
  385. public static function wikiFormat($message, $filter_links = true, $do_params = false, $plaintext = false)
  386. {
  387. global $db, $session, $paths, $template, $plugins; // Common objects
  388. return RenderMan::next_gen_wiki_format($message, $plaintext, $filter_links, $do_params);
  389. }
  390. public static function destroy_javascript($message, $_php = false)
  391. {
  392. $message = preg_replace('#<(script|object|applet|embed|iframe|frame|form|input|select)(.*?)>#is', '&lt;\\1\\2&gt;', $message);
  393. $message = preg_replace('#</(script|object|applet|embed|iframe|frame|form|input|select)(.*?)>#is', '&lt;/\\1\\2&gt;', $message);
  394. $message = preg_replace('#(javascript|script|activex|chrome|about|applet):#is', '\\1&#058;', $message);
  395. if ( $_php )
  396. {
  397. // Left in only for compatibility
  398. $message = preg_replace('#&lt;(.*?)>#is', '<\\1>', $message);
  399. $message = preg_replace('#<(.*?)&gt;#is', '<\\1>', $message);
  400. $message = preg_replace('#<(\?|\?php|%)(.*?)(\?|%)>#is', '&lt;\\1\\2\\3&gt;', $message);
  401. // strip <a href="foo" onclick="bar();">-type attacks
  402. $message = preg_replace('#<([a-zA-Z:\-]+) (.*?)on([A-Za-z]*)=(.*?)>#is', '&lt;\\1\\2on\\3=\\4&gt;', $message);
  403. }
  404. return $message;
  405. }
  406. public static function strip_php($message)
  407. {
  408. return RenderMan::destroy_javascript($message, true);
  409. }
  410. public static function sanitize_html($text)
  411. {
  412. $text = htmlspecialchars($text);
  413. $allowed_tags = Array('b', 'i', 'u', 'pre', 'code', 'tt', 'br', 'p', 'nowiki', '!--([\w\W]+)--');
  414. foreach($allowed_tags as $t)
  415. {
  416. $text = preg_replace('#&lt;'.$t.'&gt;(.*?)&lt;/'.$t.'&gt;#is', '<'.$t.'>\\1</'.$t.'>', $text);
  417. $text = preg_replace('#&lt;'.$t.' /&gt;#is', '<'.$t.' />', $text);
  418. $text = preg_replace('#&lt;'.$t.'&gt;#is', '<'.$t.'>', $text);
  419. }
  420. return $text;
  421. }
  422. /**
  423. * Reverse-renders a blob of text (converts it from XHTML back to wikitext) by using parser hints and educated guesses.
  424. * @param string XHTML
  425. * @return string Wikitext
  426. */
  427. public static function reverse_render($text)
  428. {
  429. // convert \r\n to \n
  430. $text = str_replace("\r\n", "\n", $text);
  431. // Separate certain block level elements onto their own lines. This tidies up the tag
  432. // soup that TinyMCE sometimes produces.
  433. $block_elements = array('h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'p', 'div', 'table', 'ul', 'pre');
  434. $block_elements = implode('|', $block_elements);
  435. $regex = "#(</(?:$block_elements)>)\n?<($block_elements)(>| .+?>)#i";
  436. $text = preg_replace($regex, "$1\n\n<$2$3", $text);
  437. $text = self::reverse_process_parser_hints($text);
  438. $text = self::reverse_process_headings($text);
  439. $text = self::reverse_process_lists($text);
  440. $text = self::reverse_process_tables($text);
  441. // Lastly, strip out paragraph tags.
  442. $text = preg_replace('|^ *<p>(.+?)</p> *$|m', "\\1", $text);
  443. return $text;
  444. }
  445. public static function reverse_process_parser_hints($text)
  446. {
  447. global $db, $session, $paths, $template, $plugins; // Common objects
  448. if ( !preg_match_all('|<!--#([a-z0-9_]+)(?: (.+?))?-->([\w\W]*?)<!--#/\\1-->|s', $text, $matches) )
  449. return $text;
  450. foreach ( $matches[0] as $i => $match )
  451. {
  452. $tag =& $matches[1][$i];
  453. $attribs =& $matches[2][$i];
  454. $inner =& $matches[3][$i];
  455. $attribs = self::reverse_process_hint_attribs($attribs);
  456. switch($tag)
  457. {
  458. case 'smiley':
  459. case 'internallink':
  460. case 'imagelink':
  461. if ( isset($attribs['code']) )
  462. {
  463. $text = str_replace($match, $attribs['code'], $text);
  464. }
  465. else if ( isset($attribs['src']) )
  466. {
  467. $text = str_replace($match, $attribs['src'], $text);
  468. }
  469. break;
  470. }
  471. }
  472. return $text;
  473. }
  474. public static function reverse_process_hint_attribs($attribs)
  475. {
  476. $return = array();
  477. if ( !preg_match_all('/([a-z0-9_-]+)="([^"]+?)"/', $attribs, $matches) )
  478. return array();
  479. foreach ( $matches[0] as $i => $match )
  480. {
  481. $name =& $matches[1][$i];
  482. $value =& $matches[2][$i];
  483. $value = base64_decode($value);
  484. $return[$name] = $value;
  485. }
  486. return $return;
  487. }
  488. /**
  489. * Escapes a string so that it's safe to use as an attribute in a parser hint.
  490. * @param string
  491. * @return string
  492. */
  493. public static function escape_parser_hint_attrib($text)
  494. {
  495. return base64_encode($text);
  496. }
  497. public static function reverse_process_headings($text)
  498. {
  499. if ( !preg_match_all('|^<h([1-6])(?: id="head:[\w\.\/:;\(\)@\[\]=_-]+")?>(.*?)</h\\1>$|m', $text, $matches) )
  500. return $text;
  501. foreach ( $matches[0] as $i => $match )
  502. {
  503. // generate heading tag
  504. $heading_size = intval($matches[1][$i]);
  505. $eq = '';
  506. for ( $j = 0; $j < $heading_size; $j++ )
  507. $eq .= '=';
  508. $heading =& $matches[2][$i];
  509. $tag = "$eq $heading $eq";
  510. $text = str_replace($match, $tag, $text);
  511. }
  512. return $text;
  513. }
  514. public static function reverse_process_lists($text)
  515. {
  516. if ( !preg_match('!(</?(?:ul|ol|li)>)!', $text) )
  517. return $text;
  518. $split = preg_split('!(</?(?:ul|ol|li)>)!', $text, -1, PREG_SPLIT_DELIM_CAPTURE);
  519. $stack_height = 0;
  520. $current_list = '';
  521. $old_current_list = '';
  522. $spaces = '';
  523. $marker = '*';
  524. $list_id = 0;
  525. $just_terminated = false;
  526. foreach ( $split as $tag )
  527. {
  528. switch($tag)
  529. {
  530. case '<ul>':
  531. case '<ol>':
  532. $stack_height++;
  533. $just_terminated = false;
  534. if ( $stack_height > 1 )
  535. $spaces .= $marker;
  536. $marker = ( $tag == 'ol' ) ? '#' : '*';
  537. if ( $stack_height > 1 )
  538. $current_list .= "\n";
  539. break;
  540. case '</ul>':
  541. case '</ol>':
  542. $stack_height--;
  543. $spaces = substr($spaces, 1);
  544. if ( $stack_height == 0 )
  545. {
  546. // rotate
  547. $text = str_replace_once("{$old_current_list}{$tag}", trim($current_list), $text);
  548. $current_list = '';
  549. $old_current_list = '';
  550. }
  551. $just_terminated = true;
  552. break;
  553. case '<li>':
  554. if ( $stack_height < 1 )
  555. break;
  556. $current_list .= "{$spaces}{$marker} ";
  557. break;
  558. case '</li>':
  559. if ( $stack_height < 1 )
  560. break;
  561. if ( !$just_terminated )
  562. $current_list .= "\n";
  563. $just_terminated = false;
  564. break;
  565. default:
  566. if ( $stack_height > 0 )
  567. {
  568. $current_list .= trim($tag);
  569. }
  570. break;
  571. }
  572. if ( $stack_height > 0 )
  573. {
  574. $old_current_list .= $tag;
  575. }
  576. }
  577. return $text;
  578. }
  579. public static function reverse_process_tables($text)
  580. {
  581. return $text;
  582. }
  583. /**
  584. * Parses internal links (wikilinks) in a block of text.
  585. * @param string Text to process
  586. * @param string Optional. If included will be used as a template instead of using the default syntax.
  587. * @param bool If false, does not add wikilink-nonexistent or check for exsistence of pages. Can reduce DB queries; defualts to true.
  588. * @param string Page ID. If specified, class="currentpage" will be added to links if they match the given page ID and namespace
  589. * @param string Namespace. If specified, class="currentpage" will be added to links if they match the given page ID and namespace
  590. * @return string
  591. */
  592. public static function parse_internal_links($text, $tplcode = false, $do_exist_check = true, $match_page_id = false, $match_namespace = false)
  593. {
  594. global $db, $session, $paths, $template, $plugins; // Common objects
  595. $parser = is_string($tplcode) ? $template->makeParserText($tplcode) : false;
  596. // allow blank urlname?
  597. $repeater = have_blank_urlname_page() ? '*' : '+';
  598. // stage 1 - links with alternate text
  599. preg_match_all('/\[\[([^\[\]<>\{\}\|]' . $repeater . ')\|(.+?)\]\]/', $text, $matches);
  600. foreach ( $matches[0] as $i => $match )
  601. {
  602. list($page_id, $namespace) = RenderMan::strToPageID($matches[1][$i]);
  603. $link = self::generate_internal_link($namespace, $page_id, $matches[2][$i], $match, $parser, $do_exist_check, $match_page_id, $match_namespace);
  604. $text = str_replace($match, $link, $text);
  605. }
  606. // stage 2 - links with no alternate text
  607. preg_match_all('/\[\[([^\[\]<>\{\}\|]' . $repeater . ')\]\]/', $text, $matches);
  608. foreach ( $matches[0] as $i => $match )
  609. {
  610. list($page_id, $namespace) = RenderMan::strToPageID($matches[1][$i]);
  611. $pid_clean = $paths->nslist[$namespace] . sanitize_page_id($page_id);
  612. $inner_text = ( isPage($pid_clean) ) ? htmlspecialchars(get_page_title($pid_clean)) : htmlspecialchars($matches[1][$i]);
  613. $link = self::generate_internal_link($namespace, $page_id, $inner_text, $match, $parser, $do_exist_check, $match_page_id, $match_namespace);
  614. $text = str_replace($match, $link, $text);
  615. }
  616. return $text;
  617. }
  618. /**
  619. * Internal link generation function
  620. * @access private
  621. * @return string HTML
  622. */
  623. private static function generate_internal_link($namespace, $page_id, $inner_text, $match, $parser = false, $do_exist_check = true, $match_page_id = false, $match_namespace = false)
  624. {
  625. global $db, $session, $paths, $template, $plugins; // Common objects
  626. if ( ($pos = strrpos($page_id, '#')) !== false )
  627. {
  628. $hash = substr($page_id, $pos);
  629. $page_id = substr($page_id, 0, $pos);
  630. }
  631. else
  632. {
  633. $hash = '';
  634. }
  635. if ( $namespace == 'Admin' )
  636. {
  637. // No linking directly to Admin pages!
  638. $get = 'module=' . $paths->nslist[$namespace] . sanitize_page_id($page_id);
  639. $pid_clean = $paths->nslist['Special'] . 'Administration';
  640. $onclick = ' onclick="ajaxLoginNavTo(\'Special\', \'Administration\', USER_LEVEL_ADMIN, \'' . addslashes($get) . '\'); return false;"';
  641. }
  642. else
  643. {
  644. $get = false;
  645. $onclick = '';
  646. $pid_clean = $paths->nslist[$namespace] . sanitize_page_id($page_id);
  647. }
  648. $url = makeUrl($pid_clean, $get, true) . $hash;
  649. $quot = '"';
  650. $exists = ( ($do_exist_check && isPage($pid_clean)) || !$do_exist_check ) ? '' : ' class="wikilink-nonexistent"';
  651. if ( $match_page_id && $match_namespace && $pid_clean === $paths->get_pathskey($match_page_id, $match_namespace) )
  652. $exists .= ' class="currentpage"';
  653. if ( $parser )
  654. {
  655. $parser->assign_vars(array(
  656. 'HREF' => $url,
  657. 'FLAGS' => $exists,
  658. 'TEXT' => $inner_text
  659. ));
  660. $link = $parser->run();
  661. }
  662. else
  663. {
  664. $omatch = self::escape_parser_hint_attrib($match);
  665. $link = "<!--#internallink src=\"$omatch\" --><a{$onclick} href={$quot}{$url}{$quot}{$exists}>{$inner_text}</a><!--#/internallink-->";
  666. }
  667. return $link;
  668. }
  669. /**
  670. * Parses a partial template tag in wikitext, and return an array with the parameters.
  671. * @param string The portion of the template tag that contains the parameters.
  672. * @example
  673. * <code>
  674. foo = lorem ipsum
  675. bar = dolor sit amet
  676. * </code>
  677. * @return array Example:
  678. * [foo] => lorem ipsum
  679. * [bar] => dolor sit amet
  680. */
  681. public static function parse_template_vars($input, $newlinemode = true)
  682. {
  683. $parms = array();
  684. $input = trim($input);
  685. if ( $newlinemode )
  686. {
  687. // we're going by newlines.
  688. // split by parameter, then parse each one individually
  689. $input = explode("\n", str_replace("\r", '', $input));
  690. $lastparam = '';
  691. $i = 0;
  692. foreach ( $input as $line )
  693. {
  694. if ( preg_match('/^ *\|? *([A-z0-9_]+) *= *(.+)$/', $line, $match) )
  695. {
  696. // new parameter, named
  697. $parms[ $match[1] ] = $match[2];
  698. $lastparam = $match[1];
  699. }
  700. else if ( preg_match('/^ *\| *(.+)$/', $line, $match) || $lastparam === '' )
  701. {
  702. $parms[ $i ] = $match[1];
  703. $lastparam = $i;
  704. $i++;
  705. }
  706. else
  707. {
  708. $parms[ $lastparam ] .= "\n$line";
  709. }
  710. }
  711. }
  712. else
  713. {
  714. $result = preg_match_all('/
  715. (?:^|[ ]*)\| # start of parameter - string start or series of spaces
  716. [ ]*
  717. (?:
  718. ([A-z0-9_]+) # variable name
  719. [ ]* = [ ]* # assignment
  720. )? # name section is optional - if the parameter name is not given, a numerical index is assigned
  721. ([^\|]+|.+?\n[ ]*\|) # value
  722. /x', trim($input), $matches);
  723. if ( $result )
  724. {
  725. $pi = 0;
  726. for ( $i = 0; $i < count($matches[0]); $i++ )
  727. {
  728. $matches[1][$i] = trim($matches[1][$i]);
  729. $parmname = !empty($matches[1][$i]) ? $matches[1][$i] : strval(++$pi);
  730. $parms[ $parmname ] = $matches[2][$i];
  731. }
  732. }
  733. }
  734. // die('<pre>' . print_r($parms, true) . '</pre>');
  735. return $parms;
  736. }
  737. /**
  738. * Processes all template tags within a block of wikitext.
  739. * Updated in 1.0.2 to also parse template tags in the format of {{Foo |a = b |b = c |c = therefore, a}}
  740. * @param string The text to process
  741. * @return string Formatted text
  742. * @example
  743. * <code>
  744. $text = '{{Template
  745. | parm1 = Foo
  746. | parm2 = Bar
  747. }}';
  748. $text = RenderMan::include_templates($text);
  749. * </code>
  750. */
  751. public static function include_templates($text)
  752. {
  753. global $db, $session, $paths, $template, $plugins; // Common objects
  754. // $template_regex = "/\{\{([^\]]+?)((\n([ ]*?)[A-z0-9]+([ ]*?)=([ ]*?)(.+?))*)\}\}/is";
  755. // matches:
  756. // 1 - template name
  757. // 2 - parameter section
  758. $template_regex = "/
  759. \{\{ # opening
  760. ([^\n\t\a\r]+) # template name
  761. ((?:(?:[\s]+\|?)[ ]*(?:[A-z0-9_]+)[ ]*=[ ]*?(?:.+))*) # parameters
  762. \}\} # closing
  763. /isxU";
  764. if ( $count = preg_match_all($template_regex, $text, $matches) )
  765. {
  766. // die('<pre>' . print_r($matches, true) . '</pre>');
  767. for ( $i = 0; $i < $count; $i++ )
  768. {
  769. $matches[1][$i] = sanitize_page_id($matches[1][$i]);
  770. $newlinemode = ( substr($matches[2][$i], 0, 1) == "\n" );
  771. $parmsection = trim($matches[2][$i]);
  772. if ( !empty($parmsection) )
  773. {
  774. $parms = RenderMan::parse_template_vars($parmsection, $newlinemode);
  775. if ( !is_array($parms) )
  776. // Syntax error
  777. $parms = array();
  778. }
  779. else
  780. {
  781. $parms = Array();
  782. }
  783. if ( $tpl_code = RenderMan::fetch_template_text($matches[1][$i], $parms) )
  784. {
  785. // Intelligent paragraphs.
  786. // If:
  787. // * A line is fully wrapped in a <p> tag
  788. // * The line contains a variable
  789. // * The variable contains newlines
  790. // Then:
  791. // * Drop the <p> tag, replace it fully paragraph-ized by newlines
  792. if ( preg_match_all('/^( *)<p(\s.+)?>(.*?\{([A-z0-9]+)\}.*?)<\/p> *$/m', $tpl_code, $paramatch) )
  793. {
  794. $parser = new Carpenter();
  795. $parser->exclusive_rule('paragraph');
  796. foreach ( $paramatch[0] as $j => $match )
  797. {
  798. // $line is trimmed (the <p> is gone)
  799. $spacing =& $paramatch[1][$i];
  800. $para_attrs =& $paramatch[2][$j];
  801. $para_attrs = str_replace(array('$', '\\'), array('\$', '\\\\'), $para_attrs);
  802. $line =& $paramatch[3][$j];
  803. $varname =& $paramatch[4][$j];
  804. if ( isset($parms[$varname]) && strstr($parms[$varname], "\n") )
  805. {
  806. $newline = str_replace('{' . $varname . '}', $parms[$varname], $line);
  807. $paraized = $parser->render($newline);
  808. $paraized = preg_replace('/^<p>/m', "$spacing<p{$para_attrs}>", $paraized);
  809. $paraized = $spacing . trim($paraized);
  810. $tpl_code = str_replace_once($match, $paraized, $tpl_code);
  811. }
  812. }
  813. }
  814. $parser = $template->makeParserText($tpl_code);
  815. $parser->assign_vars($parms);
  816. $text = str_replace($matches[0][$i], $parser->run(), $text);
  817. }
  818. }
  819. }
  820. return $text;
  821. }
  822. /**
  823. * Preprocesses an HTML text string prior to being sent to MySQL.
  824. * @param string $text
  825. * @param bool $strip_all_php - if true, strips all PHP regardless of user permissions. Else, strips PHP only if user level < USER_LEVEL_ADMIN. Defaults to true.
  826. * @param bool $sqlescape - if true, sends text through $db->escape(). Otherwise returns unescaped text. Defaults to true.
  827. * @param bool $reduceheadings - if true, finds HTML headings and replaces them with wikitext. Else, does not touch headings. Defaults to true.
  828. * @param Session_ACLPageInfo Optional permissions instance to check against, $session is used if not provided
  829. */
  830. public static function preprocess_text($text, $strip_all_php = true, $sqlescape = true, $reduceheadings = true, $perms = false)
  831. {
  832. global $db, $session, $paths, $template, $plugins; // Common objects
  833. $random_id = md5( time() . mt_rand() );
  834. $code = $plugins->setHook('render_sanitize_pre');
  835. foreach ( $code as $cmd )
  836. {
  837. eval($cmd);
  838. }
  839. if ( !is_object($perms) )
  840. {
  841. $namespace = $paths->namespace;
  842. $perms =& $session;
  843. }
  844. else
  845. {
  846. $namespace = $perms->namespace;
  847. }
  848. $can_do_php = ( !$strip_all_php && $perms->get_permissions('php_in_pages') );
  849. $can_do_html = $session->check_acl_scope('html_in_pages', $namespace) && $perms->get_permissions('html_in_pages');
  850. if ( $can_do_html && !$can_do_php )
  851. {
  852. $text = preg_replace('#<(\?|\?php|%)(.*?)(\?|%)>#is', '&lt;\\1\\2\\3&gt;', $text);
  853. }
  854. else if ( !$can_do_html && !$can_do_php )
  855. {
  856. $text = sanitize_html($text, true);
  857. // If we can't do PHP, we can't do Javascript either.
  858. $text = RenderMan::destroy_javascript($text);
  859. }
  860. // Strip out <nowiki> sections and PHP code
  861. $php = preg_match_all('#(<|&lt;)\?php(.*?)\?(>|&gt;)#is', $text, $phpsec);
  862. //die('<pre>'.htmlspecialchars(print_r($phpsec, true))."\n".htmlspecialchars(print_r($text, true)).'</pre>');
  863. for($i=0;$i<sizeof($phpsec[1]);$i++)
  864. {
  865. $text = str_replace($phpsec[0][$i], '{PHP:'.$random_id.':'.$i.'}', $text);
  866. }
  867. $nw = preg_match_all('#<nowiki>(.*?)<\/nowiki>#is', $text, $nowiki);
  868. for($i=0;$i<sizeof($nowiki[1]);$i++)
  869. {
  870. $text = str_replace('<nowiki>'.$nowiki[1][$i].'</nowiki>', '{NOWIKI:'.$random_id.':'.$i.'}', $text);
  871. }
  872. $text = str_replace('~~~~~', enano_date('G:i, j F Y (T)'), $text);
  873. $text = str_replace('~~~~', "[[User:$session->username|$session->username]] ".enano_date('G:i, j F Y (T)'), $text);
  874. $text = str_replace('~~~', "[[User:$session->username|$session->username]] ", $text);
  875. $code = $plugins->setHook('render_sanitize_post');
  876. foreach ( $code as $cmd )
  877. {
  878. eval($cmd);
  879. }
  880. // gently apply some reverse-processing to allow the parser to do magic with TOCs and stuff
  881. if ( $reduceheadings )
  882. $text = self::reverse_process_headings($text);
  883. // Reinsert <nowiki> sections
  884. for($i=0;$i<$nw;$i++)
  885. {
  886. $text = str_replace('{NOWIKI:'.$random_id.':'.$i.'}', '<nowiki>'.$nowiki[1][$i].'</nowiki>', $text);
  887. }
  888. // Reinsert PHP
  889. for($i=0;$i<$php;$i++)
  890. {
  891. $phsec = ''.$phpsec[1][$i].'?php'.$phpsec[2][$i].'?'.$phpsec[3][$i].'';
  892. if ( $strip_all_php )
  893. $phsec = htmlspecialchars($phsec);
  894. $text = str_replace('{PHP:'.$random_id.':'.$i.'}', $phsec, $text);
  895. }
  896. $text = ( $sqlescape ) ? $db->escape($text) : $text;
  897. return $text;
  898. }
  899. public static function smilieyize($text, $complete_urls = false)
  900. {
  901. $random_id = md5( time() . mt_rand() );
  902. // Smileys array - eventually this will be fetched from the database by
  903. // RenderMan::initSmileys during initialization, but it will all be hardcoded for beta 2
  904. $smileys = Array(
  905. 'O:-)' => 'face-angel.png',
  906. 'O:)' => 'face-angel.png',
  907. 'O=)' => 'face-angel.png',
  908. ':-)' => 'face-smile.png',
  909. ':)' => 'face-smile.png',
  910. '=)' => 'face-smile-big.png',
  911. ':-(' => 'face-sad.png',
  912. ':(' => 'face-sad.png',
  913. ';(' => 'face-sad.png',
  914. ':-O' => 'face-surprise.png',
  915. ';-)' => 'face-wink.png',
  916. ';)' => 'face-wink.png',
  917. '8-)' => 'face-glasses.png',
  918. '8)' => 'face-glasses.png',
  919. ':-D' => 'face-grin.png',
  920. ':D' => 'face-grin.png',
  921. '=D' => 'face-grin.png',
  922. ':-*' => 'face-kiss.png',
  923. ':*' => 'face-kiss.png',
  924. '=*' => 'face-kiss.png',
  925. ':\'(' => 'face-crying.png',
  926. ':-|' => 'face-plain.png',
  927. ':-\\' => 'face-plain.png',
  928. ':-/' => 'face-plain.png',
  929. ':joke:' => 'face-plain.png',
  930. ']:-&gt;' => 'face-devil-grin.png',
  931. ']:->' => 'face-devil-grin.png',
  932. ':kiss:' => 'face-kiss.png',
  933. ':-P' => 'face-tongue-out.png',
  934. ':P' => 'face-tongue-out.png',
  935. ':-p' => 'face-tongue-out.png',
  936. ':p' => 'face-tongue-out.png',
  937. ':-X' => 'face-sick.png',
  938. ':X' => 'face-sick.png',
  939. ':sick:' => 'face-sick.png',
  940. ':-]' => 'face-oops.png',
  941. ':]' => 'face-oops.png',
  942. ':oops:' => 'face-oops.png',
  943. ':-[' => 'face-embarassed.png',
  944. ':[' => 'face-embarassed.png'
  945. );
  946. // Strip out <nowiki> sections
  947. //return '<pre>'.htmlspecialchars($text).'</pre>';
  948. $nw = preg_match_all('#<nowiki>(.*?)<\/nowiki>#is', $text, $nowiki);
  949. for ( $i = 0; $i < count($nowiki[1]); $i++ )
  950. {
  951. $text = str_replace('<nowiki>' . $nowiki[1][$i] . '</nowiki>', '{NOWIKI:'.$random_id.':'.$i.'}', $text);
  952. }
  953. foreach ( $smileys as $smiley => $smiley_path )
  954. {
  955. $hex_smiley = hexencode($smiley, '&#x', ';');
  956. $pfx = ( $complete_urls ) ? get_server_url() : '';
  957. $text = str_replace(' ' . $smiley,
  958. ' <!--#smiley code="' . self::escape_parser_hint_attrib($smiley) . '"--><nowiki>
  959. <!-- The above is a reverse-parser hint -->
  960. <img title="' . $hex_smiley . '" alt="' . $hex_smiley . '" src="' . $pfx . scriptPath . '/images/smilies/' . $smiley_path . '"
  961. style="border: 0;" />
  962. </nowiki><!--#/smiley-->', $text);
  963. }
  964. //*/
  965. // Reinsert <nowiki> sections
  966. for ( $i = 0; $i < $nw; $i++ )
  967. {
  968. $text = str_replace('{NOWIKI:'.$random_id.':'.$i.'}', '<nowiki>'.$nowiki[1][$i].'</nowiki>', $text);
  969. }
  970. return $text;
  971. }
  972. /**
  973. * Generates a summary of the differences between two texts, and formats it as XHTML.
  974. * @param $str1 string the first block of text
  975. * @param $str2 string the second block of text
  976. * @return string
  977. */
  978. public static function diff($str1, $str2)
  979. {
  980. global $db, $session, $paths, $template, $plugins; // Common objects
  981. require_once(ENANO_ROOT.'/includes/diff.php');
  982. $str1 = explode("\n", $str1);
  983. $str2 = explode("\n", $str2);
  984. $diff = new Diff($str1, $str2);
  985. $renderer = new TableDiffFormatter();
  986. return '<table class="diff">'.$renderer->format($diff).'</table>';
  987. }
  988. /**
  989. * Changes wikitext image tags to HTML.
  990. * @param string The wikitext to process
  991. * @param array Will be overwritten with the list of HTML tags (the system uses tokens for TextWiki compatibility)
  992. * @return string
  993. */
  994. public static function process_image_tags($text, &$taglist)
  995. {
  996. global $db, $session, $paths, $template, $plugins; // Common objects
  997. $s_delim = "\xFF";
  998. $f_delim = "\xFF";
  999. $taglist = array();
  1000. // Wicked huh?
  1001. $ns_file = str_replace('/', '\\/', preg_quote($paths->nslist['File']));
  1002. $regex = '/
  1003. \[\[ # starting delimiter
  1004. :' . $ns_file . '([\w\s0-9_\(\)!@%\^\+\|\.-]+?\.(?:png|gif|jpg|jpeg)) # image filename
  1005. (?:(?:\|(?:.+?))*) # parameters
  1006. \]\] # ending delimiter
  1007. /ix';
  1008. preg_match_all($regex, $text, $matches);
  1009. foreach ( $matches[0] as $i => $match )
  1010. {
  1011. $full_tag =& $matches[0][$i];
  1012. $filename =& $matches[1][$i];
  1013. // apply recursion (hack? @todo could this be done with (?R) in PCRE?)
  1014. // this allows other elements such as internal/external links to be embedded in image captions
  1015. $tag_pos = strpos($text, $full_tag);
  1016. $tag_end_pos = $tag_pos + strlen($full_tag);
  1017. while ( get_char_count($full_tag, ']') < get_char_count($full_tag, '[') && $tag_end_pos < strlen($text) )
  1018. {
  1019. $full_tag .= substr($text, $tag_end_pos, 1);
  1020. $tag_end_pos++;
  1021. }
  1022. if ( $tag_end_pos > strlen($text) )
  1023. {
  1024. // discard tag, not closed fully
  1025. continue;
  1026. }
  1027. // init the various image parameters
  1028. $width = null;
  1029. $height = null;
  1030. $scale_type = null;
  1031. $clear = null;
  1032. $caption = null;
  1033. $display_type = 'inline';
  1034. // trim tag and parse particles
  1035. $tag_trim = rtrim(ltrim($full_tag, '['), ']');
  1036. // trim off the filename from the start of the tag
  1037. $filepart_len = 1 + strlen($paths->nslist['File']) + strlen($filename) + 1;
  1038. $tag_trim = substr($tag_trim, $filepart_len);
  1039. // explode and we should have parameters
  1040. $tag_parts = explode('|', $tag_trim);
  1041. // for each of the parameters, see if it matches a known option. If so, apply it;
  1042. // otherwise, see if a plugin reserved that parameter and if not treat it as the caption
  1043. foreach ( $tag_parts as $param )
  1044. {
  1045. switch($param)
  1046. {
  1047. case 'left':
  1048. case 'right':
  1049. $clear = $param;
  1050. $display_type = 'framed';
  1051. break;
  1052. case 'thumb':
  1053. $scale_type = 'thumb';
  1054. break;
  1055. case 'raw':
  1056. $display_type = 'raw';
  1057. break;
  1058. default:
  1059. // height specification
  1060. if ( preg_match('/^([0-9]+)x([0-9]+)$/', $param, $dims) )
  1061. {
  1062. $width = intval($dims[1]);
  1063. $height = intval($dims[2]);
  1064. break;
  1065. }
  1066. // not the height, so see if a plugin took this over
  1067. // this hook requires plugins to return true if they modified anything
  1068. $code = $plugins->setHook('img_tag_parse_params', true);
  1069. foreach ( $code as $cmd )
  1070. {
  1071. if ( eval($cmd) )
  1072. break 2;
  1073. }
  1074. // we would have broken out by now if a plugin properly handled this,
  1075. // so just set the caption now.
  1076. $caption = $param;
  1077. break;
  1078. }
  1079. }
  1080. if ( !isPage( $paths->nslist['File'] . $filename ) )
  1081. {
  1082. $text = str_replace($full_tag, '[[' . $paths->nslist['File'] . $filename . ']]', $text);
  1083. continue;
  1084. }
  1085. if ( $scale_type == 'thumb' )
  1086. {
  1087. $r_width = 225;
  1088. $r_height = 225;
  1089. $url = makeUrlNS('Special', 'DownloadFile/' . $filename, 'preview&width=' . $r_width . '&height=' . $r_height, true);
  1090. }
  1091. else if ( !empty($width) && !empty($height) )
  1092. {
  1093. $r_width = $width;
  1094. $r_height = $height;
  1095. $url = makeUrlNS('Special', 'DownloadFile/' . $filename, 'preview&width=' . $r_width . '&height=' . $r_height, true);
  1096. }
  1097. else
  1098. {
  1099. $url = makeUrlNS('Special', 'DownloadFile/' . $filename);
  1100. }
  1101. $img_tag = '<img src="' . $url . '" ';
  1102. // if ( isset($r_width) && isset($r_height) && $scale_type != '|thumb' )
  1103. // {
  1104. // $img_tag .= 'width="' . $r_width . '" height="' . $r_height . '" ';
  1105. // }
  1106. $img_tag .= 'style="border-width: 0px;" ';
  1107. $code = $plugins->setHook('img_tag_parse_img');
  1108. foreach ( $code as $cmd )
  1109. {
  1110. eval($cmd);
  1111. }
  1112. $img_tag .= '/>';
  1113. $s_full_tag = self::escape_parser_hint_attrib($full_tag);
  1114. $complete_tag = '<!--#imagelink src="' . $s_full_tag . '" -->';
  1115. if ( $display_type == 'framed' )
  1116. {
  1117. $complete_tag .= '<div class="thumbnail" ';
  1118. $clear_text = '';
  1119. if ( !empty($clear) )
  1120. {
  1121. $side = ( $clear == 'left' ) ? 'left' : 'right';
  1122. $opposite = ( $clear == 'left' ) ? 'right' : 'left';
  1123. $clear_text .= "float: $side; margin-$opposite: 20px; width: {$r_width}px;";
  1124. $complete_tag .= 'style="' . $clear_text . '" ';
  1125. }
  1126. $complete_tag .= '>';
  1127. $complete_tag .= '<a href="' . makeUrlNS('File', $filename) . '" style="display: block;">';
  1128. $complete_tag .= $img_tag;
  1129. $complete_tag .= '</a>';
  1130. $mag_button = '<a href="' . makeUrlNS('File', $filename) . '" style="display: block; float: right; clear: right; margin: 0 0 10px 10px;"><img alt="[ + ]" src="' . scriptPath . '/images/thumbnail.png" style="border-width: 0px;" /></a>';
  1131. if ( !empty($caption) )
  1132. {
  1133. $complete_tag .= $mag_button . $caption;
  1134. }
  1135. $complete_tag .= '</div>';
  1136. }
  1137. else if ( $display_type == 'raw' )
  1138. {
  1139. $complete_tag .= "$img_tag";
  1140. $taglist[$i] = $complete_tag;
  1141. $repl = "{$s_delim}e_img_{$i}{$f_delim}";
  1142. $text = str_replace($full_tag, $repl, $text);
  1143. continue;
  1144. }
  1145. else
  1146. {
  1147. $complete_tag .= '<a href="' . makeUrlNS('File', $filename) . '" style="display: block;"';
  1148. $code = $plugins->setHook('img_tag_parse_link');
  1149. foreach ( $code as $cmd )
  1150. {
  1151. eval($cmd);
  1152. }
  1153. $complete_tag .= '>';
  1154. $complete_tag .= $img_tag;
  1155. $complete_tag .= '</a>';
  1156. }
  1157. $complete_tag .= "<!--#/imagelink-->";
  1158. $taglist[$i] = $complete_tag;
  1159. /*
  1160. $pos = strpos($text, $full_tag);
  1161. while(true)
  1162. {
  1163. $check1 = substr($text, $pos, 3);
  1164. $check2 = substr($text, $pos, 1);
  1165. if ( $check1 == '<p>' || $pos == 0 || $check2 == "\n" )
  1166. {
  1167. // die('found at pos '.$pos);
  1168. break;
  1169. }
  1170. $pos--;
  1171. }
  1172. */
  1173. /*
  1174. $repl = "{$s_delim}e_img_{$i}{$f_delim}";
  1175. $text = substr($text, 0, $pos) . $repl . substr($text, $pos);
  1176. $text = str_replace($full_tag, '', $text);
  1177. */
  1178. $text = str_replace_once($full_tag, $complete_tag, $text);
  1179. unset($full_tag, $filename, $scale_type, $width, $height, $clear, $caption, $r_width, $r_height);
  1180. }
  1181. // if ( count($matches[0]) > 0 )
  1182. // die('<pre>' . htmlspecialchars($text) . '</pre>');
  1183. return $text;
  1184. }
  1185. /**
  1186. * Finalizes processing of image tags.
  1187. * @param string The preprocessed text
  1188. * @param array The list of image tags created by RenderMan::process_image_tags()
  1189. */
  1190. public static function process_imgtags_stage2($text, $taglist)
  1191. {
  1192. $s_delim = "\xFF";
  1193. $f_delim = "\xFF";
  1194. foreach ( $taglist as $i => $tag )
  1195. {
  1196. $repl = "{$s_delim}e_img_{$i}{$f_delim}";
  1197. $text = str_replace($repl, $tag, $text);
  1198. }
  1199. return $text;
  1200. }
  1201. }
  1202. ?>