1<?php 2 3/** 4 * Class action_plugin_tagging_main 5 */ 6class action_plugin_tagging_main extends DokuWiki_Action_Plugin { 7 8 /** 9 * Register handlers 10 * 11 * @param Doku_Event_Handler $controller 12 */ 13 public function register(Doku_Event_Handler $controller) { 14 $controller->register_hook( 15 'AJAX_CALL_UNKNOWN', 'BEFORE', $this, 16 'handle_ajax_call_unknown' 17 ); 18 19 $controller->register_hook( 20 'ACTION_ACT_PREPROCESS', 'BEFORE', $this, 21 'handle_jump' 22 ); 23 24 $controller->register_hook( 25 'DOKUWIKI_STARTED', 'AFTER', $this, 26 'js_add_security_token' 27 ); 28 29 $controller->register_hook( 30 'PLUGIN_MOVE_PAGE_RENAME', 'AFTER', $this, 31 'update_moved_page' 32 ); 33 } 34 35 /** 36 * Add sectok to JavaScript to secure ajax requests 37 * 38 * @param Doku_Event $event 39 * @param $param 40 */ 41 function js_add_security_token(Doku_Event $event, $param) { 42 global $JSINFO; 43 $JSINFO['sectok'] = getSecurityToken(); 44 } 45 46 /** 47 * Handle our AJAX requests 48 * 49 * @param Doku_Event $event 50 * @param $param 51 */ 52 function handle_ajax_call_unknown(Doku_Event &$event, $param) { 53 $handled = true; 54 55 if ($event->data == 'plugin_tagging_save') { 56 $this->save(); 57 } elseif ($event->data == 'plugin_tagging_autocomplete') { 58 $this->autocomplete(); 59 } elseif ($event->data === 'plugin_tagging_admin_change') { 60 $this->admin_change(); 61 } elseif ($event->data === 'plugin_tagging_html_pages') { 62 $this->getPagesHtml(); 63 } elseif ($event->data === 'plugin_tagging_delete') { 64 $this->deleteTag(); 65 } elseif ($event->data === 'plugin_tagging_rename') { 66 $this->renameTag(); 67 } else { 68 $handled = false; 69 } 70 if (!$handled) { 71 return; 72 } 73 74 $event->preventDefault(); 75 $event->stopPropagation(); 76 } 77 78 /** 79 * Jump to a tag 80 * 81 * @param Doku_Event $event 82 * @param $param 83 */ 84 function handle_jump(Doku_Event &$event, $param) { 85 if (act_clean($event->data) != 'tagjmp') { 86 return; 87 } 88 89 $event->preventDefault(); 90 $event->stopPropagation(); 91 $event->data = 'show'; 92 93 global $INPUT; 94 $tags = $INPUT->arr('tag', (array)$INPUT->str('tag')); 95 $lang = $INPUT->str('lang'); 96 97 /** @var helper_plugin_tagging $hlp */ 98 $hlp = plugin_load('helper', 'tagging'); 99 100 foreach ($tags as $tag) { 101 $filter = array('tag' => $tag); 102 if ($lang) { 103 $filter['lang'] = $lang; 104 } 105 $pages = $hlp->findItems($filter, 'pid', 1); 106 if (!count($pages)) { 107 continue; 108 } 109 110 $pages = array_keys($pages); 111 $id = array_pop($pages); 112 send_redirect(wl($id, '', true, '&')); 113 } 114 115 $tags = array_map('hsc', $tags); 116 msg(sprintf($this->getLang('tagjmp_error'), join(', ', $tags)), -1); 117 } 118 119 /** 120 * Save new/changed tags 121 */ 122 function save() { 123 global $INPUT; 124 global $INFO; 125 126 /** @var helper_plugin_tagging $hlp */ 127 $hlp = plugin_load('helper', 'tagging'); 128 129 $data = $INPUT->arr('tagging'); 130 $id = $data['id']; 131 $INFO['writable'] = auth_quickaclcheck($id) >= AUTH_EDIT; // we also need this in findItems 132 133 if ($INFO['writable'] && $hlp->getUser()) { 134 $hlp->replaceTags( 135 $id, $hlp->getUser(), 136 preg_split( 137 '/(\s*,\s*)|(\s*,?\s*\n\s*)/', $data['tags'], -1, 138 PREG_SPLIT_NO_EMPTY 139 ) 140 ); 141 $hlp->updateElasticState($id); 142 } 143 144 $tags = $hlp->findItems(array('pid' => $id), 'tag'); 145 $hlp->html_cloud($tags, 'tag', array($hlp, 'linkToSearch'), false); 146 } 147 148 /** 149 * Return autocompletion data 150 */ 151 function autocomplete() { 152 global $INPUT; 153 154 /** @var helper_plugin_tagging $hlp */ 155 $hlp = plugin_load('helper', 'tagging'); 156 157 $search = $INPUT->str('term'); 158 $tags = $hlp->findItems(array('tag' => '*' . $hlp->getDB()->escape_string($search) . '*'), 'tag'); 159 arsort($tags); 160 $tags = array_keys($tags); 161 162 header('Content-Type: application/json'); 163 164 echo json_encode(array_combine($tags, $tags)); 165 } 166 167 /** 168 * Allow admins to change all tags (not only their own) 169 * We change the tag for every user 170 */ 171 function admin_change() { 172 global $INPUT; 173 174 /** @var helper_plugin_tagging $hlp */ 175 $hlp = plugin_load('helper', 'tagging'); 176 177 header('Content-Type: application/json'); 178 179 if (!auth_isadmin()) { 180 echo json_encode(array('status' => 'error', 'msg' => $this->getLang('no_admin'))); 181 return; 182 } 183 184 if (!checkSecurityToken()) { 185 echo json_encode(array('status' => 'error', 'msg' => 'Security Token did not match. Possible CSRF attack.')); 186 return; 187 } 188 189 if (!$INPUT->has('id')) { 190 echo json_encode(array('status' => 'error', 'msg' => 'No page id given.')); 191 return; 192 } 193 $pid = $INPUT->str('id'); 194 195 if (!$INPUT->has('oldValue') || !$INPUT->has('newValue')) { 196 echo json_encode(array('status' => 'error', 'msg' => 'No proper input. Give "oldValue" and "newValue"')); 197 return; 198 } 199 200 201 list($err, $msg) = $hlp->modifyPageTag($pid, $INPUT->str('oldValue'), $INPUT->str('newValue')); 202 if ($err) { 203 echo json_encode(array('status' => 'error', 'msg' => $msg)); 204 return; 205 } 206 207 $tags = $hlp->findItems(array('pid' => $pid), 'tag'); 208 $userTags = $hlp->findItems(array('pid' => $pid, 'tagger' => $hlp->getUser()), 'tag'); 209 echo json_encode(array( 210 'status' => 'ok', 211 'tags_edit_value' => implode(', ', array_keys($userTags)), 212 'html_cloud' => $hlp->html_cloud($tags, 'tag', array($hlp, 'linkToSearch'), false, true), 213 )); 214 } 215 216 /** 217 * Management: delete all occurrences of a tag 218 */ 219 public function deleteTag() 220 { 221 global $INPUT; 222 $data = $INPUT->arr('tagging'); 223 224 /** @var helper_plugin_tagging $hlp */ 225 $hlp = plugin_load('helper', 'tagging'); 226 $hlp->deleteTags($data['tid']); 227 228 // update elasticsearch state for all relevant pages 229 $pids = $hlp->findItems(['tag' => $data['tid'][0]], 'pid'); 230 if (!empty($pids)) { 231 foreach (array_keys($pids) as $pid) { 232 $hlp->updateElasticState($pid); 233 } 234 } 235 } 236 237 /** 238 * Management: rename all occurrences of a tag 239 */ 240 public function renameTag() 241 { 242 global $INPUT; 243 $data = $INPUT->arr('tagging'); 244 245 /** @var helper_plugin_tagging $hlp */ 246 $hlp = plugin_load('helper', 'tagging'); 247 $hlp->renameTag($data['oldValue'], $data['newValue']); 248 249 // update elasticsearch state for all relevant pages 250 $pids = $hlp->findItems(['tag' => $data['newValue']], 'pid'); 251 if (!empty($pids)) { 252 foreach (array_keys($pids) as $pid) { 253 $hlp->updateElasticState($pid); 254 } 255 } 256 } 257 258 /** 259 * Tag dialog HTML: links to all pages with a given tag 260 * 261 * @return string 262 */ 263 public function getPagesHtml() 264 { 265 global $INPUT; 266 $data = $INPUT->arr('tagging'); 267 268 /** @var helper_plugin_tagging $hlp */ 269 $hlp = plugin_load('helper', 'tagging'); 270 echo $hlp->getPagesHtml($data['tid']); 271 } 272 273 /** 274 * Updates tagging database after a page has been moved/renamed by the move plugin 275 * 276 * @param Doku_Event $event 277 * @param $param 278 */ 279 public function update_moved_page(Doku_Event $event, $param) 280 { 281 $src = $event->data['src_id']; 282 $dst = $event->data['dst_id']; 283 284 /** @var helper_plugin_tagging $hlp */ 285 $hlp = plugin_load('helper', 'tagging'); 286 $hlp->renamePage($src, $dst); 287 } 288} 289