1<?php 2/** 3 * DokuWiki Plugin json (Remote 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 9class remote_plugin_json extends DokuWiki_Remote_Plugin { 10 public function _getMethods() { 11 return [ 12 'get' => [ 13 'args' => array('string', 'string', 'boolean'), 14 'return' => '[status, JSONdata, log]', 15 'name' => 'get', 16 'doc' => 'Get JSON data from page from data path.' 17 ], 18 'set' => [ 19 'args' => array('string', 'string', 'string|array', 'boolean'), 20 'return' => 'OK on success or error description', 21 'name' => 'set', 22 'doc' => 'Set JSON data inside the <json> element inside the wiki page.' 23 ], 24 'append' => [ 25 'args' => array('string', 'string', 'string|array'), 26 'return' => 'OK on success or error description', 27 'name' => 'append', 28 'doc' => 'Append JSON data into array inside page inside <json id=xxx> element.' 29 ] 30 ]; 31 } 32 33 /** 34 * Generate JSON database on page and return data from the JSON_path. 35 * 36 * @param string $page_id Absolute id of the wiki page. 37 * @param string $path Path on the JSON database. 38 * @param boolean $addLog If true, the additional info will be returned too. 39 * 40 * @return array status => string 'OK' on success or error description 41 * data => string JSON data from database 42 * log => array Only if $addLog is true. JSON string with 43 * description of database generation. 44 */ 45 public function get($page_id, $path = '', $addLog = false) { 46 $json_o = $this->loadHelper('json'); 47 $response = ['status' => 'OK']; 48 49 $src = $json_o->parse_src($page_id); 50 if (!is_array($src)) { 51 $response['status'] = sprintf($this->getLang('wrong_pageId'), $page_id); 52 } 53 else if (!page_exists($page_id)) { 54 $response['status'] = sprintf($this->getLang('file_not_found'), $page_id); 55 } 56 //verify file read rights 57 else if(auth_quickaclcheck($page_id) < AUTH_READ) { 58 $response['status'] = sprintf($this->getLang('permision_denied_read'), $page_id); 59 } 60 else { 61 $json = []; 62 $data_parameter = [ 63 'json_inline_raw' => '', 64 'src' => $src, 65 'path' => [ 66 'clear' => false, 67 'array' => false, 68 'tokens' => [] 69 ] 70 ]; 71 $log = ['tag' => 'remote', 'path' => $path]; 72 73 // generate complete JSON database from wikipage 74 $json_o->add_json($json, 75 $data_parameter, 76 $this->getConf('src_recursive'), 77 $log); 78 79 // get part of JSON data specified by path 80 $response['data'] = $json_o->get($json_o->parse_tokens($path), 81 $json); 82 83 // add information about JSON data loading 84 if ($addLog) { 85 $response['log'] = $log; 86 } 87 } 88 89 return $response; 90 } 91 92 /** 93 * Find <json id=… element inside page and set its inline data. 94 * 95 * @param string $page_id Absolute id of the wiki page. 96 * @param string $json_id 'id' attribute of the <json> element on the wiki page. 97 * @param string $data JSON data to be put inside <json></json>. 98 * @param boolean $overwrite If false, error will be reported if <json> element already contains data. 99 * 100 * @return string 'OK' on success or error description 101 */ 102 public function set($page_id, $json_id, $data, $overwrite = false) { 103 $err = ''; 104 105 if(!page_exists($page_id)) { 106 $err = sprintf($this->getLang('file_not_found'), $page_id); 107 } 108 //verify file write rights 109 else if(auth_quickaclcheck($page_id) < AUTH_EDIT) { 110 $err = sprintf($this->getLang('permision_denied_write'), $page_id); 111 } 112 113 //verify JSON data (must be an array, empty string or valid JSON string) 114 if($err === '') { 115 if (is_array($data)) { 116 $data = json_encode($data); 117 } 118 else { 119 $json_data = json_decode($data, true); 120 if (!(trim($data) === '' || isset($json_data))) { 121 $err = $this->getLang('json_error'); 122 } 123 unset($json_data); 124 } 125 } 126 127 //verify lock 128 $locked = false; 129 if($err === '') { 130 if(checklock($page_id)) { 131 $err = sprintf($this->getLang('file_locked'), $page_id); 132 } 133 else { 134 lock($page_id); 135 $locked = true; 136 } 137 } 138 139 //read the file 140 if($err === '') { 141 $file = rawWiki($page_id); 142 if(!$file) { 143 $err = sprintf($this->getLang('file_not_found'), $page_id); 144 } 145 } 146 147 //replace json data; must be one match 148 if($err === '') { 149 $file_updated = preg_replace_callback( 150 '/(<(json[a-z0-9]*)\b[^>]*?id\s*=[\s"\']*'.$json_id.'\b.*?>)(.*?)(<\/\2>)/s', 151 function($matches) use(&$err, $data, $overwrite) { 152 // replace only if forced or empty data 153 if($overwrite || !trim($matches[3])) { 154 $replacement = $matches[1].$data.$matches[4]; 155 return $replacement; 156 } 157 else { 158 //set error and keep the original data 159 $err = 'e'; 160 return $matches[0]; 161 } 162 }, 163 $file, 164 -1, 165 $count 166 ); 167 if($file_updated) { 168 if($count === 0) { 169 $err = sprintf($this->getLang('element_not_found'), $json_id); 170 } 171 else if($count !== 1) { 172 $err = sprintf($this->getLang('duplicated_id'), $json_id); 173 } 174 else if($err === 'e') { 175 $err = sprintf($this->getLang('not_empty'), $json_id); 176 } 177 } 178 else { 179 $err = sprintf($this->getLang('internal_error'), 'plugin/json/remote/set'); 180 } 181 } 182 183 //write file 184 if($err === '') { 185 saveWikiText($page_id, $file_updated, sprintf($this->getLang('json_updated_remote'), $json_id), true); 186 $err = 'OK'; 187 } 188 189 //unlock for editing 190 if($locked) { 191 unlock($page_id); 192 } 193 194 return $err; 195 } 196 197 198 /** 199 * Find <json id=… element inside page and append data to its inline database. 200 * 201 * Inline JSON data must be an array or empty. If empty, new array will be initialized. 202 * 203 * @param string $page_id Absolute id of the wiki page. 204 * @param string $json_id 'id' attribute of the <json> element. If empty, complete page will be 205 * treated as JSON database (page must be a JSON array, empty or non-existent). 206 * @param string $data JSON data to be appended inside <json>[]</json>. Must be an array or valid JSON string. 207 * 208 * @return string 'OK' on success or error description 209 */ 210 public function append($page_id, $json_id, $data) { 211 $err = ''; 212 213 if(!page_exists($page_id) && $json_id) { 214 $err = sprintf($this->getLang('file_not_found'), $page_id); 215 } 216 //verify file write rights 217 else if(auth_quickaclcheck($page_id) < AUTH_EDIT) { 218 $err = sprintf($this->getLang('permision_denied_write'), $page_id); 219 } 220 221 //verify JSON data (must be an array or valid JSON string) 222 if($err === '') { 223 if (is_array($data)) { 224 $data = json_encode($data); 225 } 226 else { 227 $json_data = json_decode($data, true); 228 if (isset($json_data)) { 229 $data = json_encode($json_data); //make string in one line 230 } 231 else { 232 $err = $this->getLang('json_error'); 233 } 234 unset($json_data); 235 } 236 } 237 238 //verify lock 239 $locked = false; 240 if($err === '') { 241 if(checklock($page_id)) { 242 $err = sprintf($this->getLang('file_locked'), $page_id); 243 } 244 else { 245 lock($page_id); 246 $locked = true; 247 } 248 } 249 250 //read the file 251 if($err === '') { 252 $file = rawWiki($page_id); 253 if(!$file) { 254 if ($json_id) { 255 $err = sprintf($this->getLang('file_not_found'), $page_id); 256 } 257 else { 258 $file = '[]'; 259 } 260 } 261 } 262 263 if($err === '') { 264 // pattern "[", "current_data", "]", ignore spaces 265 $json_array_pattern = '/^(\s*\[\s*)(.*?)\s*\]\s*$/s'; 266 //a classic dokuwiki page with <json> elements inside 267 if($json_id) { 268 //replace json data; must be one match 269 $file_updated = preg_replace_callback( 270 '/(<(json[a-z0-9]*)\b[^>]*?id\s*=[\s"\']*'.$json_id.'\b.*?>)(.*?)(<\/\2>)/s', 271 function($matches) use(&$err, $data, $json_array_pattern) { 272 $inlineJSON = trim($matches[3]) ? $matches[3] : '[]'; 273 274 if (preg_match($json_array_pattern, $inlineJSON, $matchesJSON)) { 275 if ($matchesJSON[2]) { 276 $inlineJSON = $matchesJSON[1].$matchesJSON[2].",\n ".$data."\n]"; 277 } else { 278 $inlineJSON = "[\n ".$data."\n]"; 279 } 280 281 return $matches[1].$inlineJSON.$matches[4]; 282 } 283 else { 284 //set error and keep original data 285 $err = 'e'; 286 return $matches[0]; 287 } 288 }, 289 $file, 290 -1, 291 $count 292 ); 293 if($file_updated) { 294 if($count === 0) { 295 $err = sprintf($this->getLang('element_not_found'), $json_id); 296 } 297 else if($count !== 1) { 298 $err = sprintf($this->getLang('duplicated_id'), $json_id); 299 } 300 else if($err === 'e') { 301 $err = sprintf($this->getLang('not_array'), $json_id); 302 } 303 } 304 else { 305 $err = sprintf($this->getLang('internal_error'), 'plugin/json/remote/append'); 306 } 307 } 308 //a pure JSON file 309 else { 310 if (preg_match($json_array_pattern, $file, $matchesJSON)) { 311 if ($matchesJSON[2]) { 312 $file_updated = $matchesJSON[1].$matchesJSON[2].",\n ".$data."\n]"; 313 } else { 314 $file_updated = "[\n ".$data."\n]"; 315 } 316 } 317 else { 318 $err = sprintf($this->getLang('not_array_file'), $page_id); 319 } 320 } 321 } 322 323 //write file 324 if($err === '') { 325 saveWikiText($page_id, $file_updated, sprintf($this->getLang('json_added_remote'), $json_id), true); 326 $err = 'OK'; 327 } 328 329 //unlock for editing 330 if($locked) { 331 unlock($page_id); 332 } 333 334 return $err; 335 } 336} 337