Completed
Push — master ( 99b5fb...d8316d )
by Mark
24s queued 11s
created

action.php (5 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
/*
3
 * Copyright (c) 2011-2020 Mark C. Prins <[email protected]>
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
use dokuwiki\Logger;
19
use dokuwiki\Sitemap\Item;
20
21
/**
22
 * DokuWiki Plugin dokuwikispatial (Action Component).
23
 *
24
 * @license BSD license
25
 * @author  Mark C. Prins <[email protected]>
26
 */
27
class action_plugin_spatialhelper extends DokuWiki_Action_Plugin
28
{
29
30
    /**
31
     * Register for events.
32
     *
33
     * @param Doku_Event_Handler $controller
34
     *          DokuWiki's event controller object. Also available as global $EVENT_HANDLER
35
     */
36
    public function register(Doku_Event_Handler $controller): void
37
    {
38
        // listen for page add / delete events
39
        // http://www.dokuwiki.org/devel:event:indexer_page_add
40
        $controller->register_hook('INDEXER_PAGE_ADD', 'BEFORE', $this, 'handleIndexerPageAdd');
41
        $controller->register_hook('IO_WIKIPAGE_WRITE', 'BEFORE', $this, 'removeFromIndex');
42
43
        // http://www.dokuwiki.org/devel:event:sitemap_generate
44
        $controller->register_hook('SITEMAP_GENERATE', 'BEFORE', $this, 'handleSitemapGenerateBefore');
45
        // using after will only trigger us if a sitemap was actually created
46
        $controller->register_hook('SITEMAP_GENERATE', 'AFTER', $this, 'handleSitemapGenerateAfter');
47
48
        // handle actions we know of
49
        $controller->register_hook('ACTION_ACT_PREPROCESS', 'BEFORE', $this, 'handleActionActPreprocess', array());
50
        // handle HTML eg. /dokuwiki/doku.php?id=start&do=findnearby&geohash=u15vk4
51
        $controller->register_hook(
52
            'TPL_ACT_UNKNOWN', 'BEFORE', $this, 'findnearby', array('format' => 'HTML')
53
        );
54
        // handles AJAX/json eg: jQuery.post("/dokuwiki/lib/exe/ajax.php?id=start&call=findnearby&geohash=u15vk4");
55
        $controller->register_hook(
56
            'AJAX_CALL_UNKNOWN', 'BEFORE', $this, 'findnearby', array('format' => 'JSON')
57
        );
58
59
        // listen for media uploads and deletes
60
        $controller->register_hook('MEDIA_UPLOAD_FINISH', 'BEFORE', $this, 'handleMediaUploaded', array());
61
        $controller->register_hook('MEDIA_DELETE_FILE', 'BEFORE', $this, 'handleMediaDeleted', array());
62
63
        $controller->register_hook('TPL_METAHEADER_OUTPUT', 'BEFORE', $this, 'handleMetaheaderOutput');
64
    }
65
66
    /**
67
     * Update the spatial index for the page.
68
     *
69
     * @param Doku_Event $event
70
     *          event object
71
     * @param mixed $param
72
     *          the parameters passed to register_hook when this handler was registered
73
     */
74
    public function handleIndexerPageAdd(Doku_Event $event, $param): void
75
    {
76
        // $event→data['page'] – the page id
77
        // $event→data['body'] – empty, can be filled by additional content to index by your plugin
78
        // $event→data['metadata'] – the metadata that shall be indexed. This is an array where the keys are the
79
        //    metadata indexes and the value a string or an array of strings with the values.
80
        //    title and relation_references will already be set.
81
        $id = $event->data ['page'];
82
        $indexer = plugin_load('helper', 'spatialhelper_index');
83
        $entries = $indexer->updateSpatialIndex($id);
0 ignored issues
show
$entries is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
84
    }
85
86
    /**
87
     * Update the spatial index, removing the page.
88
     *
89
     * @param Doku_Event $event
90
     *          event object
91
     * @param mixed $param
92
     *          the parameters passed to register_hook when this handler was registered
93
     */
94
    public function removeFromIndex(Doku_Event $event, $param): void
95
    {
96
        // event data:
97
        // $data[0] – The raw arguments for io_saveFile as an array. Do not change file path.
98
        // $data[0][0] – the file path.
99
        // $data[0][1] – the content to be saved, and may be modified.
100
        // $data[1] – ns: The colon separated namespace path minus the trailing page name. (false if root ns)
101
        // $data[2] – page_name: The wiki page name.
102
        // $data[3] – rev: The page revision, false for current wiki pages.
103
104
        Logger::debug("Event data in removeFromIndex.", $event->data);
105
        if (@file_exists($event->data [0] [0])) {
106
            // file not new
107
            if (!$event->data [0] [1]) {
108
                // file is empty, page is being deleted
109
                if (empty ($event->data [1])) {
110
                    // root namespace
111
                    $id = $event->data [2];
112
                } else {
113
                    $id = $event->data [1] . ":" . $event->data [2];
114
                }
115
                $indexer = plugin_load('helper', 'spatialhelper_index');
116
                if ($indexer) {
117
                    $indexer->deleteFromIndex($id);
118
                }
119
            }
120
        }
121
    }
122
123
    /**
124
     * Add a new SitemapItem object that points to the KML of public geocoded pages.
125
     *
126
     * @param Doku_Event $event
127
     * @param mixed $param
128
     */
129
    public function handleSitemapGenerateBefore(Doku_Event $event, $param): void
130
    {
131
        $path = mediaFN($this->getConf('media_kml'));
132
        $lastmod = @filemtime($path);
133
        $event->data ['items'] [] = new Item(
134
            ml($this->getConf('media_kml'),
135
                '',
136
                true,
137
                '&amp;',
138
                true),
139
            $lastmod
140
        );
141
    }
142
143
    /**
144
     * Create a spatial sitemap or attach the geo/kml map to the sitemap.
145
     *
146
     * @param Doku_Event $event
147
     *          event object, not used
148
     * @param mixed $param
149
     *          parameter array, not used
150
     */
151
    public function handleSitemapGenerateAfter(Doku_Event $event, $param): bool
152
    {
153
        // $event→data['items']: Array of SitemapItem instances, the array of sitemap items that already
154
        //      contains all public pages of the wiki
155
        // $event→data['sitemap']: The path of the file the sitemap will be saved to.
156
        if ($helper = plugin_load('helper', 'spatialhelper_sitemap')) {
157
            $kml = $helper->createKMLSitemap($this->getConf('media_kml'));
158
            $rss = $helper->createGeoRSSSitemap($this->getConf('media_georss'));
159
160
            if (!empty ($this->getConf('sitemap_namespaces'))) {
161
                $namespaces = array_map(
162
                    'trim',
163
                    explode("\n",
164
                        $this->getConf('sitemap_namespaces'))
165
                );
166
                foreach ($namespaces as $namespace) {
167
                    $kmlN = $helper->createKMLSitemap($namespace . $this->getConf('media_kml'));
168
                    $rssN = $helper->createGeoRSSSitemap($namespace . $this->getConf('media_georss'));
169
                    Logger::debug(
170
                        "handleSitemapGenerateAfter, created KML / GeoRSS sitemap in $namespace, succes: ",
171
                        $kmlN && $rssN
172
                    );
173
                }
174
            }
175
            return $kml && $rss;
176
        } else {
177
            Logger::debug("createSpatialSitemap NOT loaded helper.", $helper);
178
        }
179
    }
180
181
    /**
182
     * trap findnearby action.
183
     * This addional handler is required as described at: https://www.dokuwiki.org/devel:event:tpl_act_unknown
184
     *
185
     * @param Doku_Event $event
186
     *          event object
187
     * @param mixed $param
188
     *          not used
189
     */
190
    public function handleActionActPreprocess(Doku_Event $event, $param): void
191
    {
192
        if ($event->data !== 'findnearby') {
193
            return;
194
        }
195
        $event->preventDefault();
196
    }
197
198
    /**
199
     * handle findnearby action.
200
     *
201
     * @param Doku_Event $event
202
     *          event object
203
     * @param mixed $param
204
     *          associative array with keys
205
     *          'format'=> HTML | JSON
206
     */
207
    public function findnearby(Doku_Event $event, $param): void
208
    {
209
        if ($event->data !== 'findnearby') {
210
            return;
211
        }
212
        $event->preventDefault();
213
        $results = array();
214
        global $INPUT;
215
        if ($helper = plugin_load('helper', 'spatialhelper_search')) {
216
            if ($INPUT->has('lat') && $INPUT->has('lon')) {
217
                $results = $helper->findNearbyLatLon($INPUT->param('lat'), $INPUT->param('lon'));
218
            } elseif ($INPUT->has('geohash')) {
219
                $results = $helper->findNearby($INPUT->str('geohash'));
220
            } else {
221
                $results = array(
222
                    'error' => hsc($this->getLang('invalidinput'))
223
                );
224
            }
225
        }
226
227
        $showMedia = $INPUT->bool('showMedia', true);
228
229
        switch ($param['format']) {
230
            case 'JSON' :
231
                $this->printJSON($results);
232
                break;
233
            case 'HTML' :
234
                // fall through to default
235
            default :
236
                $this->printHTML($results, $showMedia);
237
                break;
238
        }
239
    }
240
241
    /**
242
     * Print seachresults as HTML lists.
243
     *
244
     * @param array $searchresults
245
     */
246
    private function printJSON(array $searchresults): void
247
    {
248
        require_once DOKU_INC . 'inc/JSON.php';
249
        $json = new JSON();
250
        header('Content-Type: application/json');
251
        print $json->encode($searchresults);
252
    }
253
254
    /**
255
     * Print seachresults as HTML lists.
256
     *
257
     * @param array $searchresults
258
     * @param bool $showMedia
259
     */
260
    private function printHTML(array $searchresults, bool $showMedia = true): void
261
    {
262
        $pages = (array)($searchresults ['pages']);
263
        $media = (array)$searchresults ['media'];
264
        $lat = (float)$searchresults ['lat'];
265
        $lon = (float)$searchresults ['lon'];
266
        $geohash = (string)$searchresults ['geohash'];
267
268
        if (isset ($searchresults ['error'])) {
269
            print '<div class="level1"><p>' . hsc($searchresults ['error']) . '</p></div>';
270
            return;
271
        }
272
273
        // print a HTML list
274
        print '<h1>' . $this->getLang('results_header') . '</h1>' . DOKU_LF;
275
        print '<div class="level1">' . DOKU_LF;
276
        if (!empty ($pages)) {
277
            $pagelist = '<ol>' . DOKU_LF;
278
            foreach ($pages as $page) {
279
                $pagelist .= '<li>' . html_wikilink(
280
                        ':' . $page ['id'], useHeading('navigation') ? null :
281
                        noNS($page ['id'])
282
                    ) . ' (' . $this->getLang('results_distance_prefix')
283
                    . $page ['distance'] . '&nbsp;m) ' . $page ['description'] . '</li>' . DOKU_LF;
284
            }
285
            $pagelist .= '</ol>' . DOKU_LF;
286
287
            print '<h2>' . $this->getLang('results_pages') . hsc(
288
                    ' lat;lon: ' . $lat . ';' . $lon
289
                    . ' (geohash: ' . $geohash . ')'
290
                ) . '</h2>';
291
            print '<div class="level2">' . DOKU_LF;
292
            print $pagelist;
293
            print '</div>' . DOKU_LF;
294
        } else {
295
            print '<p>' . hsc($this->getLang('nothingfound')) . '</p>';
296
        }
297
298
        if (!empty ($media) && $showMedia) {
299
            $pagelist = '<ol>' . DOKU_LF;
300
            foreach ($media as $m) {
301
                $opts = array();
302
                $link = ml($m ['id'], $opts, false, '&amp;', false);
303
                $opts ['w'] = '100';
304
                $src = ml($m ['id'], $opts);
305
                $pagelist .= '<li><a href="' . $link . '"><img src="' . $src . '"></a> ('
306
                    . $this->getLang('results_distance_prefix') . $page ['distance'] . '&nbsp;m) ' . hsc($desc)
0 ignored issues
show
The variable $page seems to be defined by a foreach iteration on line 278. Are you sure the iterator is never empty, otherwise this variable is not defined?

It seems like you are relying on a variable being defined by an iteration:

foreach ($a as $b) {
}

// $b is defined here only if $a has elements, for example if $a is array()
// then $b would not be defined here. To avoid that, we recommend to set a
// default value for $b.


// Better
$b = 0; // or whatever default makes sense in your context
foreach ($a as $b) {
}

// $b is now guaranteed to be defined here.
Loading history...
The variable $desc does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
307
                    . '</li>' . DOKU_LF;
308
            }
309
            $pagelist .= '</ol>' . DOKU_LF;
310
311
            print '<h2>' . $this->getLang('results_media') . hsc(
312
                    ' lat;lon: ' . $lat . ';' . $lon
313
                    . ' (geohash: ' . $geohash . ')'
314
                ) . '</h2>' . DOKU_LF;
315
            print '<div class="level2">' . DOKU_LF;
316
            print $pagelist;
317
            print '</div>' . DOKU_LF;
318
        }
319
        print '<p>' . $this->getLang('results_precision') . $searchresults ['precision'] . ' m. ';
320
        if (strlen($geohash) > 1) {
321
            $url = wl(
322
                getID(), array(
323
                    'do' => 'findnearby',
324
                    'geohash' => substr($geohash, 0, -1)
325
                )
326
            );
327
            print '<a href="' . $url . '" class="findnearby">' . $this->getLang('search_largerarea') . '</a>.</p>'
328
                . DOKU_LF;
329
        }
330
        print '</div>' . DOKU_LF;
331
    }
332
333
    /**
334
     * add media to spatial index.
335
     *
336
     * @param Doku_Event $event
337
     * @param mixed $param
338
     */
339
    public function handleMediaUploaded(Doku_Event $event, $param): void
340
    {
341
        // data[0] temporary file name (read from $_FILES)
342
        // data[1] file name of the file being uploaded
343
        // data[2] future directory id of the file being uploaded
344
        // data[3] the mime type of the file being uploaded
345
        // data[4] true if the uploaded file exists already
346
        // data[5] (since 2011-02-06) the PHP function used to move the file to the correct location
347
348
        Logger::debug("handleMediaUploaded::event data", $event->data);
349
350
        // check the list of mimetypes
351
        // if it's a supported type call appropriate index function
352
        if (substr_compare($event->data [3], 'image/jpeg', 0)) {
353
            $indexer = plugin_load('helper', 'spatialhelper_index');
354
            if ($indexer) {
355
                $indexer->indexImage($event->data [2], $event->data [1]);
356
            }
357
        }
358
        // TODO add image/tiff
359
        // TODO kml, gpx, geojson...
360
    }
361
362
    /**
363
     * removes the media from the index.
364
     */
365
    public function handleMediaDeleted(Doku_Event $event, $param): void
366
    {
367
        // data['id'] ID data['unl'] unlink return code
368
        // data['del'] Namespace directory unlink return code
369
        // data['name'] file name data['path'] full path to the file
370
        // data['size'] file size
371
372
        Logger::debug("handleMediaDeleted::event data", $event->data);
373
374
        // remove the media id from the index
375
        $indexer = plugin_load('helper', 'spatialhelper_index');
376
        if ($indexer) {
377
            $indexer->deleteFromIndex('media__' . $event->data ['id']);
378
        }
379
    }
380
381
    /**
382
     * add a link to the spatial sitemap files in the header.
383
     *
384
     * @param Doku_Event $event
385
     *          the DokuWiki event. $event->data is a two-dimensional
386
     *          array of all meta headers. The keys are meta, link and script.
387
     * @param mixed $param
388
     *
389
     * @see http://www.dokuwiki.org/devel:event:tpl_metaheader_output
390
     */
391
    public function handleMetaheaderOutput(Doku_Event $event, $param): void
392
    {
393
        // TODO maybe test for exist
394
        $event->data ["link"] [] = array(
395
            "type" => "application/atom+xml",
396
            "rel" => "alternate",
397
            "href" => ml($this->getConf('media_georss')),
398
            "title" => "Spatial ATOM Feed"
399
        );
400
        $event->data ["link"] [] = array(
401
            "type" => "application/vnd.google-earth.kml+xml",
402
            "rel" => "alternate",
403
            "href" => ml($this->getConf('media_kml')),
404
            "title" => "KML Sitemap"
405
        );
406
    }
407
408
    /**
409
     * Calculate a new coordinate based on start, distance and bearing
410
     *
411
     * @param $start array
412
     *               - start coordinate as decimal lat/lon pair
413
     * @param $dist  float
414
     *               - distance in kilometers
415
     * @param $brng  float
416
     *               - bearing in degrees (compass direction)
417
     */
418
    private function geoDestination(array $start, float $dist, float $brng): array
0 ignored issues
show
This method is not used, and could be removed.
Loading history...
419
    {
420
        $lat1 = $this->toRad($start [0]);
421
        $lon1 = $this->toRad($start [1]);
422
        // http://en.wikipedia.org/wiki/Earth_radius
423
        // average earth radius in km
424
        $dist = $dist / 6371.01;
425
        $brng = $this->toRad($brng);
426
427
        $lon2 = $lon1 + atan2(sin($brng) * sin($dist) * cos($lat1), cos($dist) - sin($lat1) * sin($lat2));
0 ignored issues
show
The variable $lat2 does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
428
        $lon2 = fmod(($lon2 + 3 * M_PI), (2 * M_PI)) - M_PI;
429
430
        return array(
431
            $this->toDeg($lat2),
432
            $this->toDeg($lon2)
433
        );
434
    }
435
436
    private function toRad(float $deg): float
437
    {
438
        return $deg * M_PI / 180;
439
    }
440
441
    private function toDeg(float $rad): float
442
    {
443
        return $rad * 180 / M_PI;
444
    }
445
}
446