1<?php 2 3use ComboStrap\ExceptionCombo; 4use ComboStrap\Identity; 5use ComboStrap\PluginUtility; 6use ComboStrap\Site; 7use ComboStrap\TplUtility; 8 9if (!defined('DOKU_INC')) die(); 10 11/** 12 * 13 */ 14class action_plugin_combo_webcode extends DokuWiki_Action_Plugin 15{ 16 17 const CALL_ID = "webcode"; 18 const MARKI_PARAM = "marki"; 19 20 21 function register(Doku_Event_Handler $controller) 22 { 23 /** 24 * To serve fragment 25 */ 26 $controller->register_hook('AJAX_CALL_UNKNOWN', 'BEFORE', $this, '_ajax_call'); 27 28 /** 29 * To enforce security 30 */ 31 $controller->register_hook('COMMON_WIKIPAGE_SAVE', 'BEFORE', $this, '_enforceSecurity'); 32 33 } 34 35 /** 36 * handle ajax requests 37 * @param $event Doku_Event 38 * 39 * {@link html_show()} 40 * 41 * https://www.dokuwiki.org/devel:plugin_programming_tips#handle_json_ajax_request 42 * 43 * CSRF checks are only for logged in users 44 * This is public ({@link getSecurityToken()} 45 */ 46 function _ajax_call(&$event) 47 { 48 49 if ($event->data !== self::CALL_ID) { 50 return; 51 } 52 //no other ajax call handlers needed 53 $event->stopPropagation(); 54 $event->preventDefault(); 55 56 57 global $INPUT; 58 $marki = $INPUT->str(self::MARKI_PARAM); 59 $title = $INPUT->str('title') ?: "ComboStrap WebCode - Dokuwiki Renderer"; 60 61 62 header('Content-Type: text/html; charset=utf-8'); 63 64 /** 65 * Conf 66 */ 67 PluginUtility::setConf(action_plugin_combo_css::CONF_DISABLE_DOKUWIKI_STYLESHEET, true); 68 69 /** 70 * Main content happens before the headers 71 * to set the headers right 72 */ 73 global $conf; 74 $conf["renderer_xhtml"] = "xhtml"; 75 76 global $ID; 77 $keep = $ID; 78 try { 79 $ID = "ajax_webcode_" . md5($marki); 80 $mainContent = p_render('xhtml', p_get_instructions($marki), $info); 81 82 /** 83 * Html 84 */ 85 $htmlBeforeHeads = '<!DOCTYPE html>' . DOKU_LF; 86 $htmlBeforeHeads .= '<html lang="en">' . DOKU_LF; 87 $htmlBeforeHeads .= '<head>' . DOKU_LF; 88 $htmlBeforeHeads .= " <title>$title</title>" . DOKU_LF; 89 // we echo because the tpl function just flush 90 echo $htmlBeforeHeads; 91 92 if (Site::isStrapTemplate()) { 93 94 /** 95 * The strap header function 96 */ 97 try { 98 Site::loadStrapUtilityTemplateIfPresentAndSameVersion(); 99 TplUtility::registerHeaderHandler(); 100 } catch (ExceptionCombo $e) { 101 \ComboStrap\LogUtility::log2file("Error while registering the header handler on webcode ajax call. Error: {$e->getMessage()}"); 102 } 103 104 } 105 106 /** 107 * To delete the not needed headers for an export 108 * such as manifest, alternate, ... 109 */ 110 global $EVENT_HANDLER; 111 $EVENT_HANDLER->register_hook('TPL_METAHEADER_OUTPUT', 'BEFORE', $this, '_delete_not_needed_headers'); 112 113 /** 114 * meta headers 115 */ 116 tpl_metaheaders(); 117 118 119 $htmlAfterHeads = '</head>' . DOKU_LF; 120 $htmlAfterHeads .= '<body>' . DOKU_LF; 121 $htmlAfterHeads .= $mainContent . DOKU_LF; 122 $htmlAfterHeads .= '</body>' . DOKU_LF; 123 $htmlAfterHeads .= '</html>' . DOKU_LF; 124 echo $htmlAfterHeads; 125 http_response_code(200); 126 127 } finally { 128 $ID = $keep; 129 } 130 131 } 132 133 /** 134 * Dynamically called in the previous function 135 * to delete the head 136 * * @param $event Doku_Event 137 */ 138 public function _delete_not_needed_headers(Doku_Event &$event) 139 { 140 $data = &$event->data; 141 142 foreach ($data as $tag => &$heads) { 143 switch ($tag) { 144 case "link": 145 $deletedRel = ["manifest", "search", "start", "alternate", "contents"]; 146 foreach ($heads as $id => $headAttributes) { 147 if (isset($headAttributes['rel'])) { 148 $rel = $headAttributes['rel']; 149 if (in_array($rel, $deletedRel)) { 150 unset($heads[$id]); 151 } 152 } 153 } 154 break; 155 case "meta": 156 $deletedMeta = ["robots"]; 157 foreach ($heads as $id => $headAttributes) { 158 if (isset($headAttributes['name'])) { 159 $rel = $headAttributes['name']; 160 if (in_array($rel, $deletedMeta)) { 161 unset($heads[$id]); 162 } 163 } 164 } 165 break; 166 case "script": 167 foreach ($heads as $id => $headAttributes) { 168 if (isset($headAttributes['src'])) { 169 $src = $headAttributes['src']; 170 if (strpos($src, "lib/exe/js.php") !== false) { 171 unset($heads[$id]); 172 } 173 } 174 } 175 } 176 } 177 } 178 179 /** 180 * @param $event Doku_Event https://www.dokuwiki.org/devel:event:common_wikipage_save 181 * @return void 182 */ 183 function _enforceSecurity(Doku_Event &$event) 184 { 185 186 $data = $event->data; 187 $text = $data["newContent"]; 188 $pattern = PluginUtility::getContainerTagPattern(syntax_plugin_combo_webcode::TAG); 189 $result = preg_match("/" . $pattern . "/ms", $text); 190 if ($result === 0) { 191 return; 192 } 193 194 $isAdmin = Identity::isAdmin(); 195 $isMember = Identity::isMember("@" . action_plugin_combo_svg::CONF_SVG_UPLOAD_GROUP_NAME); 196 if (!($isAdmin || $isMember)) { 197 $event->preventDefault(); 198 } 199 200 } 201 202} 203