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