1<?php 2/** 3 * DokuWiki Plugin linksuggest (Action Component) 4 * 5 * ajax autosuggest for links 6 * 7 * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) 8 * @author lisps 9 */ 10 11class action_plugin_linksuggest extends DokuWiki_Action_Plugin { 12 13 /** 14 * Register the eventhandlers 15 * 16 * @param Doku_Event_Handler $controller 17 */ 18 function register(Doku_Event_Handler $controller) { 19 $controller->register_hook('AJAX_CALL_UNKNOWN', 'BEFORE', $this, 'page_link'); 20 $controller->register_hook('AJAX_CALL_UNKNOWN', 'BEFORE', $this, 'media_link'); 21 } 22 23 /** 24 * ajax Request Handler 25 * page_link 26 * 27 * @param $event 28 * @param $param 29 */ 30 function page_link(&$event, $param) { 31 if ($event->data !== 'plugin_linksuggest') { 32 return; 33 } 34 //no other ajax call handlers needed 35 $event->stopPropagation(); 36 $event->preventDefault(); 37 38 global $INPUT; 39 40 $page_ns = trim($INPUT->post->str('ns')); //current namespace 41 $page_id = trim($INPUT->post->str('id')); //current id 42 $q = trim($INPUT->post->str('q')); //entered string 43 44 //keep hashlink if exists 45 $hash = null; 46 if (strpos($q, '#') !== false) { 47 list($q, $hash) = explode('#', $q, 2); 48 } 49 $has_hash = $hash === null ? false : true; 50 $ns_user = $ns = getNS($q); //namespace of entered string 51 $id = cleanID(noNS($q)); //page of entered string 52 53 if ($q && trim($q, '.') === '') { //only "." return 54 $data = []; 55 } else if ($ns === '') { // [[:xxx -> absolute link 56 $data = $this->search_pages($ns, $id, $has_hash); 57 } else if ($ns === false && $page_ns) { // [[xxx and not in root-namespace 58 $data = array_merge( 59 $this->search_pages($page_ns, $id, true),//search in current 60 $this->search_pages('', $id, $has_hash) //and in root 61 ); 62 } else if (strpos($ns, '.') !== false) { //relative link 63 resolve_pageid($page_ns, $ns, $exists); //resolve the ns based on current id 64 $data = $this->search_pages($ns, $id, $has_hash); 65 } else { 66 $data = $this->search_pages($ns, $id, $has_hash); 67 } 68 69 70 $data_r = []; 71 $link = ''; 72 73 if ($hash !== null && $data[0]['type'] === 'f') { 74 //if hash is given and a page was found 75 $page = $data[0]['id']; 76 $meta = p_get_metadata($page, false, METADATA_RENDER_USING_CACHE); 77 78 if (isset($meta['internal']['toc'])) { 79 $toc = $meta['description']['tableofcontents']; 80 trigger_event('TPL_TOC_RENDER', $toc, null, false); 81 if (is_array($toc) && count($toc) !== 0) { 82 foreach ($toc as &$t) { //loop through toc and compare 83 if ($hash === '' || strpos($t['hid'], $hash) === 0) { 84 $data_r[] = $t; 85 } 86 } 87 $link = $q; 88 } 89 } 90 } else { 91 92 foreach ($data as $entry) { 93 $data_r[] = [ 94 'id' => noNS($entry['id']), 95 'ns' => ($ns_user !== "") ? $ns_user : ':', //return what user has typed in 96 'type' => $entry['type'], // d/f 97 'title' => $entry['title'], 98 'rootns' => $entry['ns'] ? 0 : 1, 99 ]; 100 } 101 } 102 103 echo json_encode([ 104 'data' => $data_r, 105 'link' => $link 106 ]); 107 } 108 109 /** 110 * ajax Request Handler 111 * media_link 112 * 113 * @param $event 114 * @param $param 115 */ 116 function media_link(&$event, $param) { 117 if ($event->data !== 'plugin_imglinksuggest') { 118 return; 119 } 120 //no other ajax call handlers needed 121 $event->stopPropagation(); 122 $event->preventDefault(); 123 124 global $INPUT; 125 126 $page_ns = trim($INPUT->post->str('ns')); //current namespace 127 $q = trim($INPUT->post->str('q')); //entered string 128 129 $ns_user = $ns = getNS($q); //namespace of entered string 130 $id = cleanID(noNS($q)); //media of entered string 131 132 if ($q && trim($q, '.') === '') { //only "." return 133 $data = []; 134 } else if ($ns === '') { // [[:xxx -> absolute link 135 $data = $this->search_medias($ns, $id); 136 } else if ($ns === false && $page_ns) { // [[xxx and not in root-namespace 137 $data = array_merge( 138 $this->search_medias($page_ns, $id),//search in current 139 $this->search_medias('', $id) //and in root 140 ); 141 } else if (strpos($ns, '.') !== false) { //relative link 142 resolve_pageid($page_ns, $ns, $exists); //resolve the ns based on current id 143 $data = $this->search_medias($ns, $id); 144 } else { 145 $data = $this->search_medias($ns, $id); 146 } 147 148 $data_r = []; 149 $link = ''; 150 151 foreach ($data as $entry) { 152 $data_r[] = [ 153 'id' => noNS($entry['id']), 154 'ns' => ($ns_user !== "") ? $ns_user : ':', //return what user has typed in 155 'type' => $entry['type'], // d/f 156 'rootns' => $entry['ns'] ? 0 : 1, 157 ]; 158 } 159 160 echo json_encode([ 161 'data' => $data_r, 162 'link' => $link 163 ]); 164 } 165 166 167 /** 168 * List available pages, and eventually namespaces 169 * 170 * @param string $ns 171 * @param string $id 172 * @param bool $pagesonly 173 * @return array 174 */ 175 protected function search_pages($ns, $id, $pagesonly = false) { 176 global $conf; 177 178 $data = []; 179 $nsd = utf8_encodeFN(str_replace(':', '/', $ns)); //dir 180 181 $opts = [ 182 'depth' => 1, 183 'listfiles' => true, 184 'listdirs' => !$pagesonly, 185 'pagesonly' => true, 186 'firsthead' => true, 187 'sneakyacl' => $conf['sneaky_index'], 188 ]; 189 if ($id) $opts['filematch'] = '^.*\/' . $id; 190 if ($id && !$pagesonly) $opts['dirmatch'] = '^.*\/' . $id; 191 search($data, $conf['datadir'], 'search_universal', $opts, $nsd); 192 193 return $data; 194 } 195 196 /** 197 * List available media 198 * 199 * @param string $ns 200 * @param string $id 201 * @return array 202 */ 203 protected function search_medias($ns, $id) { 204 global $conf; 205 206 $data = []; 207 $nsd = utf8_encodeFN(str_replace(':', '/', $ns)); //dir 208 209 $opts = [ 210 'depth' => 1, 211 'listfiles' => true, 212 'listdirs' => true, 213 'firsthead' => true, 214 'sneakyacl' => $conf['sneaky_index'], 215 ]; 216 if ($id) $opts['filematch'] = '^.*\/' . $id; 217 if ($id) $opts['dirmatch'] = '^.*\/' . $id; 218 search($data, $conf['mediadir'], 'search_universal', $opts, $nsd); 219 220 return $data; 221 } 222 223} 224