Completed
Pull Request — master (#23)
by Mark
01:38
created

action.php (4 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\Sitemap\Item;
19
20
/**
21
 * DokuWiki Plugin dokuwikispatial (Action Component).
22
 *
23
 * @license BSD license
24
 * @author  Mark C. Prins <[email protected]>
25
 */
26
class action_plugin_spatialhelper extends DokuWiki_Action_Plugin {
27
28
    /**
29
     * Register for events.
30
     *
31
     * @param Doku_Event_Handler $controller
32
     *          DokuWiki's event controller object. Also available as global $EVENT_HANDLER
33
     */
34
    public function register(Doku_Event_Handler $controller) {
35
        // listen for page add / delete events
36
        // http://www.dokuwiki.org/devel:event:indexer_page_add
37
        $controller->register_hook('INDEXER_PAGE_ADD', 'BEFORE', $this, 'handleIndexerPageAdd');
38
        $controller->register_hook('IO_WIKIPAGE_WRITE', 'BEFORE', $this, 'removeFromIndex');
39
40
        // http://www.dokuwiki.org/devel:event:sitemap_generate
41
        $controller->register_hook('SITEMAP_GENERATE', 'BEFORE', $this, 'handleSitemapGenerateBefore');
42
        // using after will only trigger us if a sitemap was actually created
43
        $controller->register_hook('SITEMAP_GENERATE', 'AFTER', $this, 'handleSitemapGenerateAfter');
44
45
        // handle actions we know of
46
        $controller->register_hook('ACTION_ACT_PREPROCESS', 'BEFORE', $this, 'handleActionActPreprocess', array());
47
        // handle HTML eg. /dokuwiki/doku.php?id=start&do=findnearby&geohash=u15vk4
48
        $controller->register_hook(
49
            'TPL_ACT_UNKNOWN', 'BEFORE', $this, 'findnearby', array(
50
            'format' => 'HTML'
51
        )
52
        );
53
        // handles AJAX/json eg: jQuery.post("/dokuwiki/lib/exe/ajax.php?id=start&call=findnearby&geohash=u15vk4");
54
        $controller->register_hook(
55
            'AJAX_CALL_UNKNOWN', 'BEFORE', $this, 'findnearby', array(
56
            'format' => 'JSON'
57
        )
58
        );
59
60
        // listen for media uploads and deletes
61
        $controller->register_hook('MEDIA_UPLOAD_FINISH', 'BEFORE', $this, 'handleMediaUploaded', array());
62
        $controller->register_hook('MEDIA_DELETE_FILE', 'BEFORE', $this, 'handleMediaDeleted', array());
63
64
        $controller->register_hook('TPL_METAHEADER_OUTPUT', 'BEFORE', $this, 'handleMetaheaderOutput');
65
    }
66
67
    /**
68
     * Update the spatial index for the page.
69
     *
70
     * @param Doku_Event $event
71
     *          event object by reference
72
     * @param object     $param
73
     *          the parameters passed to register_hook when this handler was registered
74
     */
75
    public function handleIndexerPageAdd(Doku_Event $event, $param) {
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);
84
    }
85
86
    /**
87
     * Update the spatial index, removing the page.
88
     *
89
     * @param Doku_Event $event
90
     *          event object by reference
91
     * @param object     $param
92
     *          the parameters passed to register_hook when this handler was registered
93
     */
94
    public function removeFromIndex(Doku_Event &$event, $param) {
95
        // event data:
96
        // $data[0] – The raw arguments for io_saveFile as an array. Do not change file path.
97
        // $data[0][0] – the file path.
98
        // $data[0][1] – the content to be saved, and may be modified.
99
        // $data[1] – ns: The colon separated namespace path minus the trailing page name. (false if root ns)
100
        // $data[2] – page_name: The wiki page name.
101
        // $data[3] – rev: The page revision, false for current wiki pages.
102
103
        dbglog($event->data, "Event data in removeFromIndex.");
104
        if(@file_exists($event->data [0] [0])) {
105
            // file not new
106
            if(!$event->data [0] [1]) {
107
                // file is empty, page is being deleted
108
                if(empty ($event->data [1])) {
109
                    // root namespace
110
                    $id = $event->data [2];
111
                } else {
112
                    $id = $event->data [1] . ":" . $event->data [2];
113
                }
114
                $indexer = &plugin_load('helper', 'spatialhelper_index');
115
                if($indexer) {
116
                    $indexer->deleteFromIndex($id);
117
                }
118
            }
119
        }
120
    }
121
122
    /**
123
     * Add a new SitemapItem object that points to the KML of public geocoded pages.
124
     *
125
     * @param Doku_Event $event
126
     * @param unknown    $param
127
     */
128
    public function handleSitemapGenerateBefore(Doku_Event $event, $param) {
129
        $path                     = mediaFN($this->getConf('media_kml'));
130
        $lastmod                  = @filemtime($path);
131
        $event->data ['items'] [] = new Item(ml($this->getConf('media_kml'), '', true, '&amp;', true), $lastmod);
132
        //dbglog($event->data ['items'],
133
        //  "Added a new SitemapItem object that points to the KML of public geocoded pages.");
134
    }
135
136
    /**
137
     * Create a spatial sitemap or attach the geo/kml map to the sitemap.
138
     *
139
     * @param Doku_Event $event
140
     *          event object by reference, not used
141
     * @param mixed      $param
142
     *          parameter array, not used
143
     */
144
    public function handleSitemapGenerateAfter(Doku_Event $event, $param) {
145
        // $event→data['items']: Array of SitemapItem instances, the array of sitemap items that already
146
        //      contains all public pages of the wiki
147
        // $event→data['sitemap']: The path of the file the sitemap will be saved to.
148
        if($helper = &plugin_load('helper', 'spatialhelper_sitemap')) {
149
            // dbglog($helper, "createSpatialSitemap loaded helper.");
150
151
            $kml = $helper->createKMLSitemap($this->getConf('media_kml'));
152
            $rss = $helper->createGeoRSSSitemap($this->getConf('media_georss'));
153
154
            if(!empty ($this->getConf('sitemap_namespaces'))) {
155
                $namespaces = array_map('trim', explode("\n", $this->getConf('sitemap_namespaces')));
156
                foreach($namespaces as $namespace) {
157
                    $kmlN = $helper->createKMLSitemap($namespace . $this->getConf('media_kml'));
158
                    $rssN = $helper->createGeoRSSSitemap($namespace . $this->getConf('media_georss'));
159
                    dbglog(
160
                        $kmlN && $rssN,
161
                        "handleSitemapGenerateAfter, created KML / GeoRSS sitemap in $namespace, succes: "
162
                    );
163
                }
164
            }
165
            return $kml && $rss;
166
        } else {
167
            dbglog($helper, "createSpatialSitemap NOT loaded helper.");
168
        }
169
    }
170
171
    /**
172
     * trap findnearby action.
173
     * This addional handler is required as described at: https://www.dokuwiki.org/devel:event:tpl_act_unknown
174
     *
175
     * @param Doku_Event $event
176
     *          event object by reference
177
     * @param mixed      $param
178
     *          not used
179
     */
180
    public function handleActionActPreprocess(Doku_Event $event, $param) {
181
        if($event->data != 'findnearby') {
182
            return;
183
        }
184
        $event->preventDefault();
185
    }
186
187
    /**
188
     * handle findnearby action.
189
     *
190
     * @param Doku_Event $event
191
     *          event object by reference
192
     * @param mixed      $param
193
     *          associative array with keys
194
     *          'format'=> HTML | JSON
195
     */
196
    public function findnearby(Doku_Event &$event, $param) {
197
        if($event->data != 'findnearby') {
198
            return;
199
        }
200
        $event->preventDefault();
201
202
        global $INPUT;
203
        if($helper = &plugin_load('helper', 'spatialhelper_search')) {
204
            if($INPUT->has('lat') && $INPUT->has('lon')) {
205
                $results = $helper->findNearbyLatLon($INPUT->param('lat'), $INPUT->param('lon'));
206
            } elseif($INPUT->has('geohash')) {
207
                $results = $helper->findNearby($INPUT->str('geohash'));
208
            } else {
209
                $results = array(
210
                    'error' => hsc($this->getLang('invalidinput'))
211
                );
212
            }
213
        }
214
215
        $showMedia = $INPUT->bool('showMedia', true);
216
217
        switch($param['format']) {
218
            case 'JSON' :
219
                $this->printJSON($results);
0 ignored issues
show
The variable $results does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
220
                break;
221
            case 'HTML' :
222
                // fall through to default
223
            default :
224
                $this->printHTML($results, $showMedia);
225
                break;
226
        }
227
    }
228
229
    /**
230
     * Print seachresults as HTML lists.
231
     *
232
     * @param array $searchresults
233
     */
234
    private function printJSON($searchresults) {
235
        require_once DOKU_INC . 'inc/JSON.php';
236
        $json = new JSON();
237
        header('Content-Type: application/json');
238
        print $json->encode($searchresults);
239
    }
240
241
    /**
242
     * Print seachresults as HTML lists.
243
     *
244
     * @param array   $searchresults
245
     * @param boolean $showMedia
246
     */
247
    private function printHTML($searchresults, $showMedia = true) {
248
        $pages   = (array) ($searchresults ['pages']);
249
        $media   = (array) $searchresults ['media'];
250
        $lat     = (float) $searchresults ['lat'];
251
        $lon     = (float) $searchresults ['lon'];
252
        $geohash = (string) $searchresults ['geohash'];
253
254
        if(isset ($searchresults ['error'])) {
255
            print '<div class="level1"><p>' . hsc($results ['error']) . '</p></div>';
0 ignored issues
show
The variable $results does not exist. Did you mean $searchresults?

This check looks for variables that are accessed but have not been defined. It raises an issue if it finds another variable that has a similar name.

The variable may have been renamed without also renaming all references.

Loading history...
256
            return;
257
        }
258
259
        // print a HTML list
260
        print '<h1>' . $this->getLang('results_header') . '</h1>' . DOKU_LF;
261
        print '<div class="level1">' . DOKU_LF;
262
        if(!empty ($pages)) {
263
            $pagelist = '<ol>' . DOKU_LF;
264
            foreach($pages as $page) {
265
                $pagelist .= '<li>' . html_wikilink(
266
                        ':' . $page ['id'], useHeading('navigation') ? null :
267
                        noNS($page ['id'])
268
                    ) . ' (' . $this->getLang('results_distance_prefix')
269
                    . $page ['distance'] . '&nbsp;m) ' . $page ['description'] . '</li>' . DOKU_LF;
270
            }
271
            $pagelist .= '</ol>' . DOKU_LF;
272
273
            print '<h2>' . $this->getLang('results_pages') . hsc(
274
                    ' lat;lon: ' . $lat . ';' . $lon
275
                    . ' (geohash: ' . $geohash . ')'
276
                ) . '</h2>';
277
            print '<div class="level2">' . DOKU_LF;
278
            print $pagelist;
279
            print '</div>' . DOKU_LF;
280
        } else {
281
            print '<p>' . hsc($this->getLang('nothingfound')) . '</p>';
282
        }
283
284
        if(!empty ($media) && $showMedia) {
285
            $pagelist = '<ol>' . DOKU_LF;
286
            foreach($media as $m) {
287
                $opts       = array();
288
                $link       = ml($m ['id'], $opts, false, '&amp;', false);
289
                $opts ['w'] = '100';
290
                $src        = ml($m ['id'], $opts);
291
                $pagelist   .= '<li><a href="' . $link . '"><img src="' . $src . '"></a> ('
292
                    . $this->getLang('results_distance_prefix') . $page ['distance'] . '&nbsp;m) ' . hsc($desc)
293
                    . '</li>' . DOKU_LF;
294
            }
295
            $pagelist .= '</ol>' . DOKU_LF;
296
297
            print '<h2>' . $this->getLang('results_media') . hsc(
298
                    ' lat;lon: ' . $lat . ';' . $lon
299
                    . ' (geohash: ' . $geohash . ')'
300
                ) . '</h2>' . DOKU_LF;
301
            print '<div class="level2">' . DOKU_LF;
302
            print $pagelist;
303
            print '</div>' . DOKU_LF;
304
        }
305
        print '<p>' . $this->getLang('results_precision') . $searchresults ['precision'] . ' m. ';
306
        if(strlen($geohash) > 1) {
307
            $url = wl(
308
                getID(), array(
309
                'do'      => 'findnearby',
310
                'geohash' => substr($geohash, 0, -1)
311
            )
312
            );
313
            print '<a href="' . $url . '" class="findnearby">' . $this->getLang('search_largerarea') . '</a>.</p>'
314
                . DOKU_LF;
315
        }
316
        print '</div>' . DOKU_LF;
317
    }
318
319
    /**
320
     * add media to spatial index.
321
     *
322
     * @param Doku_Event $event
323
     *          event object by reference
324
     * @param unknown    $param
325
     */
326
    public function handleMediaUploaded(Doku_Event &$event, $param) {
327
        // data[0] temporary file name (read from $_FILES)
328
        // data[1] file name of the file being uploaded
329
        // data[2] future directory id of the file being uploaded
330
        // data[3] the mime type of the file being uploaded
331
        // data[4] true if the uploaded file exists already
332
        // data[5] (since 2011-02-06) the PHP function used to move the file to the correct location
333
334
        dbglog($event->data, "handleMediaUploaded::event data");
335
336
        // check the list of mimetypes
337
        // if it's a supported type call appropriate index function
338
        if(substr_compare($event->data [3], 'image/jpeg', 0)) {
339
            $indexer = plugin_load('helper', 'spatialhelper_index');
340
            if($indexer) {
341
                $indexer->indexImage($event->data [2], $event->data [1]);
342
            }
343
        }
344
        // TODO add image/tiff
345
        // TODO kml, gpx, geojson...
346
    }
347
348
    /**
349
     * removes the media from the index.
350
     */
351
    public function handleMediaDeleted(Doku_Event &$event, $param) {
352
        // data['id'] ID data['unl'] unlink return code
353
        // data['del'] Namespace directory unlink return code
354
        // data['name'] file name data['path'] full path to the file
355
        // data['size'] file size
356
357
        dbglog($event->data, "handleMediaDeleted::event data");
358
359
        // remove the media id from the index
360
        $indexer = &plugin_load('helper', 'spatialhelper_index');
361
        if($indexer) {
362
            $indexer->deleteFromIndex('media__' . $event->data ['id']);
363
        }
364
    }
365
366
    /**
367
     * add a link to the spatial sitemap files in the header.
368
     *
369
     * @param Doku_Event   $event
370
     *          the DokuWiki event. $event->data is a two-dimensional
371
     *          array of all meta headers. The keys are meta, link and script.
372
     * @param unknown_type $param
373
     *
374
     * @see http://www.dokuwiki.org/devel:event:tpl_metaheader_output
375
     */
376
    public function handleMetaheaderOutput(Doku_Event $event, $param) {
377
        // TODO maybe test for exist
378
        $event->data ["link"] [] = array(
379
            "type"  => "application/atom+xml",
380
            "rel"   => "alternate",
381
            "href"  => ml($this->getConf('media_georss')),
382
            "title" => "Spatial ATOM Feed"
383
        );
384
        $event->data ["link"] [] = array(
385
            "type"  => "application/vnd.google-earth.kml+xml",
386
            "rel"   => "alternate",
387
            "href"  => ml($this->getConf('media_kml')),
388
            "title" => "KML Sitemap"
389
        );
390
    }
391
392
    /**
393
     * Calculate a new coordinate based on start, distance and bearing
394
     *
395
     * @param $start array
396
     *               - start coordinate as decimal lat/lon pair
397
     * @param $dist  float
398
     *               - distance in kilometers
399
     * @param $brng  float
400
     *               - bearing in degrees (compass direction)
401
     */
402
    private function geoDestination($start, $dist, $brng) {
403
        $lat1 = toRad($start [0]);
404
        $lon1 = toRad($start [1]);
405
        // http://en.wikipedia.org/wiki/Earth_radius
406
        // average earth radius in km
407
        $dist = $dist / 6371.01;
408
        $brng = toRad($brng);
409
410
        $lon2 = $lon1 + atan2(sin($brng) * sin($dist) * cos($lat1), cos($dist) - sin($lat1) * sin($lat2));
411
        $lon2 = fmod(($lon2 + 3 * pi()), (2 * pi())) - pi();
412
413
        return array(
414
            toDeg($lat2),
415
            toDeg($lon2)
416
        );
417
    }
418
419
    private function toRad($deg) {
0 ignored issues
show
This method is not used, and could be removed.
Loading history...
420
        return $deg * pi() / 180;
421
    }
422
423
    private function toDeg($rad) {
0 ignored issues
show
This method is not used, and could be removed.
Loading history...
424
        return $rad * 180 / pi();
425
    }
426
}
427