1<?php 2 3use dokuwiki\Extension\Plugin; 4use dokuwiki\Logger; 5 6/* 7 * Copyright (c) 2014-2023 Mark C. Prins <mprins@users.sf.net> 8 * 9 * Permission to use, copy, modify, and distribute this software for any 10 * purpose with or without fee is hereby granted, provided that the above 11 * copyright notice and this permission notice appear in all copies. 12 * 13 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 14 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 15 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 16 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 17 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 18 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 19 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 20 */ 21 22/** 23 * DokuWiki Plugin spatialhelper (sitemap Component). 24 * 25 * @license BSD license 26 * @author Mark Prins 27 */ 28class helper_plugin_spatialhelper_sitemap extends Plugin 29{ 30 /** 31 * spatial index. 32 */ 33 private $spatial_idx; 34 35 /** 36 * constructor, load spatial index. 37 */ 38 public function __construct() 39 { 40 global $conf; 41 $idx_dir = $conf['indexdir']; 42 if (!@file_exists($idx_dir . '/spatial.idx')) { 43 $indexer = plugin_load('helper', 'spatialhelper_index'); 44 if ($indexer !== null) { 45 $indexer->generateSpatialIndex(); 46 } 47 } 48 $this->spatial_idx = unserialize( 49 io_readFile($idx_dir . '/spatial.idx', false), 50 ['allowed_classes' => false] 51 ); 52 } 53 54 final public function getMethods(): array 55 { 56 $result[] = [ 57 'name' => 'createGeoRSSSitemap', 58 'desc' => 'create a spatial sitemap in GeoRSS format.', 59 'params' => ['path' => 'string'], 'return' => ['success' => 'boolean'] 60 ]; 61 $result[] = [ 62 'name' => 'createKMLSitemap', 63 'desc' => 'create a spatial sitemap in KML format.', 64 'params' => ['path' => 'string'], 'return' => ['success' => 'boolean'] 65 ]; 66 return $result; 67 } 68 69 /** 70 * Create a GeoRSS Simple sitemap (Atom). 71 * 72 * @param string $mediaID id 73 * for the GeoRSS file 74 */ 75 final public function createGeoRSSSitemap(string $mediaID): bool 76 { 77 global $conf; 78 $namespace = getNS($mediaID); 79 80 $idTag = 'tag:' . parse_url(DOKU_URL, PHP_URL_HOST) . ','; 81 82 $RSSstart = '<?xml version="1.0" encoding="UTF-8"?>' . DOKU_LF; 83 $RSSstart .= '<feed xmlns="http://www.w3.org/2005/Atom" xmlns:georss="http://www.georss.org/georss" '; 84 $RSSstart .= 'xmlns:dc="http://purl.org/dc/elements/1.1/">' . DOKU_LF; 85 $RSSstart .= '<title>' . $conf['title'] . ' spatial feed</title>' . DOKU_LF; 86 if (!empty($conf['tagline'])) { 87 $RSSstart .= '<subtitle>' . $conf['tagline'] . '</subtitle>' . DOKU_LF; 88 } 89 $RSSstart .= '<dc:publisher>' . $conf['title'] . '</dc:publisher>' . DOKU_LF; 90 $RSSstart .= '<link href="' . DOKU_URL . '" />' . DOKU_LF; 91 $RSSstart .= '<link href="' . ml($mediaID, '', true, '&', true) . '" rel="self" />' . DOKU_LF; 92 $RSSstart .= '<updated>' . date(DATE_ATOM) . '</updated>' . DOKU_LF; 93 $RSSstart .= '<id>' . $idTag . date("Y-m-d") . ':' . parse_url(ml($mediaID), PHP_URL_PATH) 94 . '</id>' . DOKU_LF; 95 $RSSstart .= '<rights>' . $conf['license'] . '</rights>' . DOKU_LF; 96 97 $RSSend = '</feed>' . DOKU_LF; 98 99 io_createNamespace($mediaID, 'media'); 100 @touch(mediaFN($mediaID)); 101 @chmod(mediaFN($mediaID), $conf['fmode']); 102 $fh = fopen(mediaFN($mediaID), 'wb'); 103 fwrite($fh, $RSSstart); 104 105 foreach ($this->spatial_idx as $idxEntry) { 106 // get list of id's 107 foreach ($idxEntry as $id) { 108 // for document item in the index 109 if (strpos($id, 'media__') !== 0) { 110 if ($this->skipPage($id, $namespace)) { 111 continue; 112 } 113 114 $meta = p_get_metadata($id); 115 116 // $desc = p_render('xhtmlsummary', p_get_instructions($meta['description']['abstract']), $info); 117 $desc = strip_tags($meta['description']['abstract']); 118 119 $entry = '<entry>' . DOKU_LF; 120 $entry .= ' <title>' . $meta['title'] . '</title>' . DOKU_LF; 121 $entry .= ' <summary>' . $desc . '</summary>' . DOKU_LF; 122 $entry .= ' <georss:point>' . $meta['geo']['lat'] . ' ' . $meta['geo']['lon'] 123 . '</georss:point>' . DOKU_LF; 124 if (!empty($meta['geo']['alt'])) { 125 $entry .= ' <georss:elev>' . $meta['geo']['alt'] . '</georss:elev>' . DOKU_LF; 126 } 127 $entry .= ' <link href="' . wl($id) . '" rel="alternate" type="text/html" />' . DOKU_LF; 128 if (empty($meta['creator'])) { 129 $meta['creator'] = $conf['title']; 130 } 131 $entry .= ' <author><name>' . $meta['creator'] . '</name></author>' . DOKU_LF; 132 $entry .= ' <updated>' . date_iso8601($meta['date']['modified']) . '</updated>' . DOKU_LF; 133 $entry .= ' <published>' . date_iso8601($meta['date']['created']) . '</published>' . DOKU_LF; 134 $entry .= ' <id>' . $idTag . date("Y-m-d", $meta['date']['modified']) . ':' 135 . parse_url(wl($id), PHP_URL_PATH) . '</id>' . DOKU_LF; 136 $entry .= '</entry>' . DOKU_LF; 137 fwrite($fh, $entry); 138 } 139 } 140 } 141 142 fwrite($fh, $RSSend); 143 return fclose($fh); 144 } 145 146 /** 147 * will return true for non-public or hidden pages or pages that are not below or in the namespace. 148 */ 149 private function skipPage(string $id, string $namespace): bool 150 { 151 Logger::debug("helper_plugin_spatialhelper_sitemap::skipPage, skipping $id, not in $namespace"); 152 if (isHiddenPage($id)) { 153 return true; 154 } 155 if (auth_aclcheck($id, '', null) < AUTH_READ) { 156 return true; 157 } 158 159 if (!empty($namespace)) { 160 // only if id is in or below namespace 161 if (0 !== strpos(getNS($id), $namespace)) { 162 return true; 163 } 164 } 165 return false; 166 } 167 168 /** 169 * Create a KML sitemap. 170 * 171 * @param string $mediaID id for the KML file 172 */ 173 final public function createKMLSitemap(string $mediaID): bool 174 { 175 global $conf; 176 $namespace = getNS($mediaID); 177 178 $KMLstart = '<?xml version="1.0" encoding="UTF-8"?>' . DOKU_LF; 179 $KMLstart .= '<kml xmlns="http://www.opengis.net/kml/2.2" '; 180 $KMLstart .= 'xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" '; 181 $KMLstart .= 'xmlns:atom="http://www.w3.org/2005/Atom"'; 182 $KMLstart .= ' xsi:schemaLocation="http://www.opengis.net/kml/2.2 '; 183 $KMLstart .= 'http://schemas.opengis.net/kml/2.2.0/ogckml22.xsd">' . DOKU_LF; 184 $KMLstart .= '<Document id="root_doc">' . DOKU_LF; 185 $KMLstart .= '<name>' . $conf['title'] . ' spatial sitemap</name>' . DOKU_LF; 186 $KMLstart .= '<atom:link href="' . DOKU_URL . '" rel="related" type="text/html" />' . DOKU_LF; 187 $KMLstart .= '<!-- atom:updated>' . date(DATE_ATOM) . '</atom:updated -->' . DOKU_LF; 188 $KMLstart .= '<Style id="icon"><IconStyle><color>ffffffff</color><scale>1</scale>'; 189 $KMLstart .= '<Icon><href>' 190 . DOKU_BASE . 'lib/plugins/spatialhelper/wikiitem.png</href></Icon></IconStyle></Style>' . DOKU_LF; 191 192 $KMLend = '</Document>' . DOKU_LF . '</kml>'; 193 194 io_createNamespace($mediaID, 'media'); 195 @touch(mediaFN($mediaID)); 196 @chmod(mediaFN($mediaID), $conf['fmode']); 197 198 $fh = fopen(mediaFN($mediaID), 'wb'); 199 fwrite($fh, $KMLstart); 200 201 foreach ($this->spatial_idx as $idxEntry) { 202 // get list of id's 203 foreach ($idxEntry as $id) { 204 // for document item in the index 205 if (strpos($id, 'media__') !== 0) { 206 if ($this->skipPage($id, $namespace)) { 207 continue; 208 } 209 210 $meta = p_get_metadata($id); 211 212 // $desc = p_render('xhtmlsummary', p_get_instructions($meta['description']['abstract']), $info); 213 $desc = '<p>' . strip_tags($meta['description']['abstract']) . '</p>'; 214 $desc .= '<p><a href="' . wl($id, '', true) . '">' . $meta['title'] . '</a></p>'; 215 216 // create an entry and store it 217 $plcm = '<Placemark id="crc32-' . hash('crc32', $id) . '">' . DOKU_LF; 218 $plcm .= ' <name>' . $meta['title'] . '</name>' . DOKU_LF; 219 // TODO escape quotes in: title="' . $meta['title'] . '" 220 $plcm .= ' <atom:link href="' . wl($id, '' . true) . '" rel="alternate" type="text/html" />' 221 . DOKU_LF; 222 if (!empty($meta['creator'])) { 223 $plcm .= ' <atom:author><atom:name>' . $meta['creator'] . '</atom:name></atom:author>' 224 . DOKU_LF; 225 } 226 227 $plcm .= ' <description><![CDATA[' . $desc . ']]></description>' . DOKU_LF; 228 $plcm .= ' <styleUrl>#icon</styleUrl>' . DOKU_LF; 229 230 $plcm .= ' <Point><coordinates>' . $meta['geo']['lon'] . ',' . $meta['geo']['lat']; 231 if (!empty($meta['geo']['alt'])) { 232 $plcm .= ',' . $meta['geo']['alt']; 233 } 234 $plcm .= '</coordinates></Point>' . DOKU_LF; 235 $plcm .= '</Placemark>' . DOKU_LF; 236 237 fwrite($fh, $plcm); 238 } 239 } 240 } 241 fwrite($fh, $KMLend); 242 return fclose($fh); 243 } 244} 245