Completed
Push — master ( 4ff427...097360 )
by Mark
02:38
created

action.php (10 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-2016 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
if (!defined('DOKU_INC'))
18
	die ();
19
if (!defined('DOKU_PLUGIN'))
20
	define('DOKU_PLUGIN', DOKU_INC . 'lib/plugins/');
21
if (!defined('DOKU_LF'))
22
	define('DOKU_LF', "\n");
23
require_once (DOKU_PLUGIN . 'action.php');
24
25
/**
26
 * DokuWiki Plugin dokuwikispatial (Action Component).
27
 *
28
 * @license BSD license
29
 * @author Mark C. Prins <[email protected]>
30
 */
31
class action_plugin_spatialhelper extends DokuWiki_Action_Plugin {
32
33
	/**
34
	 * Register for events.
35
	 *
36
	 * @param Doku_Event_Handler $controller
37
	 *        	DokuWiki's event controller object. Also available as global $EVENT_HANDLER
38
	 */
39
	public function register(Doku_Event_Handler $controller) {
40
		// listen for page add / delete events
41
		// http://www.dokuwiki.org/devel:event:indexer_page_add
42
		$controller->register_hook('INDEXER_PAGE_ADD', 'BEFORE', $this, 'handle_indexer_page_add');
43
		$controller->register_hook('IO_WIKIPAGE_WRITE', 'BEFORE', $this, '_removeFromIndex');
44
45
		// http://www.dokuwiki.org/devel:event:sitemap_generate
46
		$controller->register_hook('SITEMAP_GENERATE', 'BEFORE', $this, 'handle_sitemap_generate_before');
47
		// using after will only trigger us if a sitemap was actually created
48
		$controller->register_hook('SITEMAP_GENERATE', 'AFTER', $this, 'handle_sitemap_generate_after');
49
50
		// handle actions we know of
51
		$controller->register_hook('ACTION_ACT_PREPROCESS', 'BEFORE', $this, 'handle_action_act_preprocess', array());
52
		// handle HTML eg. /dokuwiki/doku.php?id=start&do=findnearby&geohash=u15vk4
53
		$controller->register_hook('TPL_ACT_UNKNOWN', 'BEFORE', $this, '_findnearby', array(
54
				'format' => 'HTML'
55
		));
56
		// handles AJAX/json eg: jQuery.post("/dokuwiki/lib/exe/ajax.php?id=start&call=findnearby&geohash=u15vk4");
57
		$controller->register_hook('AJAX_CALL_UNKNOWN', 'BEFORE', $this, '_findnearby', array(
58
				'format' => 'JSON'
59
		));
60
61
		// listen for media uploads and deletes
62
		$controller->register_hook('MEDIA_UPLOAD_FINISH', 'BEFORE', $this, '_handle_media_uploaded', array());
63
		$controller->register_hook('MEDIA_DELETE_FILE', 'BEFORE', $this, '_handle_media_deleted', array());
64
65
		$controller->register_hook('TPL_METAHEADER_OUTPUT', 'BEFORE', $this, 'handle_metaheader_output');
66
	}
67
68
	/**
69
	 * Update the spatial index for the page.
70
	 *
71
	 * @param Doku_Event $event
72
	 *        	event object by reference
73
	 * @param object $param
74
	 *        	the parameters passed to register_hook when this handler was registered
75
	 */
76
	function handle_indexer_page_add(Doku_Event $event, $param) {
77
		// $event→data['page'] – the page id
78
		// $event→data['body'] – empty, can be filled by additional content to index by your plugin
79
		// $event→data['metadata'] – the metadata that shall be indexed. This is an array where the keys are the metadata indexes and the value a string or an array of strings with the values. title and relation_references will already be set.
80
		$id = $event->data ['page'];
81
		$indexer = & plugin_load('helper', 'spatialhelper_index');
82
		$entries = $indexer->updateSpatialIndex($id);
83
	}
84
85
	/**
86
	 * Update the spatial index, removing the page.
87
	 *
88
	 * @param Doku_Event $event
89
	 *        	event object by reference
90
	 * @param object $param
91
	 *        	the parameters passed to register_hook when this handler was registered
92
	 */
93
	function _removeFromIndex(Doku_Event & $event, $param) {
94
		// event data:
95
		// $data[0] – The raw arguments for io_saveFile as an array. Do not change file path.
96
		// $data[0][0] – the file path.
1 ignored issue
show
Unused Code Comprehensibility introduced by
42% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
97
		// $data[0][1] – the content to be saved, and may be modified.
98
		// $data[1] – ns: The colon separated namespace path minus the trailing page name. (false if root ns)
99
		// $data[2] – page_name: The wiki page name.
100
		// $data[3] – rev: The page revision, false for current wiki pages.
101
102
		// dbglog ( $event->data, "Event data in _removeFromIndex." );
1 ignored issue
show
Unused Code Comprehensibility introduced by
50% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
103
		if (@file_exists($event->data [0] [0])) {
104
			// file not new
105
			if (!$event->data [0] [1]) {
106
				// file is empty, page is being deleted
107
				if (empty ($event->data [1])) {
108
					// root namespace
109
					$id = $event->data [2];
110
				} else {
111
					$id = $event->data [1] . ":" . $event->data [2];
112
				}
113
				$indexer = & plugin_load('helper', 'spatialhelper_index');
114
				if ($indexer) {
115
					$indexer->deleteFromIndex($id);
116
				}
117
			}
118
		}
119
	}
120
121
	/**
122
	 * Add a new SitemapItem object that points to the KML of public geocoded pages.
123
	 *
124
	 * @param Doku_Event $event
125
	 * @param unknown $param
126
	 */
127
	function handle_sitemap_generate_before(Doku_Event $event, $param) {
128
		$path = mediaFN($this->getConf('media_kml'));
129
		$lastmod = @filemtime($path);
130
		$event->data ['items'] [] = new SitemapItem(ml($this->getConf('media_kml'), '', true, '&amp;', true), $lastmod);
131
		// dbglog ( $event->data ['items'], "Added a new SitemapItem object that points to the KML of public geocoded pages." );
1 ignored issue
show
Unused Code Comprehensibility introduced by
56% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
132
	}
133
134
	/**
135
	 * Create a spatial sitemap or attach the geo/kml map to the sitemap.
136
	 *
137
	 * @param Doku_Event $event
138
	 *        	event object by reference, not used
139
	 * @param mixed $param
140
	 *        	parameter array, not used
141
	 */
142
	function handle_sitemap_generate_after(Doku_Event $event, $param) {
143
		// $event→data['items']: Array of SitemapItem instances, the array of sitemap items that already contains all public pages of the wiki
144
		// $event→data['sitemap']: The path of the file the sitemap will be saved to.
145
146
		// dbglog ($event->data['items'], "createSpatialSitemap loading helper" );
1 ignored issue
show
Unused Code Comprehensibility introduced by
63% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
147
		if ($helper = & plugin_load('helper', 'spatialhelper_sitemap')) {
148
			// dbglog ( $helper, "createSpatialSitemap loaded helper." );
0 ignored issues
show
Unused Code Comprehensibility introduced by
50% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
149
150
			$kml = $helper->createKMLSitemap($this->getConf('media_kml'));
151
			$rss = $helper->createGeoRSSSitemap($this->getConf('media_georss'));
152
153
			return $kml && $rss;
154
		} else {
0 ignored issues
show
This else statement is empty and can be removed.

This check looks for the else branches of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These else branches can be removed.

if (rand(1, 6) > 3) {
print "Check failed";
} else {
    //print "Check succeeded";
}

could be turned into

if (rand(1, 6) > 3) {
    print "Check failed";
}

This is much more concise to read.

Loading history...
155
			// dbglog ( $helper, "createSpatialSitemap NOT loaded helper." );
0 ignored issues
show
Unused Code Comprehensibility introduced by
50% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
156
		}
157
	}
158
159
	/**
160
	 * trap findnearby action.
161
	 * This addional handler is required as described at: https://www.dokuwiki.org/devel:event:tpl_act_unknown
162
	 *
163
	 * @param Doku_Event $event
164
	 *        	event object by reference
165
	 * @param mixed $param
166
	 *        	not used
167
	 */
168
	function handle_action_act_preprocess(Doku_Event $event, $param) {
169
		if ($event->data != 'findnearby')
170
			return;
171
		$event->preventDefault();
172
	}
173
174
	/**
175
	 * handle findnearby action.
176
	 *
177
	 * @param Doku_Event $event
178
	 *        	event object by reference
179
	 * @param mixed $param
180
	 *        	associative array with keys
181
	 *        	'format'=> HTML | JSON
182
	 */
183
	function _findnearby(Doku_Event & $event, $param) {
184
		if ($event->data != 'findnearby')
185
			return;
186
		$event->preventDefault();
187
188
		global $INPUT;
189
		if ($helper = & plugin_load('helper', 'spatialhelper_search')) {
190
			if ($INPUT->has('lat') && $INPUT->has('lon')) {
191
				$results = $helper->findNearbyLatLon($INPUT->param('lat'), $INPUT->param('lon'));
192
			} elseif ($INPUT->has('geohash')) {
193
				$results = $helper->findNearby($INPUT->str('geohash'));
194
			} else {
195
				$results = array(
196
						'error' => hsc($this->getLang('invalidinput'))
197
				);
198
			}
199
		}
200
201
		$showMedia = $INPUT->bool('showMedia', true);
202
203
		switch ($param['format']) {
204
			case 'JSON' :
205
				$this->printJSON($results);
206
				break;
207
			case 'HTML' :
208
			// fall through to default
209
			default :
210
				$this->printHTML($results, $showMedia);
211
				break;
212
		}
213
	}
214
215
	/**
216
	 * Print seachresults as HTML lists.
217
	 *
218
	 * @param array $searchresults
219
	 */
220
	private function printJSON($searchresults) {
221
		require_once DOKU_INC . 'inc/JSON.php';
222
		$json = new JSON();
223
		header('Content-Type: application/json');
224
		print $json->encode($searchresults);
225
	}
226
227
	/**
228
	 * Print seachresults as HTML lists.
229
	 *
230
	 * @param array $searchresults
231
	 * @param boolean $showMedia
232
	 */
233
	private function printHTML($searchresults, $showMedia = true) {
234
		$pages = ( array ) ($searchresults ['pages']);
235
		$media = ( array ) $searchresults ['media'];
236
		$lat = ( float ) $searchresults ['lat'];
237
		$lon = ( float ) $searchresults ['lon'];
238
		$geohash = ( string ) $searchresults ['geohash'];
239
240
		if (isset ($searchresults ['error'])) {
241
			print '<div class="level1"><p>' . hsc($results ['error']) . '</p></div>';
242
			return;
243
		}
244
245
		// print a HTML list
246
		print '<h1>' . $this->getLang('results_header') . '</h1>' . DOKU_LF;
247
		print '<div class="level1">' . DOKU_LF;
248
		if (!empty ($pages)) {
249
			$pagelist = '<ol>' . DOKU_LF;
250
			foreach ($pages as $page) {
251
				$pagelist .= '<li>' . html_wikilink(':' . $page ['id'], useHeading('navigation') ? null : noNS($page ['id'])) . ' (' . $this->getLang('results_distance_prefix') . $page ['distance'] . '&nbsp;m) ' . $page ['description'] . '</li>' . DOKU_LF;
252
			}
253
			$pagelist .= '</ol>' . DOKU_LF;
254
255
			print '<h2>' . $this->getLang('results_pages') . hsc(' lat;lon: ' . $lat . ';' . $lon . ' (geohash: ' . $geohash . ')') . '</h2>';
256
			print '<div class="level2">' . DOKU_LF;
257
			print $pagelist;
258
			print '</div>' . DOKU_LF;
259
		} else {
260
			print '<p>' . hsc($this->getLang('nothingfound')) . '</p>';
261
		}
262
263
		if (!empty ($media) && $showMedia) {
264
			$pagelist = '<ol>' . DOKU_LF;
265
			foreach ($media as $m) {
266
				$opts = array();
267
				$link = ml($m ['id'], $opts, false, '&amp;', false);
268
				$opts ['w'] = '100';
269
				$src = ml($m ['id'], $opts);
270
				$pagelist .= '<li><a href="' . $link . '"><img src="' . $src . '"></a> (' . $this->getLang('results_distance_prefix') . $page ['distance'] . '&nbsp;m) ' . hsc($desc) . '</li>' . DOKU_LF;
271
			}
272
			$pagelist .= '</ol>' . DOKU_LF;
273
274
			print '<h2>' . $this->getLang('results_media') . hsc(' lat;lon: ' . $lat . ';' . $lon . ' (geohash: ' . $geohash . ')') . '</h2>' . DOKU_LF;
275
			print '<div class="level2">' . DOKU_LF;
276
			print $pagelist;
277
			print '</div>' . DOKU_LF;
278
		}
279
		print '<p>' . $this->getLang('results_precision') . $searchresults ['precision'] . ' m. ';
280
		if (strlen($geohash) > 1) {
281
			$url = wl(getID(), array(
282
					'do' => 'findnearby',
283
					'geohash' => substr($geohash, 0, - 1)
284
			));
285
			print '<a href="' . $url . '" class="findnearby">' . $this->getLang('search_largerarea') . '</a>.</p>' . DOKU_LF;
286
		}
287
		print '</div>' . DOKU_LF;
288
	}
289
290
	/**
291
	 * add media to spatial index.
292
	 *
293
	 * @param Doku_Event $event
294
	 *        	event object by reference
295
	 * @param unknown $param
296
	 */
297
	function _handle_media_uploaded(Doku_Event & $event, $param) {
298
		// data[0] temporary file name (read from $_FILES)
299
		// data[1] file name of the file being uploaded
300
		// data[2] future directory id of the file being uploaded
301
		// data[3] the mime type of the file being uploaded
302
		// data[4] true if the uploaded file exists already
303
		// data[5] (since 2011-02-06) the PHP function used to move the file to the correct location
304
305
		// dbglog ( $event->data, "_handle_media_uploaded::event data" );
0 ignored issues
show
Unused Code Comprehensibility introduced by
50% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
306
307
		// check the list of mimetypes
308
		// if it's a supported type call appropriate index function
309
		if (substr_compare($event->data [3], 'image/jpeg', 0)) {
310
			$indexer = plugin_load('helper', 'spatialhelper_index');
311
			if ($indexer) {
312
				$indexer->indexImage($event->data [2], $event->data [1]);
313
			}
314
		}
315
		// TODO add image/tiff
316
		// TODO kml, gpx, geojson...
317
	}
318
319
	/**
320
	 * removes the media from the index.
321
	 */
322
	function _handle_media_deleted(Doku_Event & $event, $param) {
323
		// data['id'] ID data['unl'] unlink return code
1 ignored issue
show
Unused Code Comprehensibility introduced by
39% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
324
		// data['del'] Namespace directory unlink return code
325
		// data['name'] file name data['path'] full path to the file
326
		// data['size'] file size
327
328
		// dbglog ( $event->data, "_handle_media_deleted::event data" );
0 ignored issues
show
Unused Code Comprehensibility introduced by
50% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
329
330
		// remove the media id from the index
331
		$indexer = & plugin_load('helper', 'spatialhelper_index');
332
		if ($indexer) {
333
			$indexer->deleteFromIndex('media__' . $event->data ['id']);
334
		}
335
	}
336
337
	/**
338
	 * add a link to the spatial sitemap files in the header.
339
	 *
340
	 * @param Doku_Event $event
341
	 *        	the DokuWiki event. $event->data is a two-dimensional
342
	 *        	array of all meta headers. The keys are meta, link and script.
343
	 * @param unknown_type $param
344
	 *
345
	 * @see http://www.dokuwiki.org/devel:event:tpl_metaheader_output
346
	 */
347
	public function handle_metaheader_output(Doku_Event $event, $param) {
348
		// TODO maybe test for exist
349
		$event->data ["link"] [] = array(
350
				"type" => "application/atom+xml",
351
				"rel" => "alternate",
352
				"href" => ml($this->getConf('media_georss')),
353
				"title" => "Spatial ATOM Feed"
354
		);
355
		$event->data ["link"] [] = array(
356
				"type" => "application/vnd.google-earth.kml+xml",
357
				"rel" => "alternate",
358
				"href" => ml($this->getConf('media_kml')),
359
				"title" => "KML Sitemap"
360
		);
361
	}
362
363
	/**
364
	 * Calculate a new coordinate based on start, distance and bearing
365
	 *
366
	 * @param $start array
367
	 *        	- start coordinate as decimal lat/lon pair
368
	 * @param $dist float
369
	 *        	- distance in kilometers
370
	 * @param $brng float
371
	 *        	- bearing in degrees (compass direction)
372
	 */
373
	private function _geo_destination($start, $dist, $brng) {
374
		$lat1 = _toRad($start [0]);
375
		$lon1 = _toRad($start [1]);
376
		// http://en.wikipedia.org/wiki/Earth_radius
377
		// average earth radius in km
378
		$dist = $dist / 6371.01;
379
		$brng = _toRad($brng);
380
381
		$lon2 = $lon1 + atan2(sin($brng) * sin($dist) * cos($lat1), cos($dist) - sin($lat1) * sin($lat2));
382
		$lon2 = fmod(($lon2 + 3 * pi()), (2 * pi())) - pi();
383
384
		return array(
385
				_toDeg($lat2),
386
				_toDeg($lon2)
387
		);
388
	}
389
	private function _toRad($deg) {
390
		return $deg * pi() / 180;
391
	}
392
	private function _toDeg($rad) {
393
		return $rad * 180 / pi();
394
	}
395
}
396