PageRenderTime 26ms CodeModel.GetById 9ms RepoModel.GetById 0ms app.codeStats 0ms

/plugins/serendipity_event_xhtmlcleanup/serendipity_event_xhtmlcleanup.php

http://github.com/s9y/Serendipity
PHP | 273 lines | 230 code | 34 blank | 9 comment | 19 complexity | fa977c1dc11b32d91099bad7cc9222f4 MD5 | raw file
Possible License(s): BSD-3-Clause, LGPL-3.0, LGPL-2.1, MPL-2.0-no-copyleft-exception, Apache-2.0
  1. <?php
  2. if (IN_serendipity !== true) {
  3. die ("Don't hack!");
  4. }
  5. @serendipity_plugin_api::load_language(dirname(__FILE__));
  6. class serendipity_event_xhtmlcleanup extends serendipity_event
  7. {
  8. var $title = PLUGIN_EVENT_XHTMLCLEANUP_NAME;
  9. var $cleanup_tag, $cleanup_checkfor, $cleanup_val, $cleanup_parse;
  10. function introspect(&$propbag)
  11. {
  12. global $serendipity;
  13. $propbag->add('name', PLUGIN_EVENT_XHTMLCLEANUP_NAME);
  14. $propbag->add('description', PLUGIN_EVENT_XHTMLCLEANUP_DESC);
  15. $propbag->add('stackable', false);
  16. $propbag->add('author', 'Garvin Hicking');
  17. $propbag->add('version', '1.8.1');
  18. $propbag->add('requirements', array(
  19. 'serendipity' => '1.6',
  20. 'smarty' => '2.6.7',
  21. 'php' => '4.1.0'
  22. ));
  23. $propbag->add('groups', array('BACKEND_TEMPLATES'));
  24. $propbag->add('cachable_events', array('frontend_display' => true));
  25. $propbag->add('event_hooks', array(
  26. 'frontend_display' => true,
  27. 'frontend_display:html:per_entry' => true,
  28. 'backend_view_comment' => true
  29. ));
  30. $this->markup_elements = array(
  31. array(
  32. 'name' => 'ENTRY_BODY',
  33. 'element' => 'body',
  34. ),
  35. array(
  36. 'name' => 'EXTENDED_BODY',
  37. 'element' => 'extended',
  38. ),
  39. array(
  40. 'name' => 'COMMENT',
  41. 'element' => 'comment',
  42. ),
  43. array(
  44. 'name' => 'HTML_NUGGET',
  45. 'element' => 'html_nugget',
  46. )
  47. );
  48. $conf_array = array();
  49. foreach($this->markup_elements as $element) {
  50. $conf_array[] = $element['name'];
  51. }
  52. $conf_array[] = 'xhtml_parse';
  53. $conf_array[] = 'utf8_parse';
  54. $conf_array[] = 'youtube';
  55. $propbag->add('configuration', $conf_array);
  56. }
  57. function install()
  58. {
  59. serendipity_plugin_api::hook_event('backend_cache_entries', $this->title);
  60. }
  61. function uninstall(&$propbag)
  62. {
  63. serendipity_plugin_api::hook_event('backend_cache_purge', $this->title);
  64. serendipity_plugin_api::hook_event('backend_cache_entries', $this->title);
  65. }
  66. function generate_content(&$title)
  67. {
  68. $title = $this->title;
  69. }
  70. function introspect_config_item($name, &$propbag)
  71. {
  72. if ($name == 'utf8_parse') {
  73. $propbag->add('type', 'boolean');
  74. $propbag->add('name', PLUGIN_EVENT_XHTMLCLEANUP_UTF8);
  75. $propbag->add('description', PLUGIN_EVENT_XHTMLCLEANUP_UTF8_DESC);
  76. $propbag->add('default', 'true');
  77. } elseif ($name == 'xhtml_parse') {
  78. $propbag->add('type', 'boolean');
  79. $propbag->add('name', PLUGIN_EVENT_XHTMLCLEANUP_XHTML);
  80. $propbag->add('description', PLUGIN_EVENT_XHTMLCLEANUP_XHTML_DESC);
  81. $propbag->add('default', 'true');
  82. } elseif ($name == 'youtube') {
  83. $propbag->add('type', 'boolean');
  84. $propbag->add('name', PLUGIN_EVENT_XHTMLCLEANUP_YOUTUBE);
  85. $propbag->add('description', PLUGIN_EVENT_XHTMLCLEANUP_YOUTUBE_DESC);
  86. $propbag->add('default', 'false');
  87. } else {
  88. $propbag->add('type', 'boolean');
  89. $propbag->add('name', constant($name));
  90. $propbag->add('description', sprintf(APPLY_MARKUP_TO, constant($name)));
  91. $propbag->add('default', 'true');
  92. }
  93. return true;
  94. }
  95. function fixUTFEntity(&$string)
  96. {
  97. $string = preg_replace('/&amp;#(x[a-f0-9]{1,4}|[0-9]{1,5});/', '&#$1;', $string);
  98. return true;
  99. }
  100. function event_hook($event, &$bag, &$eventData, $addData = null)
  101. {
  102. global $serendipity;
  103. static $convert_fields = array(
  104. 'fullBody',
  105. 'summary',
  106. 'title',
  107. 'author',
  108. );
  109. static $youtube = null;
  110. if ($youtube === null) {
  111. $youtube = serendipity_db_bool($this->get_config('youtube', 'false'));
  112. }
  113. $hooks = &$bag->get('event_hooks');
  114. if (isset($hooks[$event])) {
  115. switch($event) {
  116. case 'backend_view_comment':
  117. if (serendipity_db_bool($this->get_config('utf8_parse', 'true'))) {
  118. foreach($convert_fields AS $convert_field) {
  119. $this->fixUTFEntity($eventData[$convert_field]);
  120. }
  121. }
  122. break;
  123. case 'frontend_display':
  124. $this->cleanup_parse = serendipity_db_bool($this->get_config('xhtml_parse', 'true'));
  125. foreach ($this->markup_elements as $temp) {
  126. if (serendipity_db_bool($this->get_config($temp['name'], 'true')) && isset($eventData[$temp['element']]) &&
  127. (isset($eventData['properties']) && isset($eventData['properties']['ep_disable_markup_' . $this->instance]) && !$eventData['properties']['ep_disable_markup_' . $this->instance]) &&
  128. !isset($serendipity['POST']['properties']['disable_markup_' . $this->instance])) {
  129. $element = $temp['element'];
  130. $this->cleanup_tag = 'IMG';
  131. $this->cleanup_checkfor = 'ALT';
  132. $this->cleanup_val = '';
  133. // Basic cleanup (core s9y functionality)
  134. $eventData[$element] = xhtml_cleanup($eventData[$element]);
  135. $eventData[$element] = preg_replace_callback('@(<img.+/?>)@imsU', array($this, 'clean_tag'), $eventData[$element]);
  136. $eventData[$element] = preg_replace_callback("@<(a|iframe|param)(.*)(href|src|value)=(\"|')([^\"']+)(\"|')@isUm", array($this, 'clean_htmlspecialchars'), $eventData[$element]);
  137. if ($youtube) {
  138. $this->youtubify($eventData[$element]);
  139. }
  140. }
  141. }
  142. if (serendipity_db_bool($this->get_config('utf8_parse', 'true'))) {
  143. $this->fixUTFEntity($eventData['author']);
  144. $this->fixUTFEntity($eventData['comment']);
  145. }
  146. break;
  147. case 'frontend_display:html:per_entry':
  148. if (serendipity_db_bool($this->get_config('utf8_parse', 'true'))) {
  149. $this->fixUTFEntity($eventData['author']);
  150. $this->fixUTFEntity($eventData['title']);
  151. }
  152. break;
  153. default:
  154. return false;
  155. }
  156. return true;
  157. } else {
  158. return false;
  159. }
  160. }
  161. function youtubify(&$text)
  162. {
  163. $text = preg_replace_callback('@<object(.*)>(.*)</object>@imsU', array($this, 'youtubify_regex'), $text);
  164. }
  165. function youtubify_regex($matches)
  166. {
  167. if (!preg_match('@<embed@i', $matches[2])) return $matches[0];
  168. preg_match('@width=["\']?([0-9]+)@ims', $matches[1], $m);
  169. $width = $m[1];
  170. preg_match('@height=["\']?([0-9]+)@ims', $matches[1], $m);
  171. $height = $m[1];
  172. preg_match('@<param name="movie" value="(.+)"[\s/]*?>@imsU', $matches[2], $m);
  173. $movie = $m[1];
  174. if (empty($movie)) {
  175. preg_match('@<param value="(.+)" name="movie"[\s/]*?>@imsU', $matches[2], $m);
  176. $movie = $m[1];
  177. }
  178. $appendix = preg_replace('@<embed.*>@imsU', '', $matches[2]);
  179. $appendix = str_replace('</embed>', '', $appendix);
  180. $out = '<!-- xhtml clean youtube --><object type="application/x-shockwave-flash" width="' . $width . '" height="' . $height . '" data="' . $movie . '">'
  181. . '<param name="movie" value="' . $movie . '" />'
  182. . $appendix . '</object><!-- /xhtml clean youtube -->';
  183. $out .= "\n\n<!-- {$matches[0]} -->\n\n";
  184. return $out;
  185. }
  186. // Takes an input tag and search for ommitted attributes. Expects a single tag (array, index 0)
  187. function clean_tag($data)
  188. {
  189. // Restore tags from preg_replace_callback buffer, as those can't be passed in the function header
  190. $tag = &$this->cleanup_tag;
  191. $checkfor = &$this->cleanup_checkfor;
  192. $val = &$this->cleanup_val;
  193. // Instead of nasty regex-mangling we use the XML parser to get the attribute list of our input tag
  194. switch(strtolower(LANG_CHARSET)) {
  195. case 'iso-8859-1':
  196. case 'utf-8':
  197. $p = xml_parser_create(LANG_CHARSET);
  198. break;
  199. default:
  200. $p = xml_parser_create('');
  201. }
  202. @xml_parse_into_struct($p, $data[0], $vals, $index);
  203. xml_parser_free($p);
  204. // Check if the xml parser returned anything useful
  205. if (is_array($vals) && isset($vals[0]) && $vals[0]['tag'] == $tag) {
  206. if (!empty($vals[0]['attributes'][$checkfor])) {
  207. // The attribute we search for already exists. Return original string.
  208. return $data[0];
  209. }
  210. // Assign the value we submitted for the attribute to insert
  211. $vals[0]['attributes'][$checkfor] = $val;
  212. // Reconstruct XHTML tag.
  213. $atts = ' ';
  214. foreach($vals[0]['attributes'] AS $att => $att_con) {
  215. $atts .= strtolower($att) . '="' . ($this->cleanup_parse ? serendipity_specialchars($att_con) : $att_con) . '" ';
  216. }
  217. return '<' . strtolower($tag) . $atts . ' />';
  218. }
  219. return $data[0];
  220. }
  221. function clean_htmlspecialchars($given, $quote_style = ENT_QUOTES)
  222. {
  223. return '<' . $given[1] . $given[2] . $given[3] . '=' . $given[4] . serendipity_specialchars(serendipity_entity_decode($given[5], $quote_style), $quote_style) . $given[6];
  224. }
  225. }
  226. /* vim: set sts=4 ts=4 expandtab : */
  227. ?>