1<?php 2 3use dokuwiki\Extension\ActionPlugin; 4use dokuwiki\Extension\EventHandler; 5use dokuwiki\Extension\Event; 6 7/** 8 * DokuWiki Plugin networkgraph (Action Component) 9 * 10 * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html 11 * @author Feralheart <dokuwiki@feralheart.dev> 12 */ 13class action_plugin_networkgraph_grapher extends ActionPlugin 14{ 15 /** @inheritDoc */ 16 public function register(EventHandler $controller) 17 { 18 $controller->register_hook('INDEXER_PAGE_ADD', 'AFTER', $this, 'rebuildGraphData'); 19 } 20 21 /** 22 * Event handler for INDEXER_TASKS_RUN 23 * 24 * @see https://www.dokuwiki.org/devel:events:INDEXER_TASKS_RUN 25 * @param Event $event Event object 26 * @param mixed $param optional parameter passed when event was registered 27 * @return void 28 */ 29 public function rebuildGraphData(Event $event, $param) 30 { 31 global $conf; 32 33 $namespaces = ['']; 34 $depth = 0; 35 $incmedia = 'ns'; 36 $pages = []; 37 $media = []; 38 $nodes = []; 39 $links = []; 40 foreach($namespaces as $ns) { 41 // find media 42 if($incmedia == 'ns') { 43 $data = []; 44 search( 45 $data, 46 $conf['mediadir'], 47 'search_universal', 48 [ 49 'depth' => $depth, 50 'listfiles' => true, 51 'listdirs' => false, 52 'pagesonly' => false, 53 'skipacl' => true, 54 'keeptxt' => true, 55 'meta' => true, 56 ], 57 str_replace(':', '/', $ns) 58 ); 59 60 // go through all those media files 61 while($item = array_shift($data)) { 62 $media[$item['id']] = [ 63 'title' => noNS($item['id']), 64 'size' => $item['size'], 65 'ns' => getNS($item['id']), 66 'time' => $item['mtime'], 67 ]; 68 } 69 } 70 71 // find pages 72 $data = []; 73 search( 74 $data, 75 $conf['datadir'], 76 'search_universal', 77 [ 78 'depth' => $depth, 79 'listfiles' => true, 80 'listdirs' => false, 81 'pagesonly' => true, 82 'skipacl' => true, 83 'firsthead' => true, 84 'meta' => true, 85 ], 86 str_replace(':', '/', $ns) 87 ); 88 89 // ns start page 90 if($ns && page_exists($ns)) { 91 $data[] = [ 92 'id' => $ns, 93 'ns' => getNS($ns), 94 'title' => p_get_first_heading($ns, false), 95 'size' => filesize(wikiFN($ns)), 96 'mtime' => filemtime(wikiFN($ns)), 97 'perm' => 16, 98 'type' => 'f', 99 'level' => 0, 100 'open' => 1, 101 ]; 102 } 103 104 // go through all those pages 105 while($item = array_shift($data)) { 106 $time = (int) p_get_metadata($item['id'], 'date created', false); 107 if(!$time) $time = $item['mtime']; 108 109 $pages[$item['id']] = [ 110 'title' => $item['title'], 111 'ns' => $item['ns'], 112 'size' => $item['size'], 113 'time' => $time, 114 'links' => [], 115 'media' => [], 116 ]; 117 } 118 } 119 120 // now get links and media 121 foreach($pages as $pid => $item) { 122 // get instructions 123 $ins = p_cached_instructions(wikiFN($pid), false, $pid); 124 // find links and media usage 125 foreach($ins as $i) { 126 $mid = null; 127 128 if($i[0] == 'internallink') { 129 $id = $i[1][0]; 130 $exists = true; 131 resolve_pageid($item['ns'], $id, $exists); 132 list($id) = explode('#', $id, 2); 133 if($id == $pid) continue; // skip self references 134 if($exists && isset($pages[$id])) { 135 $pages[$pid]['links'][] = $id; 136 } 137 if(is_array($i[1][1]) && $i[1][1]['type'] == 'internalmedia') { 138 $mid = $i[1][1]['src']; // image link 139 } else { 140 continue; // we're done here 141 } 142 } 143 144 if($i[0] == 'internalmedia') { 145 $mid = $i[1][0]; 146 } 147 148 if(is_null($mid)) continue; 149 if($incmedia == 'none') continue; // no media wanted 150 151 $exists = true; 152 resolve_mediaid($item['ns'], $mid, $exists); 153 list($mid) = explode('#', $mid, 2); 154 $mid = cleanID($mid); 155 156 if($exists) { 157 if($incmedia == 'all') { 158 if(!isset($media[$mid])) { //add node 159 $media[$mid] = [ 160 'size' => filesize(mediaFN($mid)), 161 'time' => filemtime(mediaFN($mid)), 162 'ns' => getNS($mid), 163 'title' => noNS($mid), 164 ]; 165 } 166 $pages[$pid]['media'][] = $mid; 167 } elseif(isset($media[$mid])) { 168 $pages[$pid]['media'][] = $mid; 169 } 170 } 171 } 172 173 $backlinks = ft_backlinks($pid, true); 174 foreach($backlinks as $backlink) { 175 $pages[$backlink]['links'][] = $pid; 176 } 177 } 178 179 // clean up duplicates then create nodes 180 foreach($pages as $pid => $item) { 181 $pages[$pid]['links'] = array_unique($pages[$pid]['links']); 182 $pages[$pid]['media'] = array_unique($pages[$pid]['media']); 183 184 //create nodes 185 $nodes[] = [ 186 'id' => $pid, 187 'group' => 'page', 188 'label' => $pid."\n".$item['title'] 189 ]; 190 191 //create links 192 foreach($pages[$pid]['links'] as $link) { 193 $links[] = [ 194 'source' => $pid, 195 'target' => $link 196 ]; 197 } 198 199 foreach($pages[$pid]['media'] as $mediaLink) { 200 $links[] = [ 201 'source' => $pid, 202 'target' => $mediaLink 203 ]; 204 } 205 } 206 207 foreach($media as $mid => $item) { 208 $nodes[] = [ 209 'id' => $mid, 210 'group' => 'media', 211 'label' => $mid."\n".$item['title'] 212 ]; 213 } 214 215 $graphData = json_encode(['nodes' => $nodes, 'links' => $links]); 216 $saveFile = metaFN('networkgraph_data', '.json'); 217 io_saveFile($saveFile, json_encode([ 218 'nodes' => $nodes, 219 'links' => $links 220 ])); 221 222 } 223} 224