1<?php 2/** 3 * DokuWiki Plugin jsongendoc (Action Component) 4 * 5 * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html 6 * @author Janez Paternoster <janez.paternoster@siol.net> 7 */ 8 9// must be run within Dokuwiki 10if (!defined('DOKU_INC')) { 11 die(); 12} 13 14//Resolve to absolute page ID 15use dokuwiki\File\PageResolver; 16 17class action_plugin_jsongendoc extends DokuWiki_Action_Plugin 18{ 19 20 /** 21 * Registers a callback function for a given event 22 * 23 * @param Doku_Event_Handler $controller DokuWiki's event controller object 24 * 25 * @return void 26 */ 27 public function register(Doku_Event_Handler $controller) { 28 $controller->register_hook('AJAX_CALL_UNKNOWN', 'BEFORE', $this, 'handle_ajax_call_unknown'); 29 } 30 31 32 /** 33 * Handle event AJAX_CALL_UNKNOWN for json_plugin_save_inline and 34 * json_plugin_archive call 35 * 36 * @param Doku_Event $event event object by reference 37 * @param mixed $param [the parameters passed as fifth argument to register_hook() when this 38 * handler was registered] 39 * 40 * @return Ajax response as json 41 */ 42 public function handle_ajax_call_unknown(Doku_Event $event, $param) { 43 if ($event->data !== 'jsongendoc_plugin') { 44 return; 45 } 46 47 //no other ajax call handlers needed 48 $event->stopPropagation(); 49 $event->preventDefault(); 50 51 //access additional request variables 52 global $INPUT; 53 $new_page_id = cleanID($INPUT->str('name')); 54 $data = json_decode($INPUT->str('data'), true); 55 $parent_id = strval($data['parent_id']); 56 $mime = strval($data['mime']); 57 $docname_meta = strval($data['docname_meta']); 58 $namespace = strval($data['namespace']); 59 $template_id = cleanID($data['template']); 60 $json = $data['json']; 61 $new_page_exist = $template_exist = false; 62 $resolver = new PageResolver($namespace.':page'); // ':page' will be stripped 63 64 //resolve tempalate_id 65 if($template_id) { 66 $template_id = $resolver->resolveId($template_id); 67 $template_exist = page_exists($template_id); 68 $template_link = '<a href="'.wl($template_id).'">'.$template_id.'</a>'; 69 } 70 //resolve page_id, if page will be stored locally (mime undefined) 71 if(!$mime && $new_page_id) { 72 $new_page_id = $resolver->resolveId($new_page_id); 73 $new_page_exist = page_exists($new_page_id); 74 $new_page_link = '<a href="'.wl($new_page_id).'">'.$new_page_id.'</a>'; 75 $new_page_id_for_template = $new_page_id; 76 } 77 else { 78 //document will be downloaded to the client machine 79 $new_page_link = $new_page_id; 80 $new_page_id_for_template = $template_id; 81 } 82 83 //verify parameters, existances and rights 84 $err = ''; 85 if(!isset($json)) { 86 $err = $this->getLang('err_json'); 87 } 88 else if($data['template'] && $template_exist === false) { 89 $err = sprintf($this->getLang('err_template_not_exist'), $template_link); 90 } 91 else if($template_id && auth_quickaclcheck($template_id) < AUTH_READ) { 92 $err = sprintf($this->getLang('permision_denied_template'), $template_id); 93 } 94 else if(!$mime) { 95 if(!$new_page_id) { 96 $err = $this->getLang('err_no_page_name'); 97 } 98 else if($new_page_exist === true) { 99 $err = sprintf($this->getLang('err_page_exists'), $new_page_link); 100 } 101 else if(auth_quickaclcheck($new_page_id) < AUTH_CREATE) { 102 $err = sprintf($this->getLang('permision_denied_write'), $new_page_link); 103 } 104 } 105 106 //load template text from user or default namespace template 107 //and replace standard replacement patterns 108 //https://www.dokuwiki.org/namespace_templates 109 if($err === '') { 110 if($template_exist === true) { 111 //load user template 112 $parsePageTemplate_arg = array('id'=>$new_page_id_for_template, 'tpl'=>rawWiki($template_id)); 113 //perform common page template replacements 114 $new_page_text = parsePageTemplate($parsePageTemplate_arg); 115 } 116 else { 117 //load default template 118 $template_id = 'Namespace Template'; //for info 119 $new_page_text = pageTemplate($new_page_id_for_template); 120 $template_link = $this->external_link('https://www.dokuwiki.org/namespace_templates', 'Namespace Template', 'urlextern'); 121 } 122 if(!$new_page_text) { 123 $err = sprintf($this->getLang('err_empty_template'), $template_link, $new_page_link); 124 } 125 } 126 127 //statistics 128 $cnt = array( 129 'sections shown' => 0, 130 'sections removed' => 0, 131 'scalars extracted' => 0, 132 'json extracted' => 0, 133 'null values' => 0 134 ); 135 136 //helper functions from JSON plugin 137 $json_o = $this->loadHelper('json'); 138 139 //display or remove sections @%$-start (filter)% ... %$end%@ 140 if($err === '') { 141 $new_page_text = preg_replace_callback( 142 '/@(\%\$-start\s*\(([^[\]{}%]*?)\)%)(.*?)%\$end\%@/s', 143 function($matches) use($json_o, $json, &$cnt, &$err) { 144 if($err === '') { 145 list($match, $pattern, $filter, $contents) = array_pad($matches, 4, ''); 146 147 $filter = $json_o->parse_filter($filter); 148 if(count($filter) > 0) { 149 if($json_o->filter($json, $filter)) { 150 $cnt['sections shown'] ++; 151 return $contents; 152 } 153 else { 154 $cnt['sections removed'] ++; 155 return ''; 156 } 157 } 158 else { 159 $err = printf($this->getLang('err_tpl_section'), $template_link, $pattern); 160 } 161 } 162 return ''; 163 }, 164 $new_page_text 165 ); 166 } 167 168 //replace patterns @%$ ... %@ 169 if($err === '') { 170 $new_page_text = preg_replace_callback( 171 '/@%\$(.*?)%@/s', 172 function($matches) use($json_o, $json, &$cnt) { 173 list(, $path) = $matches; 174 175 $var = $json; 176 $tokens = $json_o->parse_tokens($path); 177 178 //get variable on specified path of the json data 179 foreach($tokens as $tok) { 180 if(is_array($var) && isset($var[$tok])) { 181 $var = $var[$tok]; 182 } 183 else { 184 $var = null; 185 break; 186 } 187 } 188 189 //return extraced data 190 if(is_bool($var)) { 191 $cnt['scalars extracted'] ++; 192 return $var ? 'true' : 'false'; 193 } 194 else if(is_scalar($var)) { 195 $cnt['scalars extracted'] ++; 196 return strval($var); 197 } 198 else if(is_array($var)) { 199 $cnt['json extracted'] ++; 200 return json_encode($var); 201 } 202 else { 203 $cnt['null values'] ++; 204 return 'null'; 205 } 206 }, 207 $new_page_text 208 ); 209 } 210 211 //create new file or send error 212 if($err === '') { 213 $response = array( 214 'response' => 'OK', 215 'mime' => '', 216 'msg' => sprintf($this->getLang('file_created_msg'), $new_page_link, $template_link) 217 .'<br/> ' 218 .sprintf($this->getLang('file_created_stat'), 219 $cnt['sections shown'], $cnt['sections removed'], 220 $cnt['scalars extracted'], $cnt['json extracted'], $cnt['null values'] 221 ) 222 .'<br/>' 223 ); 224 225 if(!$mime) { 226 //save document as new Dokuwiki page 227 saveWikiText($new_page_id, $new_page_text, sprintf($this->getLang('file_created_summary'), $template_id)); 228 } 229 else { 230 //send data, so they can be downloaded 231 $response['mime'] = $mime; 232 $response['document'] = $new_page_text; 233 } 234 235 //store autoincrement into metadata of the caller 236 if(strlen($docname_meta) > 0 && preg_match('/^(.+?)(\d+)$/', $new_page_id, $matches)) { 237 list(, $base, $number) = $matches; 238 $base .= str_repeat('0', strlen($number)); 239 if($docname_meta === $base && strlen($parent_id) > 0) { 240 p_set_metadata($parent_id, array('jsongendoc_'.$docname_meta => $number + 1)); 241 } 242 } 243 } 244 else { 245 $response = array( 246 'response' => 'error', 247 'msg' => '<span class="json-error">'.$this->getLang('err').'</span>: '.$err.'<br/>' 248 ); 249 } 250 251 //send response 252 header('Content-Type: application/json'); 253 echo json_encode($response); 254 } 255} 256