Completed
Push — develop ( 152113...fddfbb )
by Greg
12:05
created

GoogleMapsModule::getPreLoadContent()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 18
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 11
nc 1
nop 0
dl 0
loc 18
rs 9.4285
c 0
b 0
f 0
1
<?php
2
/**
3
 * webtrees: online genealogy
4
 * Copyright (C) 2016 webtrees development team
5
 * This program is free software: you can redistribute it and/or modify
6
 * it under the terms of the GNU General Public License as published by
7
 * the Free Software Foundation, either version 3 of the License, or
8
 * (at your option) any later version.
9
 * This program is distributed in the hope that it will be useful,
10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
 * GNU General Public License for more details.
13
 * You should have received a copy of the GNU General Public License
14
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15
 */
16
namespace Fisharebest\Webtrees\Module;
17
18
use Fisharebest\Webtrees\Auth;
19
use Fisharebest\Webtrees\Controller\ChartController;
20
use Fisharebest\Webtrees\Controller\PageController;
21
use Fisharebest\Webtrees\Controller\SimpleController;
22
use Fisharebest\Webtrees\Database;
23
use Fisharebest\Webtrees\Fact;
24
use Fisharebest\Webtrees\Family;
25
use Fisharebest\Webtrees\Filter;
26
use Fisharebest\Webtrees\FlashMessages;
27
use Fisharebest\Webtrees\Functions\Functions;
28
use Fisharebest\Webtrees\Functions\FunctionsCharts;
29
use Fisharebest\Webtrees\Functions\FunctionsEdit;
30
use Fisharebest\Webtrees\Functions\FunctionsPrint;
31
use Fisharebest\Webtrees\GedcomTag;
32
use Fisharebest\Webtrees\I18N;
33
use Fisharebest\Webtrees\Individual;
34
use Fisharebest\Webtrees\Log;
35
use Fisharebest\Webtrees\Menu;
36
use Fisharebest\Webtrees\Module;
37
use Fisharebest\Webtrees\Stats;
38
use Fisharebest\Webtrees\Theme;
39
use Fisharebest\Webtrees\Tree;
40
use PDO;
41
42
/**
43
 * Class GoogleMapsModule
44
 *
45
 * @link http://www.google.com/permissions/guidelines.html
46
 *
47
 * "... an unregistered Google Brand Feature should be followed by
48
 * the superscripted letters TM or SM ..."
49
 *
50
 * Hence, use "Google Maps™"
51
 *
52
 * "... Use the trademark only as an adjective"
53
 *
54
 * "... Use a generic term following the trademark, for example:
55
 * GOOGLE search engine, Google search"
56
 *
57
 * Hence, use "Google Maps™ mapping service" where appropriate.
58
 */
59
class GoogleMapsModule extends AbstractModule implements ModuleConfigInterface, ModuleTabInterface, ModuleChartInterface {
60
	// How to update the database schema for this module
61
	const SCHEMA_TARGET_VERSION   = 6;
62
	const SCHEMA_SETTING_NAME     = 'GM_SCHEMA_VERSION';
63
	const SCHEMA_MIGRATION_PREFIX = '\Fisharebest\Webtrees\Module\GoogleMaps\Schema';
64
65
	/** @var Individual[] of ancestors of root person */
66
	private $ancestors = array();
67
68
	/** @var int Number of generation to display */
69
	private $generations;
70
71
	/** @var int Number of nodes in the chart */
72
	private $treesize;
73
74
	/** {@inheritdoc} */
75
	public function getTitle() {
76
		return /* I18N: The name of a module. Google Maps™ is a trademark. Do not translate it? http://en.wikipedia.org/wiki/Google_maps */ I18N::translate('Google Maps™');
77
	}
78
79
	/** {@inheritdoc} */
80
	public function getDescription() {
81
		return /* I18N: Description of the “Google Maps™” module */ I18N::translate('Show the location of places and events using the Google Maps™ mapping service.');
82
	}
83
84
	/**
85
	 * This is a general purpose hook, allowing modules to respond to routes
86
	 * of the form module.php?mod=FOO&mod_action=BAR
87
	 *
88
	 * @param string $mod_action
89
	 */
90
	public function modAction($mod_action) {
91
		Database::updateSchema(self::SCHEMA_MIGRATION_PREFIX, self::SCHEMA_SETTING_NAME, self::SCHEMA_TARGET_VERSION);
92
93
		switch ($mod_action) {
94
		case 'admin_config':
95
			$this->config();
96
			break;
97
		case 'flags':
98
			$this->flags();
99
			break;
100
		case 'pedigree_map':
101
			$this->pedigreeMap();
102
			break;
103
		case 'admin_placecheck':
104
			$this->adminPlaceCheck();
105
			break;
106
		case 'admin_places':
107
			$this->adminPlaces();
108
			break;
109
		case 'places_edit':
110
			$this->placesEdit();
111
			break;
112
		case 'wt_street_view':
113
			$this->wtStreetView();
114
			break;
115
		default:
116
			http_response_code(404);
117
			break;
118
		}
119
	}
120
121
	/** {@inheritdoc} */
122
	public function getConfigLink() {
123
		Database::updateSchema(self::SCHEMA_MIGRATION_PREFIX, self::SCHEMA_SETTING_NAME, self::SCHEMA_TARGET_VERSION);
124
125
		return 'module.php?mod=' . $this->getName() . '&amp;mod_action=admin_config';
126
	}
127
128
	/** {@inheritdoc} */
129
	public function defaultTabOrder() {
130
		return 80;
131
	}
132
133
	/** {@inheritdoc} */
134
	public function getPreLoadContent() {
135
		global $controller;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
136
137
		$controller->addInlineJavascript("
138
		jQuery('head').append('<link type=\"text/css\" href =\"" . WT_STATIC_URL . WT_MODULES_DIR . "googlemap/css/wt_v3_googlemap.css\" rel=\"stylesheet\">');
139
		");
140
141
		ob_start();
142
		?>
143
		<script src="<?php echo $this->googleMapsScript() ?>"></script>
0 ignored issues
show
Security Cross-Site Scripting introduced by
$this->googleMapsScript() can contain request data and is used in html attribute with double-quotes context(s) leading to a potential security vulnerability.

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
144
		<script>
145
			var minZoomLevel   = <?php echo $this->getSetting('GM_MIN_ZOOM') ?>;
0 ignored issues
show
Security Cross-Site Scripting introduced by
$this->getSetting('GM_MIN_ZOOM') can contain request data and is used in output context(s) leading to a potential security vulnerability.

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
146
			var maxZoomLevel   = <?php echo $this->getSetting('GM_MAX_ZOOM') ?>;
0 ignored issues
show
Security Cross-Site Scripting introduced by
$this->getSetting('GM_MAX_ZOOM') can contain request data and is used in output context(s) leading to a potential security vulnerability.

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
147
			var startZoomLevel = maxZoomLevel;
148
		</script>
149
		<?php
150
		return ob_get_clean();
151
	}
152
153
	/** {@inheritdoc} */
154
	public function canLoadAjax() {
155
		return true;
156
	}
157
158
	/** {@inheritdoc} */
159
	public function getTabContent() {
160
		global $controller;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
161
162
		Database::updateSchema(self::SCHEMA_MIGRATION_PREFIX, self::SCHEMA_SETTING_NAME, self::SCHEMA_TARGET_VERSION);
163
164
		if ($this->checkMapData($controller->record)) {
165
			// This call can return an empty string if no facts with map co-ordinates exist
166
			$mapdata = $this->buildIndividualMap($controller->record);
167
		} else {
168
			$mapdata = '';
169
		}
170
		if ($mapdata) {
171
			$html = '<div id="' . $this->getName() . '_content" class="facts_table">';
172
			$html .= '<div class="gm-wrapper" style="height:' . $this->getSetting('GM_YSIZE') . 'px;">';
173
			$html .= '<div class="gm-map"></div>';
174
			$html .= $mapdata;
175
			$html .= '</div>';
176
			if (Auth::isAdmin()) {
177
				$html .= '<div class="gm-options noprint">';
178
				$html .= '<a href="module.php?mod=' . $this->getName() . '&amp;mod_action=admin_config">' . I18N::translate('Google Maps™ preferences') . '</a>';
179
				$html .= ' | <a href="module.php?mod=' . $this->getName() . '&amp;mod_action=admin_places">' . I18N::translate('Geographic data') . '</a>';
180
				$html .= ' | <a href="module.php?mod=' . $this->getName() . '&amp;mod_action=admin_placecheck">' . I18N::translate('Place check') . '</a>';
181
				$html .= '</div>';
182
			}
183
			$html .= '<script>loadMap();</script>';
184
			$html .= '</div>';
185
		} else {
186
			$html = '<div class="facts_value noprint">' . I18N::translate('No map data exists for this individual') . '</div>';
187
			if (Auth::isAdmin()) {
188
				$html .= '<div style="text-align: center;"><a href="module.php?mod=googlemap&amp;mod_action=admin_config">' . I18N::translate('Google Maps™ preferences') . '</a></div>';
189
			}
190
		}
191
192
		return $html;
193
	}
194
195
	/** {@inheritdoc} */
196
	public function hasTabContent() {
197
		return Module::getModuleByName('googlemap') || Auth::isAdmin();
198
	}
199
200
	/** {@inheritdoc} */
201
	public function isGrayedOut() {
202
		return false;
203
	}
204
205
	/**
206
	 * Return a menu item for this chart.
207
	 *
208
	 * @param Individual $individual
209
	 *
210
	 * @return Menu
211
	 */
212
	public function getChartMenu(Individual $individual) {
213
		return new Menu(
214
			I18N::translate('Pedigree map'),
215
			'module.php?mod=googlemap&amp;mod_action=pedigree_map&amp;rootid=' . $individual->getXref() . '&amp;ged=' . $individual->getTree()->getNameUrl(),
216
			'menu-chart-pedigree_map',
217
			array('rel' => 'nofollow')
218
		);
219
	}
220
221
	/**
222
	 * Return a menu item for this chart - for use in individual boxes.
223
	 *
224
	 * @param Individual $individual
225
	 *
226
	 * @return Menu
227
	 */
228
	public function getBoxChartMenu(Individual $individual) {
229
		return $this->getChartMenu($individual);
230
	}
231
232
	/**
233
	 * A form to edit the module configuration.
234
	 */
235
	private function config() {
236
		$controller = new PageController;
237
		$controller
238
			->restrictAccess(Auth::isAdmin())
239
			->setPageTitle(I18N::translate('Google Maps™'));
240
241
		if (Filter::post('action') === 'update') {
242
			$this->setSetting('GM_API_KEY', Filter::post('GM_API_KEY'));
243
			$this->setSetting('GM_MAP_TYPE', Filter::post('GM_MAP_TYPE'));
244
			$this->setSetting('GM_USE_STREETVIEW', Filter::post('GM_USE_STREETVIEW'));
245
			$this->setSetting('GM_MIN_ZOOM', Filter::post('GM_MIN_ZOOM'));
246
			$this->setSetting('GM_MAX_ZOOM', Filter::post('GM_MAX_ZOOM'));
247
			$this->setSetting('GM_XSIZE', Filter::post('GM_XSIZE'));
248
			$this->setSetting('GM_YSIZE', Filter::post('GM_YSIZE'));
249
			$this->setSetting('GM_PLACE_HIERARCHY', Filter::post('GM_PLACE_HIERARCHY'));
250
			$this->setSetting('GM_PH_XSIZE', Filter::post('GM_PH_XSIZE'));
251
			$this->setSetting('GM_PH_YSIZE', Filter::post('GM_PH_YSIZE'));
252
			$this->setSetting('GM_PH_MARKER', Filter::post('GM_PH_MARKER'));
253
			$this->setSetting('GM_PREFIX_1', Filter::post('GM_PREFIX_1'));
254
			$this->setSetting('GM_PREFIX_2', Filter::post('GM_PREFIX_2'));
255
			$this->setSetting('GM_PREFIX_3', Filter::post('GM_PREFIX_3'));
256
			$this->setSetting('GM_PREFIX_4', Filter::post('GM_PREFIX_4'));
257
			$this->setSetting('GM_PREFIX_5', Filter::post('GM_PREFIX_5'));
258
			$this->setSetting('GM_PREFIX_6', Filter::post('GM_PREFIX_6'));
259
			$this->setSetting('GM_PREFIX_7', Filter::post('GM_PREFIX_7'));
260
			$this->setSetting('GM_PREFIX_8', Filter::post('GM_PREFIX_8'));
261
			$this->setSetting('GM_PREFIX_9', Filter::post('GM_PREFIX_9'));
262
			$this->setSetting('GM_POSTFIX_1', Filter::post('GM_POSTFIX_1'));
263
			$this->setSetting('GM_POSTFIX_2', Filter::post('GM_POSTFIX_2'));
264
			$this->setSetting('GM_POSTFIX_3', Filter::post('GM_POSTFIX_3'));
265
			$this->setSetting('GM_POSTFIX_4', Filter::post('GM_POSTFIX_4'));
266
			$this->setSetting('GM_POSTFIX_5', Filter::post('GM_POSTFIX_5'));
267
			$this->setSetting('GM_POSTFIX_6', Filter::post('GM_POSTFIX_6'));
268
			$this->setSetting('GM_POSTFIX_7', Filter::post('GM_POSTFIX_7'));
269
			$this->setSetting('GM_POSTFIX_8', Filter::post('GM_POSTFIX_8'));
270
			$this->setSetting('GM_POSTFIX_9', Filter::post('GM_POSTFIX_9'));
271
272
			FlashMessages::addMessage(I18N::translate('The preferences for the module “%s” have been updated.', $this->getTitle()), 'success');
273
			header('Location: ' . WT_BASE_URL . 'module.php?mod=googlemap&mod_action=admin_config');
274
275
			return;
276
		}
277
278
		$controller->pageHeader();
279
280
		?>
281
		<ol class="breadcrumb small">
282
			<li><a href="admin.php"><?php echo I18N::translate('Control panel') ?></a></li>
283
			<li><a href="admin_modules.php"><?php echo I18N::translate('Module administration') ?></a></li>
284
			<li class="active"><?php echo $controller->getPageTitle() ?></li>
285
		</ol>
286
287
		<ul class="nav nav-tabs nav-justified" role="tablist">
288
			<li role="presentation" class="active">
289
				<a href="#" role="tab">
290
					<?php echo I18N::translate('Google Maps™ preferences') ?>
291
				</a>
292
			</li>
293
			<li role="presentation">
294
				<a href="?mod=googlemap&amp;mod_action=admin_places">
295
					<?php echo I18N::translate('Geographic data') ?>
296
				</a>
297
			</li>
298
			<li role="presentation">
299
				<a href="?mod=googlemap&amp;mod_action=admin_placecheck">
300
					<?php echo I18N::translate('Place check') ?>
301
				</a>
302
			</li>
303
		</ul>
304
305
		<h2><?php echo I18N::translate('Google Maps™ preferences') ?></h2>
306
307
		<form class="form-horizontal" method="post" name="configform" action="module.php?mod=googlemap&mod_action=admin_config">
308
			<input type="hidden" name="action" value="update">
309
310
			<!-- GM_MAP_TYPE -->
311
			<div class="form-group">
312
				<label class="control-label col-sm-3" for="GM_API_KEY">
313
					<?php echo /* I18N: https://en.wikipedia.org/wiki/API_key */ I18N::translate('API key') ?>
314
				</label>
315
				<div class="col-sm-9">
316
					<input id="GM_API_KEY" class="form-control" type="text" name="GM_API_KEY" value="<?php echo $this->getSetting('GM_API_KEY') ?>">
0 ignored issues
show
Security Cross-Site Scripting introduced by
$this->getSetting('GM_API_KEY') can contain request data and is used in html attribute with double-quotes context(s) leading to a potential security vulnerability.

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
317
					<p class="small text-muted"><?php echo I18N::translate('Google allows a small number of anonymous map requests per day.  If you need more than this, you will need a Google account and an API key.') ?>
318
						<a href="https://developers.google.com/maps/documentation/javascript/get-api-key">
319
							<?php echo /* I18N: https://en.wikipedia.org/wiki/API_key */ I18N::translate('Get an API key from Google.') ?>
320
						</a>
321
					</p>
322
				</div>
323
			</div>
324
325
			<div class="form-group">
326
				<label class="control-label col-sm-3" for="GM_MAP_TYPE">
327
					<?php echo I18N::translate('Default map type') ?>
328
				</label>
329
				<div class="col-sm-9">
330
					<?php
331
					$options = array(
332
						'ROADMAP'   => I18N::translate('Map'),
333
						'SATELLITE' => I18N::translate('Satellite'),
334
						'HYBRID'    => I18N::translate('Hybrid'),
335
						'TERRAIN'   => I18N::translate('Terrain'),
336
					);
337
					echo FunctionsEdit::selectEditControl('GM_MAP_TYPE', $options, null, $this->getSetting('GM_MAP_TYPE'), 'class="form-control"');
338
					?>
339
				</div>
340
			</div>
341
342
			<!-- GM_USE_STREETVIEW -->
343
			<fieldset class="form-group">
344
				<legend class="control-label col-sm-3">
345
					<?php echo /* I18N: http://en.wikipedia.org/wiki/Google_street_view */ I18N::translate('Google Street View™') ?>
346
				</legend>
347
				<div class="col-sm-9">
348
					<?php echo FunctionsEdit::radioButtons('GM_USE_STREETVIEW', array(false => I18N::translate('hide'), true => I18N::translate('show')), $this->getSetting('GM_USE_STREETVIEW'), 'class="radio-inline"') ?>
349
				</div>
350
			</fieldset>
351
352
			<!-- GM_XSIZE / GM_YSIZE -->
353
			<fieldset class="form-group">
354
				<legend class="control-label col-sm-3">
355
					<?php echo I18N::translate('Size of map (in pixels)') ?>
356
				</legend>
357
				<div class="col-sm-9">
358
					<div class="row">
359
						<div class="col-sm-6">
360
							<div class="input-group">
361
								<label class="input-group-addon" for="GM_XSIZE"><?php echo I18N::translate('Width') ?></label>
362
								<input id="GM_XSIZE" class="form-control" type="text" name="GM_XSIZE" value="<?php echo $this->getSetting('GM_XSIZE') ?>">
0 ignored issues
show
Security Cross-Site Scripting introduced by
$this->getSetting('GM_XSIZE') can contain request data and is used in html attribute with double-quotes context(s) leading to a potential security vulnerability.

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
363
							</div>
364
						</div>
365
						<div class="col-sm-6">
366
							<div class="input-group">
367
								<label class="input-group-addon" for="GM_YSIZE"><?php echo I18N::translate('Height') ?></label>
368
								<input id="GM_YSIZE" class="form-control" type="text" name="GM_YSIZE" value="<?php echo $this->getSetting('GM_YSIZE') ?>">
0 ignored issues
show
Security Cross-Site Scripting introduced by
$this->getSetting('GM_YSIZE') can contain request data and is used in html attribute with double-quotes context(s) leading to a potential security vulnerability.

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
369
							</div>
370
						</div>
371
					</div>
372
				</div>
373
			</fieldset>
374
375
			<!-- GM_MIN_ZOOM / GM_MAX_ZOOM -->
376
			<fieldset class="form-group">
377
				<legend class="control-label col-sm-3">
378
					<?php echo I18N::translate('Zoom level of map') ?>
379
				</legend>
380
				<div class="col-sm-9">
381
					<div class="row">
382
						<div class="col-sm-6">
383
							<div class="input-group">
384
								<label class="input-group-addon" for="GM_MIN_ZOOM"><?php echo I18N::translate('minimum') ?></label>
385
								<?php echo FunctionsEdit::selectEditControl('GM_MIN_ZOOM', array_combine(range(1, 14), range(1, 14)), null, $this->getSetting('GM_MIN_ZOOM'), 'class="form-control"') ?>
386
							</div>
387
						</div>
388
						<div class="col-sm-6">
389
							<div class="input-group">
390
								<label class="input-group-addon" for="GM_MAX_ZOOM"><?php echo I18N::translate('maximum') ?></label>
391
								<?php echo FunctionsEdit::selectEditControl('GM_MAX_ZOOM', array_combine(range(1, 20), range(1, 20)), null, $this->getSetting('GM_MAX_ZOOM'), 'class="form-control"') ?>
392
							</div>
393
						</div>
394
					</div>
395
					<p class="small text-muted"><?php echo I18N::translate('Minimum and maximum zoom level for the Google map. 1 is the full map, 15 is single house. Note that 15 is only available in certain areas.') ?></p>
396
				</div>
397
			</fieldset>
398
399
			<!-- GM_PREFIX / GM_POSTFIX -->
400
			<fieldset class="form-group">
401
				<legend class="control-label col-sm-3">
402
					<?php echo I18N::translate('Optional prefixes and suffixes') ?>
403
				</legend>
404
				<div class="col-sm-9">
405
					<div class="row">
406
						<div class ="col-sm-6">
407
							<p class="form-control-static"><strong><?php echo I18N::translate('Prefixes') ?></strong></p>
408 View Code Duplication
							<?php for ($level = 1; $level < 10; $level++): ?>
409
							<?php
410
							if ($level == 1) {
411
								$label = I18N::translate('Country');
412
							} else {
413
								$label = I18N::translate('Level') . ' ' . $level;
414
							}
415
							?>
416
							<div class="input-group">
417
								<label class="input-group-addon" for="GM_PREFIX_<?php echo $level ?>"><?php echo $label ?></label>
418
								<input class="form-control" type="text" name="GM_PREFIX_<?php echo $level ?>" value="<?php echo $this->getSetting('GM_PREFIX_' . $level) ?>">
0 ignored issues
show
Security Cross-Site Scripting introduced by
$this->getSetting('GM_PREFIX_' . $level) can contain request data and is used in html attribute with double-quotes context(s) leading to a potential security vulnerability.

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
419
							</div>
420
							<?php endfor ?>
421
						</div>
422
						<div class="col-sm-6">
423
							<p class="form-control-static"><strong><?php echo I18N::translate('Suffixes') ?></strong></p>
424 View Code Duplication
							<?php for ($level = 1; $level < 10; $level++): ?>
425
							<?php
426
							if ($level == 1) {
427
								$label = I18N::translate('Country');
428
							} else {
429
								$label = I18N::translate('Level') . ' ' . $level;
430
							}
431
							?>
432
							<div class="input-group">
433
								<label class="input-group-addon" for="GM_POSTFIX_<?php echo $level ?>"><?php echo $label ?></label>
434
								<input class="form-control" type="text" name="GM_POSTFIX_<?php echo $level ?>" value="<?php echo $this->getSetting('GM_POSTFIX_' . $level) ?>">
0 ignored issues
show
Security Cross-Site Scripting introduced by
$this->getSetting('GM_POSTFIX_' . $level) can contain request data and is used in html attribute with double-quotes context(s) leading to a potential security vulnerability.

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
435
							</div>
436
							<?php endfor ?>
437
						</div>
438
					</div>
439
					<p class="small text-muted"><?php echo I18N::translate('Some place names may be written with optional prefixes and suffixes. For example “Orange” versus “Orange County”. If the family tree contains the full place names, but the geographic database contains the short place names, then you should specify a list of the prefixes and suffixes to be disregarded. Multiple values should be separated with semicolons. For example “County;County of” or “Township;Twp;Twp.”.') ?></p>
440
				</div>
441
			</fieldset>
442
443
			<h3><?php echo I18N::translate('Place hierarchy') ?></h3>
444
445
			<!-- GM_PLACE_HIERARCHY -->
446
			<fieldset class="form-group">
447
				<legend class="control-label col-sm-3">
448
					<?php echo I18N::translate('Use Google Maps™ for the place hierarchy') ?>
449
				</legend>
450
				<div class="col-sm-9">
451
					<?php echo FunctionsEdit::editFieldYesNo('GM_PLACE_HIERARCHY', $this->getSetting('GM_PLACE_HIERARCHY'), 'class="radio-inline"') ?>
0 ignored issues
show
Documentation introduced by
$this->getSetting('GM_PLACE_HIERARCHY') is of type string|null, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
452
				</div>
453
			</fieldset>
454
455
			<!-- GM_PH_XSIZE / GM_PH_YSIZE -->
456
			<fieldset class="form-group">
457
				<legend class="control-label col-sm-3">
458
					<?php echo I18N::translate('Size of map (in pixels)') ?>
459
				</legend>
460
				<div class="col-sm-9">
461
					<div class="row">
462
						<div class="col-sm-6">
463
							<div class="input-group">
464
								<label class="input-group-addon" for="GM_PH_XSIZE"><?php echo I18N::translate('Width') ?></label>
465
								<input id="GM_PH_XSIZE" class="form-control" type="text" name="GM_PH_XSIZE" value="<?php echo $this->getSetting('GM_PH_XSIZE') ?>">
0 ignored issues
show
Security Cross-Site Scripting introduced by
$this->getSetting('GM_PH_XSIZE') can contain request data and is used in html attribute with double-quotes context(s) leading to a potential security vulnerability.

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
466
							</div>
467
						</div>
468
						<div class="col-sm-6">
469
							<div class="input-group">
470
								<label class="input-group-addon" for="GM_PH_YSIZE"><?php echo I18N::translate('Height') ?></label>
471
								<input id="GM_PH_YSIZE" class="form-control" type="text" name="GM_PH_YSIZE" value="<?php echo $this->getSetting('GM_PH_YSIZE') ?>">
0 ignored issues
show
Security Cross-Site Scripting introduced by
$this->getSetting('GM_PH_YSIZE') can contain request data and is used in html attribute with double-quotes context(s) leading to a potential security vulnerability.

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
472
							</div>
473
						</div>
474
					</div>
475
				</div>
476
			</fieldset>
477
478
			<!-- GM_PH_MARKER -->
479
			<div class="form-group">
480
				<label class="control-label col-sm-3" for="GM_PH_MARKER">
481
					<?php echo I18N::translate('Type of place markers in the place hierarchy') ?>
482
				</label>
483
				<div class="col-sm-9">
484
					<?php
485
					$ph_options = array(
486
						'G_DEFAULT_ICON' => I18N::translate('Standard'),
487
						'G_FLAG'         => I18N::translate('Flag'),
488
					);
489
					echo FunctionsEdit::selectEditControl('GM_PH_MARKER', $ph_options, null, $this->getSetting('GM_PH_MARKER'), 'class="form-control"');
490
					?>
491
				</div>
492
			</div>
493
494
			<!-- SAVE BUTTON -->
495
			<div class="form-group">
496
				<div class="col-sm-offset-3 col-sm-9">
497
					<button type="submit" class="btn btn-primary">
498
						<i class="fa fa-check"></i>
499
						<?php echo I18N::translate('save') ?>
500
					</button>
501
				</div>
502
			</div>
503
		</form>
504
		<?php
505
	}
506
507
	/**
508
	 * Google Maps API script
509
	 *
510
	 * @return string
511
	 */
512
	private function googleMapsScript() {
513
		$key = $this->getSetting('GM_API_KEY');
514
515
		return 'https://maps.googleapis.com/maps/api/js?v=3&amp;key=' . $key . '&amp;language=' . WT_LOCALE;
516
	}
517
518
	/**
519
	 * Select a flag.
520
	 */
521
	private function flags() {
522
		global $WT_TREE;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
523
524
		$controller = new SimpleController;
525
		$controller
526
			->setPageTitle(I18N::translate('Select flag'))
527
			->pageHeader();
528
529
		$stats           = new Stats($WT_TREE);
530
		$countries       = $stats->getAllCountries();
531
		$action          = Filter::post('action');
532
		$countrySelected = Filter::get('countrySelected', null, 'Countries');
533
		$stateSelected   = Filter::get('stateSelected', null, 'States');
534
535
		$country = array();
536
		if (is_dir(WT_ROOT . WT_MODULES_DIR . 'googlemap/places/flags')) {
537
			$files = glob(WT_ROOT . WT_MODULES_DIR . 'googlemap/places/flags/*.png');
538
			foreach ($files as $file) {
539
				$country[] = basename($file, '.png');
540
			}
541
		}
542
543
		if ($countrySelected == 'Countries') {
544
			$flags = $country;
545
		} else {
546
			$flags = array();
547
			if (is_dir(WT_ROOT . WT_MODULES_DIR . 'googlemap/places/' . $countrySelected . '/flags')) {
548
				$files = glob(WT_ROOT . WT_MODULES_DIR . 'googlemap/places/' . $countrySelected . '/flags/*.png');
549
				foreach ($files as $file) {
550
					$flags[] = basename($file, '.png');
551
				}
552
			}
553
		}
554
555
		$flags_s = array();
556
		if ($stateSelected != 'States' && is_dir(WT_ROOT . WT_MODULES_DIR . 'googlemap/places/' . $countrySelected . '/flags/' . $stateSelected)) {
557
			$files = glob(WT_ROOT . WT_MODULES_DIR . 'googlemap/places/' . $countrySelected . '/flags/' . $stateSelected . '*.png');
558
			foreach ($files as $file) {
559
				$flags_s[] = basename($file, '.png');
560
			}
561
		}
562
563
		if ($action == 'ChangeFlag' && Filter::post('FLAGS') !== null) {
564
		?>
565
			<script>
566
		<?php if (Filter::post('selcountry') == 'Countries') { ?>
567
					window.opener.document.editplaces.icon.value = 'places/flags/<?php echo $flags[Filter::post('FLAGS')] ?>.png';
568
					window.opener.document.getElementById('flagsDiv').innerHTML = "<img src=\"<?php echo WT_STATIC_URL . WT_MODULES_DIR ?>googlemap/places/flags/<?php echo $country[Filter::post('FLAGS')] ?>.png\">&nbsp;&nbsp;<a href=\"#\" onclick=\"change_icon();return false;\"><?php echo I18N::translate('Change flag') ?></a>&nbsp;&nbsp;<a href=\"#\" onclick=\"remove_icon();return false;\"><?php echo I18N::translate('Remove flag') ?></a>";
569
		<?php } elseif (Filter::post('selstate') != "States") { ?>
570
					window.opener.document.editplaces.icon.value = 'places/<?php echo $countrySelected . '/flags/' . Filter::post('selstate') . '/' . $flags_s[Filter::post('FLAGS')] ?>.png';
571
					window.opener.document.getElementById('flagsDiv').innerHTML = "<img src=\"<?php echo WT_STATIC_URL . WT_MODULES_DIR ?>googlemap/places/<?php echo $countrySelected . "/flags/" . Filter::post('selstate') . '/' . $flags_s[Filter::post('FLAGS')] ?>.png\">&nbsp;&nbsp;<a href=\"#\" onclick=\"change_icon();return false;\"><?php echo I18N::translate('Change flag') ?></a>&nbsp;&nbsp;<a href=\"#\" onclick=\"remove_icon();return false;\"><?php echo I18N::translate('Remove flag') ?></a>";
572
		<?php } else { ?>
573
					window.opener.document.editplaces.icon.value = "places/<?php echo $countrySelected . '/flags/' . $flags[Filter::post('FLAGS')] ?>.png";
574
					window.opener.document.getElementById('flagsDiv').innerHTML = "<img src=\"<?php echo WT_STATIC_URL . WT_MODULES_DIR ?>googlemap/places/<?php echo $countrySelected . '/flags/' . $flags[Filter::post('FLAGS')] ?>.png\">&nbsp;&nbsp;<a href=\"#\" onclick=\"change_icon();return false;\"><?php echo I18N::translate('Change flag') ?></a>&nbsp;&nbsp;<a href=\"#\" onclick=\"remove_icon();return false;\"><?php echo I18N::translate('Remove flag') ?></a>";
575
		<?php } ?>
576
					window.opener.updateMap();
577
					window.close();
578
			</script>
579
		<?php
580
			return;
581
		} else {
582
		?>
583
		<script>
584
			function selectCountry() {
585
				if (document.flags.COUNTRYSELECT.value == 'Countries') {
586
					window.location="module.php?mod=googlemap&mod_action=flags&countrySelected=Countries";
587
				} else if (document.flags.STATESELECT.value != 'States') {
588
					window.location="module.php?mod=googlemap&mod_action=flags&countrySelected=" + document.flags.COUNTRYSELECT.value + "&stateSelected=" + document.flags.STATESELECT.value;
589
				} else {
590
					window.location="module.php?mod=googlemap&mod_action=flags&countrySelected=" + document.flags.COUNTRYSELECT.value;
591
				}
592
			}
593
		</script>
594
		<?php
595
		}
596
		$countryList = array();
597
598
		foreach ($country as $item) {
599
			if (is_dir(WT_MODULES_DIR . 'googlemap/places/' . $item . '/flags')) {
600
				if (isset($countries[$item])) {
601
					$countryList[$item] = $countries[$item];
602
				} else {
603
					$countryList[$item] = $item;
604
				}
605
			}
606
		}
607
		asort($countryList);
608
		$stateList = array();
609
610
		if ($countrySelected != 'Countries') {
611
			$placesDir = scandir(WT_MODULES_DIR . 'googlemap/places/' . $countrySelected . '/flags/');
612
			foreach ($flags as $flag) {
613
				if (in_array($flag, $placesDir)) {
614
					$rep = opendir(WT_MODULES_DIR . 'googlemap/places/' . $countrySelected . '/flags/' . $flag . '/');
615
					while ($file = readdir($rep)) {
0 ignored issues
show
Unused Code introduced by
$file 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...
616
						$stateList[$flag] = $flag;
617
					}
618
					closedir($rep);
619
				}
620
			}
621
			asort($stateList);
622
		}
623
624
		?>
625
		<h4><?php echo I18N::translate('Change flag') ?></h4>
626
627
		<p class="small text-muted">
628
			<?php echo I18N::translate('Using the pull down menu it is possible to select a country, of which a flag can be selected. If no flags are shown, then there are no flags defined for this country.') ?>
629
		</p>
630
631
		<form method="post" id="flags" name="flags" action="module.php?mod=googlemap&amp;mod_action=flags&amp;countrySelected=<?php echo $countrySelected ?>&amp;stateSelected=<?php echo $stateSelected ?>">
632
			<input type="hidden" name="action" value="ChangeFlag">
633
			<input type="hidden" name="selcountry" value="<?php echo $countrySelected ?>">
634
			<input type="hidden" name="selstate" value="<?php echo $stateSelected ?>">
635
			<table class="facts_table" style="margin: 0 auto 20px">
636
				<tr>
637
					<td class="optionbox" colspan="4">
638
						<select name="COUNTRYSELECT" dir="ltr" onchange="selectCountry()">
639
							<option value="Countries"><?php echo I18N::translate('Countries') ?></option>
640
							<?php foreach ($countryList as $country_key => $country_name) {
641
								echo '<option value="', $country_key, '" ';
642
								if ($countrySelected == $country_key) {
643
									echo 'selected';
644
								}
645
								echo '>', $country_name, '</option>';
646
							} ?>
647
						</select>
648
					</td>
649
				</tr>
650
651
				<?php
652
				$i    = 0;
653
				$path = WT_STATIC_URL . WT_MODULES_DIR . 'googlemap/places/';
654
				$path .= $countrySelected == 'Countries' ? 'flags/' : $countrySelected . '/flags/';
655
				foreach (array_chunk($flags, 4) as $row) {
656
					echo "<tr>";
657
					foreach ($row as $flag) {
658
						if ($flag != 'blank') {
659
							if (isset($countries[$flag])) {
660
								$title = $countries[$flag];
661
							} else {
662
								$title = $flag;
663
							}
664
						} else {
665
							$title = $countries['???'];
666
						}
667
						echo '<td>';
668
						echo '<input type="radio" dir="ltr" name="FLAGS" value="' . $i++ . '">';
669
						echo '<img style="width:25px; height:15px; margin-right:15px" src="' . $path . $flag . '.png" alt="' . $flag . '" title="' . $title . '">';
670
						echo $flag . '</td>';
671
672
					}
673
674
					echo str_repeat('<td></td>', 4 - count($row));
675
					echo "</tr>";
676
				}
677
678
				echo'<tr style="visibility:' . $countrySelected == 'Countries' || count($stateList) == 0 ? 'hidden' : 'visible' . '">';
679
				?>
680
681
				<td class="optionbox" colspan="4">
682
					<select name="STATESELECT" dir="ltr" onchange="selectCountry()">
683
						<option value="States"><?php echo /* I18N: Part of a country, state/region/county */ I18N::translate('Subdivision') ?></option>
684
						<?php foreach ($stateList as $state_key => $state_name) {
685
							echo '<option value="', $state_key, '" ';
686
							if ($stateSelected == $state_key) {
687
								echo 'selected';
688
							}
689
							echo '>', $state_name, '</option>';
690
						} ?>
691
					</select>
692
				</td>
693
			</tr>
694
695
			<?php
696
697
			if (!empty($flags_s)) {
698
				foreach (array_chunk($flags_s, 4) as $row) {
699
					echo "<tr>";
700
					foreach ($row as $flag) {
701
						echo '<td><input type="radio" dir="ltr" name="FLAGS" value="', $i++, '"><img src="', WT_STATIC_URL . WT_MODULES_DIR, 'googlemap/places/', $countrySelected, '/flags/', $stateSelected, '/', $flag, '.png">&nbsp;&nbsp;', $flag, '></td>';
702
					}
703
					echo str_repeat('<td></td>', 4 - count($row));
704
					echo '</tr>';
705
				}
706
			}
707
708
			?>
709
710
			</table>
711
			<p id="save-cancel">
712
				<input type="submit" class="save" value="<?php echo I18N::translate('save') ?>">
713
				<input type="button" class="cancel" value="<?php echo I18N::translate('close') ?>" onclick="window.close();">
714
			</p>
715
		</form>
716
		<?php
717
	}
718
719
	/**
720
	 * Display a map showing the originas of ones ancestors.
721
	 */
722
	private function pedigreeMap() {
723
		global $controller, $WT_TREE;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
724
725
		$MAX_PEDIGREE_GENERATIONS = $WT_TREE->getPreference('MAX_PEDIGREE_GENERATIONS');
726
		// Limit this to match available number of icons.
727
		// 8 generations equals 255 individuals
728
		$MAX_PEDIGREE_GENERATIONS = min($MAX_PEDIGREE_GENERATIONS, 8);
729
730
		$controller        = new ChartController();
731
		$this->generations = Filter::getInteger('PEDIGREE_GENERATIONS', 2, $MAX_PEDIGREE_GENERATIONS, $WT_TREE->getPreference('DEFAULT_PEDIGREE_GENERATIONS'));
732
		$this->treesize    = pow(2, $this->generations) - 1;
0 ignored issues
show
Documentation Bug introduced by
It seems like pow(2, $this->generations) - 1 can also be of type double. However, the property $treesize is declared as type integer. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
733
		$this->ancestors   = array_values($controller->sosaAncestors($this->generations));
734
735
		$controller
736
			->setPageTitle(/* I18N: %s is an individual’s name */ I18N::translate('Pedigree map of %s', $controller->root->getFullName()))
737
			->pageHeader()
738
			->addExternalJavascript(WT_AUTOCOMPLETE_JS_URL)
739
			/* prepending the module css in the page head allows the theme to over-ride it*/
740
			->addInlineJavascript("
741
				jQuery('head').prepend('<link type=\"text/css\" href =\"" . WT_STATIC_URL . WT_MODULES_DIR . "googlemap/css/wt_v3_googlemap.css\" rel=\"stylesheet\">');
742
				autocomplete();" .
743
				$this->pedigreeMapJavascript()
744
			);
745
746
		echo '<div id="pedigreemap-page"><h2>', $controller->getPageTitle(), '</h2>';
747
748
		// -- print the form to change the number of displayed generations
749
		?>
750
		<form name="people" method="get" action="?">
751
			<input type="hidden" name="ged" value="<?php echo $WT_TREE->getNameHtml() ?>">
752
			<input type="hidden" name="mod" value="googlemap">
753
			<input type="hidden" name="mod_action" value="pedigree_map">
754
			<table class="list_table">
755
				<tr>
756
					<td class="descriptionbox wrap">
757
						<label for="rootid">
758
							<?php echo I18N::translate('Individual') ?>
759
						</label>
760
					</td>
761
					<td class="optionbox">
762
						<input class="pedigree_form" data-autocomplete-type="INDI" type="text" id="rootid" name="rootid" size="3" value="<?php echo $controller->root->getXref() ?>">
0 ignored issues
show
Security Cross-Site Scripting introduced by
$controller->root->getXref() can contain request data and is used in html attribute with double-quotes context(s) leading to a potential security vulnerability.

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
763
						<?php echo FunctionsPrint::printFindIndividualLink('rootid') ?>
764
					</td>
765
					<td class="topbottombar" rowspan="2">
766
						<input type="submit" value="<?php echo I18N::translate('View') ?>">
767
					</td>
768
				</tr>
769
				<tr>
770
					<td class="descriptionbox wrap">
771
						<label for="PEDIGREE_GENERATIONS">
772
							<?php echo I18N::translate('Generations') ?>
773
						</label>
774
					</td>
775
					<td class="optionbox">
776
						<select name="PEDIGREE_GENERATIONS" id="PEDIGREE_GENERATIONS">
777
						<?php
778
							for ($p = 3; $p <= $MAX_PEDIGREE_GENERATIONS; $p++) {
779
								echo '<option value="', $p, '" ';
780
								if ($p == $this->generations) {
781
									echo 'selected';
782
								}
783
								echo '>', $p, '</option>';
784
							}
785
						?>
786
						</select>
787
					</td>
788
				</tr>
789
			</table>
790
		</form>
791
		<!-- end of form -->
792
793
		<!-- count records by type -->
794
		<?php
795
		$curgen   = 1;
796
		$priv     = 0;
797
		$count    = 0;
798
		$miscount = 0;
799
		$missing  = array();
800
801
		$latlongval = array();
802
		$lat        = array();
803
		$lon        = array();
804
		for ($i = 0; $i < ($this->treesize); $i++) {
805
			// -- check to see if we have moved to the next generation
806
			if ($i + 1 >= pow(2, $curgen)) {
807
				$curgen++;
808
			}
809
			$person = $this->ancestors[$i];
810
			if (!empty($person)) {
811
				$name = $person->getFullName();
812
				if ($name == I18N::translate('Private')) {
813
					$priv++;
814
				}
815
				$place = $person->getBirthPlace();
816
				if (empty($place)) {
817
					$latlongval[$i] = null;
818
				} else {
819
					$latlongval[$i] = $this->getLatitudeAndLongitudeFromPlaceLocation($person->getBirthPlace());
820
				}
821
				if ($latlongval[$i]) {
822
					$lat[$i] = strtr($latlongval[$i]->pl_lati, array('N' => '', 'S' => '-', ',' => '.'));
823
					$lon[$i] = strtr($latlongval[$i]->pl_long, array('N' => '', 'S' => '-', ',' => '.'));
824
					if ($lat[$i] && $lon[$i]) {
825
						$count++;
826 View Code Duplication
					} else {
827
						// The place is in the table but has empty values
828
						if ($name) {
829
							$missing[] = '<a href="' . $person->getHtmlUrl() . '">' . $name . '</a>';
830
							$miscount++;
831
						}
832
					}
833 View Code Duplication
				} else {
834
					// There was no place, or not listed in the map table
835
					if ($name) {
836
						$missing[] = '<a href="' . $person->getHtmlUrl() . '">' . $name . '</a>';
837
						$miscount++;
838
					}
839
				}
840
			}
841
		}
842
		//<!-- end of count records by type -->
843
		//<!-- start of map display -->
844
		echo '<div class="gm-pedigree-map">';
845
		echo '<div class="gm-wrapper" style="height:' . $this->getSetting('GM_YSIZE') . 'px;">';
0 ignored issues
show
Security Cross-Site Scripting introduced by
'<div class="gm-wrapper"...g('GM_YSIZE') . 'px;">' can contain request data and is used in output context(s) leading to a potential security vulnerability.

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
846
		echo '<div class="gm-map"><i class="icon-loading-large"></i></div>';
847
		echo '<div class="gm-ancestors"></div>';
848
		echo '</div>';
849
850
		if (Auth::isAdmin()) {
851
			echo '<div class="gm-options noprint">';
852
			echo '<a href="module.php?mod=' . $this->getName() . '&amp;mod_action=admin_config">' . I18N::translate('Google Maps™ preferences') . '</a>';
853
			echo ' | <a href="module.php?mod=' . $this->getName() . '&amp;mod_action=admin_places">' . I18N::translate('Geographic data') . '</a>';
854
			echo ' | <a href="module.php?mod=' . $this->getName() . '&amp;mod_action=admin_placecheck">' . I18N::translate('Place check') . '</a>';
855
			echo '</div>';
856
		}
857
		// display info under map
858
		echo '<hr>';
859
		// print summary statistics
860
		if (isset($curgen)) {
861
			$total = pow(2, $curgen) - 1;
862
			echo '<div>';
863
			echo I18N::plural(
864
				'%1$s individual displayed, out of the normal total of %2$s, from %3$s generations.',
865
				'%1$s individuals displayed, out of the normal total of %2$s, from %3$s generations.',
866
				$count,
867
				I18N::number($count), I18N::number($total), I18N::number($curgen)
868
			);
869
			echo '</div>';
870
			if ($priv) {
871
				echo '<div>' . I18N::plural('%s individual is private.', '%s individuals are private.', $priv, $priv), '</div>';
872
			}
873
			if ($count + $priv != $total) {
874
				if ($miscount == 0) {
875
					echo '<div>' . I18N::translate('No ancestors in the database.'), '</div>';
876
				} else {
877
					echo '<div>' . /* I18N: %1$s is a count of individuals, %2$s is a list of their names */ I18N::plural(
878
						'%1$s individual is missing birthplace map coordinates: %2$s.',
879
						'%1$s individuals are missing birthplace map coordinates: %2$s.',
880
						$miscount, I18N::number($miscount), implode(I18N::$list_separator, $missing)),
881
						'</div>';
882
				}
883
			}
884
		}
885
		echo '</div>';
886
		echo '</div>';
887
		echo '<script src="', $this->googleMapsScript(), '"></script>';
0 ignored issues
show
Security Cross-Site Scripting introduced by
$this->googleMapsScript() can contain request data and is used in output context(s) leading to a potential security vulnerability.

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
888
	}
889
890
	/**
891
	 * Create the Javascript to activate the map.
892
	 *
893
	 * @return string
894
	 */
895
	private function pedigreeMapJavascript() {
896
897
		$js = '
898
		// this variable will collect the html which will eventually be placed in the side bar
899
		var gm_ancestors_html = "";
900
		// arrays to hold copies of the markers and html used by the side bar
901
		// because the function closure trick doesnt work there
902
		var gmarkers = [];
903
		var index = 0;
904
		var lastlinkid;
905
		var infowindow = new google.maps.InfoWindow({});
906
		// === Create an associative array of GIcons()
907
		var gicons = [];
908
		gicons["1"]        = new google.maps.MarkerImage(WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/icon1.png")
909
		gicons["1"].shadow = new google.maps.MarkerImage(WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/shadow50.png",
910
									new google.maps.Size(37, 34), // Shadow size
911
									new google.maps.Point(0, 0),  // Shadow origin
912
									new google.maps.Point(10, 34) // Shadow anchor is base of image
913
								);
914
		gicons["2"]         = new google.maps.MarkerImage(WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/icon2.png")
915
		gicons["2"].shadow  = new google.maps.MarkerImage(WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/shadow50.png",
916
									new google.maps.Size(37, 34), // Shadow size
917
									new google.maps.Point(0, 0),  // Shadow origin
918
									new google.maps.Point(10, 34) // Shadow anchor is base of image
919
								);
920
		gicons["2L"] = new google.maps.MarkerImage(WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/icon2L.png",
921
									new google.maps.Size(32, 32), // Image size
922
									new google.maps.Point(0, 0),  // Image origin
923
									new google.maps.Point(28, 28) // Image anchor
924
								);
925
		gicons["2L"].shadow = new google.maps.MarkerImage(WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/shadow-left-large.png",
926
									new google.maps.Size(49, 32), // Shadow size
927
									new google.maps.Point(0, 0),  // Shadow origin
928
									new google.maps.Point(32, 27) // Shadow anchor is base of image
929
								);
930
		gicons["2R"] = new google.maps.MarkerImage(WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/icon2R.png",
931
									new google.maps.Size(32, 32), // Image size
932
									new google.maps.Point(0, 0),  // Image origin
933
									new google.maps.Point(4, 28)  // Image anchor
934
								);
935
		gicons["2R"].shadow = new google.maps.MarkerImage(WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/shadow-right-large.png",
936
									new google.maps.Size(49, 32), // Shadow size
937
									new google.maps.Point(0, 0),  // Shadow origin
938
									new google.maps.Point(15, 27) // Shadow anchor is base of image
939
								);
940
		gicons["2Ls"] = new google.maps.MarkerImage(WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/icon2Ls.png",
941
									new google.maps.Size(24, 24), // Image size
942
									new google.maps.Point(0, 0),  // Image origin
943
									new google.maps.Point(22, 22) // Image anchor
944
								);
945
		gicons["2Rs"] = new google.maps.MarkerImage(WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/icon2Rs.png",
946
									new google.maps.Size(24, 24), // Image size
947
									new google.maps.Point(0, 0),  // Image origin
948
									new google.maps.Point(2, 22)  // Image anchor
949
								);
950
		gicons["3"] = new google.maps.MarkerImage(WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/icon3.png")
951
		gicons["3"].shadow = new google.maps.MarkerImage(WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/shadow50.png",
952
									new google.maps.Size(37, 34), // Shadow size
953
									new google.maps.Point(0, 0),  // Shadow origin
954
									new google.maps.Point(10, 34) // Shadow anchor is base of image
955
								);
956
		gicons["3L"] = new google.maps.MarkerImage(WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/icon3L.png",
957
									new google.maps.Size(32, 32), // Image size
958
									new google.maps.Point(0, 0),  // Image origin
959
									new google.maps.Point(28, 28) // Image anchor
960
								);
961
		gicons["3L"].shadow = new google.maps.MarkerImage(WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/shadow-left-large.png",
962
									new google.maps.Size(49, 32), // Shadow size
963
									new google.maps.Point(0, 0),  // Shadow origin
964
									new google.maps.Point(32, 27) // Shadow anchor is base of image
965
								);
966
		gicons["3R"] = new google.maps.MarkerImage(WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/icon3R.png",
967
									new google.maps.Size(32, 32), // Image size
968
									new google.maps.Point(0, 0),  // Image origin
969
									new google.maps.Point(4, 28)  // Image anchor
970
								);
971
		gicons["3R"].shadow = new google.maps.MarkerImage(WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/shadow-right-large.png",
972
									new google.maps.Size(49, 32), // Shadow size
973
									new google.maps.Point(0, 0),  // Shadow origin
974
									new google.maps.Point(15, 27) // Shadow anchor is base of image
975
								);
976
		gicons["3Ls"] = new google.maps.MarkerImage(WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/icon3Ls.png",
977
									new google.maps.Size(24, 24), // Image size
978
									new google.maps.Point(0, 0),  // Image origin
979
									new google.maps.Point(22, 22) // Image anchor
980
								);
981
		gicons["3Rs"] = new google.maps.MarkerImage(WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/icon3Rs.png",
982
									new google.maps.Size(24, 24), // Image size
983
									new google.maps.Point(0, 0),  // Image origin
984
									new google.maps.Point(2, 22)  // Image anchor
985
								);
986
		gicons["4"] = new google.maps.MarkerImage(WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/icon4.png")
987
		gicons["4"].shadow = new google.maps.MarkerImage(WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/shadow50.png",
988
									new google.maps.Size(37, 34), // Shadow size
989
									new google.maps.Point(0, 0),  // Shadow origin
990
									new google.maps.Point(10, 34) // Shadow anchor is base of image
991
								);
992
		gicons["4L"] = new google.maps.MarkerImage(WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/icon4L.png",
993
									new google.maps.Size(32, 32), // Image size
994
									new google.maps.Point(0, 0),  // Image origin
995
									new google.maps.Point(28, 28) // Image anchor
996
								);
997
		gicons["4L"].shadow = new google.maps.MarkerImage(WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/shadow-left-large.png",
998
									new google.maps.Size(49, 32), // Shadow size
999
									new google.maps.Point(0, 0),  // Shadow origin
1000
									new google.maps.Point(32, 27) // Shadow anchor is base of image
1001
								);
1002
		gicons["4R"] = new google.maps.MarkerImage(WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/icon4R.png",
1003
									new google.maps.Size(32, 32), // Image size
1004
									new google.maps.Point(0, 0),  // Image origin
1005
									new google.maps.Point(4, 28)  // Image anchor
1006
								);
1007
		gicons["4R"].shadow = new google.maps.MarkerImage(WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/shadow-right-large.png",
1008
									new google.maps.Size(49, 32), // Shadow size
1009
									new google.maps.Point(0, 0),  // Shadow origin
1010
									new google.maps.Point(15, 27) // Shadow anchor is base of image
1011
								);
1012
		gicons["4Ls"] = new google.maps.MarkerImage(WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/icon4Ls.png",
1013
									new google.maps.Size(24, 24), // Image size
1014
									new google.maps.Point(0, 0),  // Image origin
1015
									new google.maps.Point(22, 22) // Image anchor
1016
								);
1017
		gicons["4Rs"] = new google.maps.MarkerImage(WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/icon4Rs.png",
1018
									new google.maps.Size(24, 24), // Image size
1019
									new google.maps.Point(0, 0),  // Image origin
1020
									new google.maps.Point(2, 22)  // Image anchor
1021
								);
1022
		gicons["5"] = new google.maps.MarkerImage(WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/icon5.png")
1023
		gicons["5"].shadow = new google.maps.MarkerImage(WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/shadow50.png",
1024
									new google.maps.Size(37, 34), // Shadow size
1025
									new google.maps.Point(0, 0),  // Shadow origin
1026
									new google.maps.Point(10, 34) // Shadow anchor is base of image
1027
								);
1028
		gicons["5L"] = new google.maps.MarkerImage(WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/icon5L.png",
1029
									new google.maps.Size(32, 32), // Image size
1030
									new google.maps.Point(0, 0),  // Image origin
1031
									new google.maps.Point(28, 28) // Image anchor
1032
								);
1033
		gicons["5L"].shadow = new google.maps.MarkerImage(WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/shadow-left-large.png",
1034
									new google.maps.Size(49, 32), // Shadow size
1035
									new google.maps.Point(0, 0),  // Shadow origin
1036
									new google.maps.Point(32, 27) // Shadow anchor is base of image
1037
								);
1038
		gicons["5R"] = new google.maps.MarkerImage(WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/icon5R.png",
1039
									new google.maps.Size(32, 32), // Image size
1040
									new google.maps.Point(0, 0),  // Image origin
1041
									new google.maps.Point(4, 28)  // Image anchor
1042
								);
1043
		gicons["5R"].shadow = new google.maps.MarkerImage(WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/shadow-right-large.png",
1044
									new google.maps.Size(49, 32), // Shadow size
1045
									new google.maps.Point(0, 0),  // Shadow origin
1046
									new google.maps.Point(15, 27) // Shadow anchor is base of image
1047
								);
1048
		gicons["5Ls"] = new google.maps.MarkerImage(WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/icon5Ls.png",
1049
									new google.maps.Size(24, 24), // Image size
1050
									new google.maps.Point(0, 0),  // Image origin
1051
									new google.maps.Point(22, 22) // Image anchor
1052
								);
1053
		gicons["5Rs"] = new google.maps.MarkerImage(WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/icon5Rs.png",
1054
									new google.maps.Size(24, 24), // Image size
1055
									new google.maps.Point(0, 0),  // Image origin
1056
									new google.maps.Point(2, 22)  // Image anchor
1057
								);
1058
		gicons["6"] = new google.maps.MarkerImage(WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/icon6.png")
1059
		gicons["6"].shadow = new google.maps.MarkerImage(WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/shadow50.png",
1060
									new google.maps.Size(37, 34), // Shadow size
1061
									new google.maps.Point(0, 0),  // Shadow origin
1062
									new google.maps.Point(10, 34) // Shadow anchor is base of image
1063
								);
1064
		gicons["6L"] = new google.maps.MarkerImage(WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/icon6L.png",
1065
									new google.maps.Size(32, 32), // Image size
1066
									new google.maps.Point(0, 0),  // Image origin
1067
									new google.maps.Point(28, 28) // Image anchor
1068
								);
1069
		gicons["6L"].shadow = new google.maps.MarkerImage(WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/shadow-left-large.png",
1070
									new google.maps.Size(49, 32), // Shadow size
1071
									new google.maps.Point(0, 0),  // Shadow origin
1072
									new google.maps.Point(32, 27) // Shadow anchor is base of image
1073
								);
1074
		gicons["6R"] = new google.maps.MarkerImage(WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/icon6R.png",
1075
									new google.maps.Size(32, 32), // Image size
1076
									new google.maps.Point(0, 0),  // Image origin
1077
									new google.maps.Point(4, 28)  // Image anchor
1078
								);
1079
		gicons["6R"].shadow = new google.maps.MarkerImage(WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/shadow-right-large.png",
1080
									new google.maps.Size(49, 32), // Shadow size
1081
									new google.maps.Point(0, 0),  // Shadow origin
1082
									new google.maps.Point(15, 27) // Shadow anchor is base of image
1083
								);
1084
		gicons["6Ls"] = new google.maps.MarkerImage(WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/icon6Ls.png",
1085
									new google.maps.Size(24, 24), // Image size
1086
									new google.maps.Point(0, 0),  // Image origin
1087
									new google.maps.Point(22, 22) // Image anchor
1088
								);
1089
		gicons["6Rs"] = new google.maps.MarkerImage(WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/icon6Rs.png",
1090
									new google.maps.Size(24, 24), // Image size
1091
									new google.maps.Point(0, 0),  // Image origin
1092
									new google.maps.Point(2, 22)  // Image anchor
1093
								);
1094
		gicons["7"] = new google.maps.MarkerImage(WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/icon7.png")
1095
		gicons["7"].shadow = new google.maps.MarkerImage(WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/shadow50.png",
1096
									new google.maps.Size(37, 34), // Shadow size
1097
									new google.maps.Point(0, 0),  // Shadow origin
1098
									new google.maps.Point(10, 34) // Shadow anchor is base of image
1099
								);
1100
		gicons["7L"] = new google.maps.MarkerImage(WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/icon7L.png",
1101
									new google.maps.Size(32, 32), // Image size
1102
									new google.maps.Point(0, 0),  // Image origin
1103
									new google.maps.Point(28, 28) // Image anchor
1104
								);
1105
		gicons["7L"].shadow = new google.maps.MarkerImage(WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/shadow-left-large.png",
1106
									new google.maps.Size(49, 32), // Shadow size
1107
									new google.maps.Point(0, 0),  // Shadow origin
1108
									new google.maps.Point(32, 27) // Shadow anchor is base of image
1109
								);
1110
		gicons["7R"] = new google.maps.MarkerImage(WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/icon7R.png",
1111
									new google.maps.Size(32, 32), // Image size
1112
									new google.maps.Point(0, 0),  // Image origin
1113
									new google.maps.Point(4, 28)  // Image anchor
1114
								);
1115
		gicons["7R"].shadow = new google.maps.MarkerImage(WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/shadow-right-large.png",
1116
									new google.maps.Size(49, 32), // Shadow size
1117
									new google.maps.Point(0, 0),  // Shadow origin
1118
									new google.maps.Point(15, 27) // Shadow anchor is base of image
1119
								);
1120
		gicons["7Ls"] = new google.maps.MarkerImage(WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/icon7Ls.png",
1121
									new google.maps.Size(24, 24), // Image size
1122
									new google.maps.Point(0, 0),  // Image origin
1123
									new google.maps.Point(22, 22) // Image anchor
1124
								);
1125
		gicons["7Rs"] = new google.maps.MarkerImage(WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/icon7Rs.png",
1126
									new google.maps.Size(24, 24), // Image size
1127
									new google.maps.Point(0, 0),  // Image origin
1128
									new google.maps.Point(2, 22)  // Image anchor
1129
								);
1130
		gicons["8"] = new google.maps.MarkerImage(WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/icon8.png")
1131
		gicons["8"].shadow = new google.maps.MarkerImage(WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/shadow50.png",
1132
									new google.maps.Size(37, 34), // Shadow size
1133
									new google.maps.Point(0, 0),  // Shadow origin
1134
									new google.maps.Point(10, 34) // Shadow anchor is base of image
1135
								);
1136
		gicons["8L"] = new google.maps.MarkerImage(WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/icon8L.png",
1137
									new google.maps.Size(32, 32), // Image size
1138
									new google.maps.Point(0, 0),  // Image origin
1139
									new google.maps.Point(28, 28) // Image anchor
1140
								);
1141
		gicons["8L"].shadow = new google.maps.MarkerImage(WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/shadow-left-large.png",
1142
									new google.maps.Size(49, 32), // Shadow size
1143
									new google.maps.Point(0, 0),  // Shadow origin
1144
									new google.maps.Point(32, 27) // Shadow anchor is base of image
1145
								);
1146
		gicons["8R"] = new google.maps.MarkerImage(WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/icon8R.png",
1147
									new google.maps.Size(32, 32), // Image size
1148
									new google.maps.Point(0, 0),  // Image origin
1149
									new google.maps.Point(4, 28)  // Image anchor
1150
								);
1151
		gicons["8R"].shadow = new google.maps.MarkerImage(WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/shadow-right-large.png",
1152
									new google.maps.Size(49, 32), // Shadow size
1153
									new google.maps.Point(0, 0),  // Shadow origin
1154
									new google.maps.Point(15, 27) // Shadow anchor is base of image
1155
								);
1156
		gicons["8Ls"] = new google.maps.MarkerImage(WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/icon8Ls.png",
1157
									new google.maps.Size(24, 24), // Image size
1158
									new google.maps.Point(0, 0),  // Image origin
1159
									new google.maps.Point(22, 22) // Image anchor
1160
								);
1161
		gicons["8Rs"] = new google.maps.MarkerImage(WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/icon8Rs.png",
1162
									new google.maps.Size(24, 24), // Image size
1163
									new google.maps.Point(0, 0),  // Image origin
1164
									new google.maps.Point(2, 22)  // Image anchor
1165
								);
1166
		// / A function to create the marker and set up the event window
1167
		function createMarker(point, name, html, mhtml, icontype) {
1168
			// Create a marker with the requested icon
1169
			var marker = new google.maps.Marker({
1170
				icon:     gicons[icontype],
1171
				shadow:   gicons[icontype].shadow,
1172
				map:      pm_map,
1173
				position: point,
1174
				id:       index,
1175
				zIndex:   0
1176
			});
1177
			google.maps.event.addListener(marker, "click", function() {
1178
				infowindow.close();
1179
				infowindow.setContent(mhtml);
1180
				infowindow.open(pm_map, marker);
1181
				var el = jQuery("#link_" + marker.id);
1182
				if(el.hasClass("person_box")) {
1183
					el
1184
						.removeClass("person_box")
1185
						.addClass("gm-ancestor-visited");
1186
					infowindow.close();
1187
				} else {
1188
					el
1189
					.addClass("person_box")
1190
					.removeClass("gm-ancestor-visited");
1191
				}
1192
				var anchor = infowindow.getAnchor();
1193
				lastlinkid = anchor ? anchor.id : null;
1194
			});
1195
			// save the info we need to use later for the side bar
1196
			gmarkers[index] = marker;
1197
			gm_ancestors_html += "<div id=\"link_" + index++ + "\" class=\"gm-ancestor\">" + html +"</div>";
1198
1199
			return marker;
1200
		};
1201
		// create the map
1202
		var myOptions = {
1203
			zoom: 6,
1204
			center: new google.maps.LatLng(0, 0),
1205
			mapTypeId: google.maps.MapTypeId.TERRAIN,  // ROADMAP, SATELLITE, HYBRID, TERRAIN
1206
			mapTypeControlOptions: {
1207
				style: google.maps.MapTypeControlStyle.DROPDOWN_MENU  // DEFAULT, DROPDOWN_MENU, HORIZONTAL_BAR
1208
			},
1209
			navigationControlOptions: {
1210
				position: google.maps.ControlPosition.TOP_RIGHT,  // BOTTOM, BOTTOM_LEFT, LEFT, TOP, etc
1211
				style: google.maps.NavigationControlStyle.SMALL   // ANDROID, DEFAULT, SMALL, ZOOM_PAN
1212
			},
1213
			streetViewControl: false,  // Show Pegman or not
1214
			scrollwheel: true
1215
		};
1216
		var pm_map = new google.maps.Map(document.querySelector(".gm-map"), myOptions);
1217
		google.maps.event.addListener(pm_map, "click", function() {
1218
			jQuery(".gm-ancestor.person_box")
1219
				.removeClass("person_box")
1220
				.addClass("gm-ancestor-visited");
1221
			infowindow.close();
1222
			lastlinkid = null;
1223
		});
1224
		// create the map bounds
1225
		var bounds = new google.maps.LatLngBounds();';
1226
		// add the points
1227
		$curgen       = 1;
1228
		$count        = 0;
1229
		$colored_line = array(
1230
			'1' => '#FF0000',
1231
			'2' => '#0000FF',
1232
			'3' => '#00FF00',
1233
			'4' => '#FFFF00',
1234
			'5' => '#00FFFF',
1235
			'6' => '#FF00FF',
1236
			'7' => '#C0C0FF',
1237
			'8' => '#808000',
1238
		);
1239
		$lat        = array();
1240
		$lon        = array();
1241
		$latlongval = array();
1242
		for ($i = 0; $i < $this->treesize; $i++) {
1243
			// moved up to grab the sex of the individuals
1244
			$person = $this->ancestors[$i];
1245
			if ($person) {
1246
				$name = $person->getFullName();
1247
1248
				// -- check to see if we have moved to the next generation
1249
				if ($i + 1 >= pow(2, $curgen)) {
1250
					$curgen++;
1251
				}
1252
1253
				$relationship = FunctionsCharts::getSosaName($i + 1);
1254
1255
				// add thumbnail image
1256
				if ($person->getTree()->getPreference('SHOW_HIGHLIGHT_IMAGES')) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $person->getTree()->getP...SHOW_HIGHLIGHT_IMAGES') of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
1257
					$image = $person->displayImage();
1258
				} else {
1259
					$image = '';
1260
				}
1261
				// end of add image
1262
				$event = $image;
1263
				$event .= '<img src="' . WT_STATIC_URL . WT_MODULES_DIR . 'googlemap/images/sq' . $curgen . '.png" width="10" height="10"> ';
1264
				$event .= '<strong>' . $relationship . '</strong>';//</a>';
1265
1266
				$birth = $person->getFirstFact('BIRT');
1267
				$data  = Filter::escapeJs($event . ' <span><a href="' . $person->getHtmlUrl() . '">' . $name . '</a></span>');
1268
				$data .= $birth ? Filter::escapeJs($birth->summary()) : '';
1269
1270
				$latlongval[$i] = $this->getLatitudeAndLongitudeFromPlaceLocation($person->getBirthPlace());
1271
				if ($latlongval[$i]) {
1272
					$lat[$i] = (double) strtr($latlongval[$i]->pl_lati, array('N' => '', 'S' => '-', ',' => '.'));
1273
					$lon[$i] = (double) strtr($latlongval[$i]->pl_long, array('E' => '', 'W' => '-', ',' => '.'));
1274
					if ($lat[$i] || $lon[$i]) {
1275
						$marker_number = $curgen;
1276
						$dups          = 0;
1277
						for ($k = 0; $k < $i; $k++) {
1278
							if ($latlongval[$i] == $latlongval[$k]) {
1279
								$dups++;
1280
								switch ($dups) {
1281
								case 1:
1282
									$marker_number = $curgen . 'L';
1283
									break;
1284
								case 2:
1285
									$marker_number = $curgen . 'R';
1286
									break;
1287
								case 3:
1288
									$marker_number = $curgen . 'Ls';
1289
									break;
1290
								case 4:
1291
									$marker_number = $curgen . 'Rs';
1292
									break;
1293
								case 5: //adjust position where markers have same coodinates
1294
								default:
1295
									$marker_number = $curgen;
1296
									$lon[$i] += 0.0025;
1297
									$lat[$i] += 0.0025;
1298
									break;
1299
								}
1300
							}
1301
						}
1302
1303
						$js .= 'var point = new google.maps.LatLng(' . $lat[$i] . ',' . $lon[$i] . ');';
1304
						$js .= 'var marker = createMarker(point, "' . Filter::escapeJs($name) . '","<div class=\"gm-ancestor-link\">' . $data . '</div>", "';
1305
						$js .= '<div class=\"gm-info-window\">' . $data . '</div>", "' . $marker_number . '");';
1306
						// Construct the polygon lines
1307
						$to_child = (intval(($i - 1) / 2)); // Draw a line from parent to child
1308
						if (array_key_exists($to_child, $lat) && $lat[$to_child] != 0 && $lon[$to_child] != 0) {
1309
							$js .= '
1310
								var linecolor;
1311
								var plines;
1312
								var lines = [new google.maps.LatLng(' . $lat[$i] . ',' . $lon[$i] . '),
1313
									new google.maps.LatLng(' . $lat[$to_child] . ',' . $lon[$to_child] . ')];
1314
								linecolor = "' . $colored_line[$curgen] . '";
1315
								plines = new google.maps.Polygon({
1316
									paths: lines,
1317
									strokeColor: linecolor,
1318
									strokeOpacity: 0.8,
1319
									strokeWeight: 3,
1320
									fillColor: "#FF0000",
1321
									fillOpacity: 0.1
1322
								});
1323
								plines.setMap(pm_map);';
1324
						}
1325
						// Extend and fit marker bounds
1326
1327
						$js .= 'bounds.extend(point);';
1328
						$count++;
1329
					}
1330
				}
1331
			} else {
1332
				$latlongval[$i] = null;
1333
			}
1334
		}
1335
		$js .= 'oneplace=' . json_encode(count(array_unique($lat)) == 1 && count(array_unique($lon)) == 1) . ';';
1336
		$js .= 'if (oneplace) {
1337
			// Only have one location so adjust zoom
1338
			pm_map.setZoom(12);
1339
		} else {
1340
			pm_map.fitBounds(bounds);
1341
		}
1342
		pm_map.setCenter(bounds.getCenter());
1343
		// Close the sidebar highlight when the infowindow is closed
1344
		google.maps.event.addListener(infowindow, "closeclick", function() {
1345
			jQuery("#link_" + lastlinkid).toggleClass("gm-ancestor-visited person_box");
1346
			lastlinkid = null;
1347
		});
1348
		// put the assembled gm_ancestors_html contents into the gm-ancestors div
1349
		document.querySelector(".gm-ancestors").innerHTML = gm_ancestors_html;
1350
1351
		jQuery(".gm-ancestor-link")
1352
			.on("click", ".gallery", function(e) {
1353
				//e.stopPropagation();
1354
			})' .
1355
			'.on("click", function(e) {
1356
				if (lastlinkid != null) {
1357
					jQuery("#link_" + lastlinkid).toggleClass("person_box gm-ancestor-visited");
1358
				}
1359
				var el = jQuery(this).closest(".gm-ancestor");
1360
				var target = el.attr("id").split("_").pop();
1361
				google.maps.event.trigger(gmarkers[target], "click");
1362
			});
1363
		';
1364
1365
		return $js;
1366
	}
1367
1368
	/**
1369
	 * Check places for missing data, etc.
1370
	 */
1371
	private function adminPlaceCheck() {
1372
		global $WT_TREE;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
1373
1374
		$gedcom_id = Filter::get('gedcom_id', null, $WT_TREE->getTreeId());
1375
		$country   = Filter::get('country', '.+', 'XYZ');
1376
		$state     = Filter::get('state', '.+', 'XYZ');
1377
		$matching  = Filter::getBool('matching');
1378
1379
		$controller = new PageController;
1380
		$controller
1381
			->restrictAccess(Auth::isAdmin())
1382
			->setPageTitle(I18N::translate('Google Maps™'))
1383
			->pageHeader();
1384
1385
		?>
1386
		<ol class="breadcrumb small">
1387
			<li><a href="admin.php"><?php echo I18N::translate('Control panel') ?></a></li>
1388
			<li><a href="admin_modules.php"><?php echo I18N::translate('Module administration') ?></a></li>
1389
			<li class="active"><?php echo $controller->getPageTitle() ?></li>
1390
		</ol>
1391
1392
		<ul class="nav nav-tabs nav-justified" role="tablist">
1393
			<li role="presentation">
1394
				<a href="?mod=googlemap&amp;mod_action=admin_config" role="tab">
1395
					<?php echo I18N::translate('Google Maps™ preferences') ?>
1396
				</a>
1397
			</li>
1398
			<li role="presentation">
1399
				<a href="?mod=googlemap&amp;mod_action=admin_places">
1400
					<?php echo I18N::translate('Geographic data') ?>
1401
				</a>
1402
			</li>
1403
			<li role="presentation" class="active">
1404
				<a href="#">
1405
					<?php echo I18N::translate('Place check') ?>
1406
				</a>
1407
			</li>
1408
		</ul>
1409
		<?php
1410
1411
		echo '<h2>', I18N::translate('Place check'), '</h2>';
1412
1413
		// User options
1414
		$rows = Database::prepare("SELECT pl_id, pl_place FROM `##placelocation` WHERE pl_level=0 ORDER BY pl_place")->fetchAssoc();
1415
1416
		echo '<form name="placecheck" class="form form-inline">';
1417
		echo '<input type="hidden" name="mod" value="', $this->getName(), '">';
1418
		echo '<input type="hidden" name="mod_action" value="admin_placecheck">';
1419
		echo '<div class="form-group">';
1420
		echo '<label for="gedcom_id">', I18N::translate('Family tree'), '</label> ';
1421
		echo FunctionsEdit::selectEditControl('gedcom_id', Tree::getIdList(), null, $gedcom_id, ' onchange="this.form.submit();" class="form-control"'), ' ';
1422
		echo '<label for="country">', I18N::translate('Country'), '</label> ';
1423
		echo '<select name="country" onchange="this.form.submit();" class="form-control"> ';
1424
		echo '<option value="XYZ">', I18N::translate('All'), '</option>';
1425
		foreach ($rows as $id => $place) {
1426
			echo '<option value="', Filter::escapeHtml($place), '" ';
1427
			if ($place == $country) {
1428
				echo 'selected';
1429
				$par_id = $id;
1430
			}
1431
			echo '>', Filter::escapeHtml($place), '</option>';
1432
		}
1433
		echo '</select> ';
1434
		if ($country != 'XYZ') {
1435
			echo '<label for="state">', /* I18N: Part of a country, state/region/county */ I18N::translate('Subdivision'), '</label> ';
1436
			echo '<select name="state" onchange="this.form.submit();" class="form-control">';
1437
			echo '<option value="XYZ">', I18N::translate('All'), '</option>';
1438
			$places = Database::prepare("SELECT pl_place FROM `##placelocation` WHERE pl_parent_id=? ORDER BY pl_place")
1439
				->execute(array($par_id))
0 ignored issues
show
Bug introduced by
The variable $par_id 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...
1440
				->fetchOneColumn();
1441
			foreach ($places as $place) {
1442
				echo '<option value="', Filter::escapeHtml($place), '" ', $place == $state ? 'selected' : '', '>', Filter::escapeHtml($place), '</option>';
1443
			}
1444
			echo '</select> ';
1445
		}
1446
		echo '<div class="checkbox-inline">';
1447
		echo '<label for="matching">';
1448
		echo '<input type="checkbox" name="matching" value="1" onchange="this.form.submit();" ', ($matching ? 'checked' : ''), '>';
1449
		echo I18N::translate('Include fully matched places');
1450
		echo '</label>';
1451
		echo '</div></div>';
1452
		echo '</form>';
1453
		echo '<hr>';
1454
1455
		//Select all '2 PLAC ' tags in the file and create array
1456
		$place_list = array();
1457
		$ged_data   = Database::prepare("SELECT i_gedcom FROM `##individuals` WHERE i_gedcom LIKE ? AND i_file=?")
1458
			->execute(array("%\n2 PLAC %", $gedcom_id))
1459
			->fetchOneColumn();
1460 View Code Duplication
		foreach ($ged_data as $ged_datum) {
1461
			preg_match_all('/\n2 PLAC (.+)/', $ged_datum, $matches);
1462
			foreach ($matches[1] as $match) {
1463
				$place_list[$match] = true;
1464
			}
1465
		}
1466
		$ged_data = Database::prepare("SELECT f_gedcom FROM `##families` WHERE f_gedcom LIKE ? AND f_file=?")
1467
			->execute(array("%\n2 PLAC %", $gedcom_id))
1468
			->fetchOneColumn();
1469 View Code Duplication
		foreach ($ged_data as $ged_datum) {
1470
			preg_match_all('/\n2 PLAC (.+)/', $ged_datum, $matches);
1471
			foreach ($matches[1] as $match) {
1472
				$place_list[$match] = true;
1473
			}
1474
		}
1475
		// Unique list of places
1476
		$place_list = array_keys($place_list);
1477
1478
		// Apply_filter
1479
		if ($country == 'XYZ') {
1480
			$filter = '.*$';
1481
		} else {
1482
			$filter = preg_quote($country) . '$';
1483
			if ($state != 'XYZ') {
1484
				$filter = preg_quote($state) . ', ' . $filter;
1485
			}
1486
		}
1487
		$place_list = preg_grep('/' . $filter . '/', $place_list);
1488
1489
		//sort the array, limit to unique values, and count them
1490
		usort($place_list, '\Fisharebest\Webtrees\I18N::strcasecmp');
1491
		$i = count($place_list);
1492
1493
		//calculate maximum no. of levels to display
1494
		$x   = 0;
1495
		$max = 0;
1496
		while ($x < $i) {
1497
			$levels                 = explode(",", $place_list[$x]);
1498
			$parts                  = count($levels);
1499
			if ($parts > $max) {
1500
				$max = $parts;
1501
			}
1502
			$x++; }
1503
		$x = 0;
1504
1505
		//scripts for edit, add and refresh
1506
		?>
1507
		<script>
1508
			function edit_place_location(placeid) {
1509
				window.open('module.php?mod=googlemap&mod_action=places_edit&action=update&placeid='+placeid, '_blank', gmap_window_specs);
1510
				return false;
1511
			}
1512
1513
			function add_place_location(placeid) {
1514
				window.open('module.php?mod=googlemap&mod_action=places_edit&action=add&placeid='+placeid, '_blank', gmap_window_specs);
1515
				return false;
1516
			}
1517
		</script>
1518
		<?php
1519
1520
		//start to produce the display table
1521
		echo '<table class="table table-bordered table-condensed table-hover"><thead><tr>';
1522
		echo '<th rowspan="3">', I18N::translate('Place'), '</th>';
1523
		echo '<th colspan="', $max * 3, '">', I18N::translate('Geographic data'), '</th></tr>';
1524
		echo '<tr>';
1525
		for ($cols = 0; $cols < $max; ++$cols) {
1526
			if ($cols == 0) {
1527
				echo '<th colspan="3">', I18N::translate('Country'), '</th>';
1528
			} else {
1529
				echo '<th colspan="3">', I18N::translate('Level'), ' ', $cols + 1, '</th>';
1530
			}
1531
		}
1532
		echo '</tr><tr>';
1533
		for ($cols = 0; $cols < $max; ++$cols) {
1534
			echo '<th>', GedcomTag::getLabel('PLAC'), '</th>';
1535
			echo '<th>', I18N::translate('Latitude'), '</th>';
1536
			echo '<th>', I18N::translate('Longitude'), '</th>';
1537
		}
1538
		echo '</tr></thead><tbody>';
1539
		$countrows = 0;
1540
		$matched   = array();
1541
		while ($x < $i) {
1542
			$placestr = '';
1543
			$levels   = explode(', ', $place_list[$x]);
1544
			$parts    = count($levels);
1545
			$levels   = array_reverse($levels);
1546
			$placestr .= '<a href="placelist.php?action=show';
1547
			foreach ($levels as $pindex => $ppart) {
1548
				$placestr .= '&amp;parent[' . $pindex . ']=' . urlencode($ppart);
1549
			}
1550
			$placestr .= '">' . $place_list[$x] . "</a>";
1551
			$gedplace    = '<tr><td>' . $placestr . '</td>';
1552
			$z           = 0;
1553
			$id          = 0;
1554
			$level       = 0;
1555
			$matched[$x] = 0; // used to exclude places where the gedcom place is matched at all levels
1556
			$mapstr_edit = '<a href="#" dir="auto" onclick="edit_place_location(\'';
1557
			$mapstr_add  = '<a href="#" dir="auto" onclick="add_place_location(\'';
1558
			$mapstr3     = '';
1559
			$mapstr4     = '';
1560
			$mapstr5     = '\')" title=\'';
1561
			$mapstr6     = '\' >';
1562
			$mapstr7     = '\')">';
1563
			$mapstr8     = '</a>';
1564
			$plac        = array();
1565
			$lati        = array();
1566
			$long        = array();
1567
			while ($z < $parts) {
1568
				if ($levels[$z] == '') {
1569
					$levels[$z] = 'unknown'; // GoogleMap module uses "unknown" while GEDCOM uses , ,
1570
				}
1571
1572
				$placelist = $this->createPossiblePlaceNames($levels[$z], $z + 1); // add the necessary prefix/postfix values to the place name
1573
				foreach ($placelist as $key => $placename) {
1574
					$row =
1575
						Database::prepare("SELECT pl_id, pl_place, pl_long, pl_lati, pl_zoom FROM `##placelocation` WHERE pl_level=? AND pl_parent_id=? AND pl_place LIKE ? ORDER BY pl_place")
1576
							->execute(array($z, $id, $placename))
1577
							->fetchOneRow(PDO::FETCH_ASSOC);
1578
					if (!empty($row['pl_id'])) {
1579
						$row['pl_placerequested'] = $levels[$z]; // keep the actual place name that was requested so we can display that instead of what is in the db
1580
						break;
1581
					}
1582
				}
1583
				if ($row['pl_id'] != '') {
1584
					$id = $row['pl_id'];
0 ignored issues
show
Bug introduced by
The variable $row 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...
1585
				}
1586
1587
				if ($row['pl_place'] != '') {
1588
					$placestr2 = $mapstr_edit . $id . "&amp;level=" . $level . $mapstr3 . $mapstr5 . I18N::translate('Zoom') . ' ' . $row['pl_zoom'] . $mapstr6 . $row['pl_placerequested'] . $mapstr8;
1589
					if ($row['pl_place'] === 'unknown') {
1590
						$matched[$x]++;
1591
					}
1592
				} else {
1593
					if ($levels[$z] === 'unknown') {
1594
						$placestr2 = $mapstr_add . $id . "&amp;level=" . $level . $mapstr3 . $mapstr7 . "<strong>" . I18N::translate('unknown') . "</strong>" . $mapstr8; $matched[$x]++;
1595
					} else {
1596
						$placestr2 = $mapstr_add . $id . "&amp;place_name=" . urlencode($levels[$z]) . "&amp;level=" . $level . $mapstr3 . $mapstr7 . '<span class="danger">' . $levels[$z] . '</span>' . $mapstr8; $matched[$x]++;
1597
					}
1598
				}
1599
				$plac[$z] = '<td>' . $placestr2 . '</td>';
1600 View Code Duplication
				if ($row['pl_lati'] == '0' && $row['pl_long'] == '0') {
1601
					$lati[$z] = '<td class="danger">0</td>';
1602
				} elseif ($row['pl_lati'] != '') {
1603
					$lati[$z] = '<td>' . $row['pl_lati'] . '</td>';
1604
				} else {
1605
					$lati[$z] = '<td class="danger"><i class="fa fa-warning"></i></td>';
1606
					$matched[$x]++;
1607
				}
1608 View Code Duplication
				if ($row['pl_lati'] == '0' && $row['pl_long'] == '0') {
1609
					$long[$z] = '<td class="danger">0</td>';
1610
				} elseif ($row['pl_long'] != '') {
1611
					$long[$z] = '<td>' . $row['pl_long'] . '</td>';
1612
				} else {
1613
					$long[$z] = '<td class="danger"><i class="fa fa-warning"></i></td>';
1614
					$matched[$x]++;
1615
				}
1616
				$level++;
1617
				$mapstr3 = $mapstr3 . "&amp;parent[" . $z . "]=" . Filter::escapeJs($row['pl_placerequested']);
1618
				$mapstr4 = $mapstr4 . "&amp;parent[" . $z . "]=" . Filter::escapeJs($levels[$z]);
1619
				$z++;
1620
			}
1621
			if ($matching) {
1622
				$matched[$x] = 1;
1623
			}
1624
			if ($matched[$x] != 0) {
1625
				echo $gedplace;
1626
				$z = 0;
1627
				while ($z < $max) {
1628
					if ($z < $parts) {
1629
						echo $plac[$z];
1630
						echo $lati[$z];
1631
						echo $long[$z];
1632
					} else {
1633
						echo '<td></td>';
1634
						echo '<td></td>';
1635
						echo '<td></td>';
1636
					}
1637
					$z++;
1638
				}
1639
				echo '</tr>';
1640
				$countrows++;
1641
			}
1642
			$x++;
1643
		}
1644
		echo '</tbody>';
1645
		echo '<tfoot>';
1646
		echo '<tr>';
1647
		echo '<td colspan="', (1 + 3 * $max), '">', /* I18N: A count of places */ I18N::translate('Total places: %s', I18N::number($countrows)), '</td>';
1648
		echo '</tr>';
1649
		echo '</tfoot>';
1650
		echo '</table>';
1651
	}
1652
1653
	/**
1654
	 * Does an individual (or their spouse-families) have any facts with places?
1655
	 *
1656
	 * @param Individual $individual
1657
	 *
1658
	 * @return bool
1659
	 */
1660
	private function checkMapData(Individual $individual) {
1661
		$statement = Database::prepare(
1662
			"SELECT COUNT(*) FROM `##placelinks` WHERE pl_gid = :xref AND pl_file = :tree_id"
1663
		);
1664
		$args = array(
1665
			'xref'    => $individual->getXref(),
1666
			'tree_id' => $individual->getTree()->getTreeId(),
1667
		);
1668
1669
		if ($statement->execute($args)->fetchOne()) {
1670
			return true;
1671
		}
1672
1673
		foreach ($individual->getSpouseFamilies() as $family) {
1674
			$args['xref'] = $family->getXref();
1675
			if ($statement->execute($args)->fetchOne()) {
1676
				return true;
1677
			}
1678
		}
1679
1680
		return false;
1681
	}
1682
1683
	/**
1684
	 * Remove prefixes from a place name to allow it to be matched.
1685
	 *
1686
	 * @param string   $prefix_list
1687
	 * @param string   $place
1688
	 * @param string[] $placelist
1689
	 *
1690
	 * @return string[]
1691
	 */
1692
	private function removePrefixFromPlaceName($prefix_list, $place, $placelist) {
1693
		if ($prefix_list) {
1694
			foreach (explode(';', $prefix_list) as $prefix) {
1695
				if ($prefix && substr($place, 0, strlen($prefix) + 1) == $prefix . ' ') {
1696
					$placelist[] = substr($place, strlen($prefix) + 1);
1697
				}
1698
			}
1699
		}
1700
1701
		return $placelist;
1702
	}
1703
1704
	/**
1705
	 * Remove suffixes from a place name to allow it to be matched.
1706
	 *
1707
	 * @param string   $suffix_list
1708
	 * @param string   $place
1709
	 * @param string[] $placelist
1710
	 *
1711
	 * @return string[]
1712
	 */
1713
	private function removeSuffixFromPlaceName($suffix_list, $place, $placelist) {
1714
		if ($suffix_list) {
1715
			foreach (explode(';', $suffix_list) as $postfix) {
1716
				if ($postfix && substr($place, -strlen($postfix) - 1) == ' ' . $postfix) {
1717
					$placelist[] = substr($place, 0, strlen($place) - strlen($postfix) - 1);
1718
				}
1719
			}
1720
		}
1721
1722
		return $placelist;
1723
	}
1724
1725
	/**
1726
	 * Remove prefixes and sufixes to allow place names to be matched.
1727
	 *
1728
	 * @param string   $prefix_list
1729
	 * @param string   $suffix_list
1730
	 * @param string   $place
1731
	 * @param string[] $placelist
1732
	 *
1733
	 * @return string[]
1734
	 */
1735
	private function removePrefixAndSuffixFromPlaceName($prefix_list, $suffix_list, $place, $placelist) {
1736
		if ($prefix_list && $suffix_list) {
1737
			foreach (explode(';', $prefix_list) as $prefix) {
1738
				foreach (explode(';', $suffix_list) as $postfix) {
1739
					if ($prefix && $postfix && substr($place, 0, strlen($prefix) + 1) == $prefix . ' ' && substr($place, -strlen($postfix) - 1) == ' ' . $postfix) {
1740
						$placelist[] = substr($place, strlen($prefix) + 1, strlen($place) - strlen($prefix) - strlen($postfix) - 2);
1741
					}
1742
				}
1743
			}
1744
		}
1745
1746
		return $placelist;
1747
	}
1748
1749
	/**
1750
	 * Match placenames with different prefixes and suffixes.
1751
	 *
1752
	 * @param string $placename
1753
	 * @param int    $level
1754
	 *
1755
	 * @return string[]
1756
	 */
1757
	private function createPossiblePlaceNames($placename, $level) {
1758
		$retlist = array();
1759
		if ($level <= 9) {
1760
			$retlist = $this->removePrefixAndSuffixFromPlaceName($this->getSetting('GM_PREFIX_' . $level), $this->getSetting('GM_POSTFIX_' . $level), $placename, $retlist); // Remove both
1761
			$retlist = $this->removePrefixFromPlaceName($this->getSetting('GM_PREFIX_' . $level), $placename, $retlist); // Remove prefix
1762
			$retlist = $this->removeSuffixFromPlaceName($this->getSetting('GM_POSTFIX_' . $level), $placename, $retlist); // Remove suffix
1763
		}
1764
		$retlist[] = $placename; // Exact
1765
1766
		return $retlist;
1767
	}
1768
1769
	/**
1770
	 * Get the map co-ordinates of a place.
1771
	 *
1772
	 * @param string $place
1773
	 *
1774
	 * @return null|\stdClass
0 ignored issues
show
Documentation introduced by
Should the return type not be \stdClass|array|null? Also, consider making the array more specific, something like array<String>, or String[].

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

If the return type contains the type array, this check recommends the use of a more specific type like String[] or array<String>.

Loading history...
1775
	 */
1776
	private function getLatitudeAndLongitudeFromPlaceLocation($place) {
1777
		$parent     = explode(',', $place);
1778
		$parent     = array_reverse($parent);
1779
		$place_id   = 0;
1780
		$num_parent = count($parent);
1781
		for ($i = 0; $i < $num_parent; $i++) {
1782
			$parent[$i] = trim($parent[$i]);
1783
			if (empty($parent[$i])) {
1784
				$parent[$i] = 'unknown'; // GoogleMap module uses "unknown" while GEDCOM uses , ,
1785
			}
1786
			$placelist = $this->createPossiblePlaceNames($parent[$i], $i + 1);
1787
			foreach ($placelist as $placename) {
1788
				$pl_id = Database::prepare(
1789
					"SELECT pl_id FROM `##placelocation` WHERE pl_level=? AND pl_parent_id=? AND pl_place LIKE ? ORDER BY pl_place"
1790
				)->execute(array($i, $place_id, $placename))->fetchOne();
1791
				if (!empty($pl_id)) {
1792
					break;
1793
				}
1794
			}
1795
			if (empty($pl_id)) {
1796
				break;
1797
			}
1798
			$place_id = $pl_id;
1799
		}
1800
1801
		return Database::prepare(
1802
			"SELECT sv_lati, sv_long, sv_bearing, sv_elevation, sv_zoom, pl_lati, pl_long, pl_zoom, pl_icon, pl_level" .
1803
			" FROM `##placelocation`" .
1804
			" WHERE pl_id = ?" .
1805
			" ORDER BY pl_place"
1806
		)->execute(array($place_id))->fetchOneRow();
1807
	}
1808
1809
	/**
1810
	 * @param Fact $fact
1811
	 *
1812
	 * @return array
1813
	 */
1814
	private function getPlaceData(Fact $fact) {
1815
		$result = array();
1816
1817
		$has_latitude  = preg_match('/\n4 LATI (.+)/', $fact->getGedcom(), $match1);
1818
		$has_longitude = preg_match('/\n4 LONG (.+)/', $fact->getGedcom(), $match2);
1819
1820
		// If co-ordinates are stored in the GEDCOM then use them
1821
		if ($has_latitude && $has_longitude) {
1822
			$result = array(
1823
				'index'   => 'ID' . $match1[1] . $match2[1],
1824
				'mapdata' => array(
1825
					'class'        => 'optionbox',
1826
					'place'        => $fact->getPlace()->getFullName(),
1827
					'tooltip'      => $fact->getPlace()->getGedcomName(),
1828
					'lat'          => strtr($match1[1], array('N' => '', 'S' => '-', ',' => '.')),
1829
					'lng'          => strtr($match2[1], array('E' => '', 'W' => '-', ',' => '.')),
1830
					'pl_icon'      => '',
1831
					'pl_zoom'      => '0',
1832
					'sv_bearing'   => '0',
1833
					'sv_elevation' => '0',
1834
					'sv_lati'      => '0',
1835
					'sv_long'      => '0',
1836
					'sv_zoom'      => '0',
1837
					'events'       => '',
1838
				),
1839
			);
1840
		} else {
1841
			$place_location = $this->getLatitudeAndLongitudeFromPlaceLocation($fact->getPlace()->getGedcomName());
1842
			if ($place_location && $place_location->pl_lati && $place_location->pl_long) {
1843
				$result = array(
1844
					'index'   => 'ID' . $place_location->pl_lati . $place_location->pl_long,
1845
					'mapdata' => array(
1846
						'class'        => 'optionbox',
1847
						'place'        => $fact->getPlace()->getFullName(),
1848
						'tooltip'      => $fact->getPlace()->getGedcomName(),
1849
						'lat'          => strtr($place_location->pl_lati, array('N' => '', 'S' => '-', ',' => '.')),
1850
						'lng'          => strtr($place_location->pl_long, array('E' => '', 'W' => '-', ',' => '.')),
1851
						'pl_icon'      => $place_location->pl_icon,
1852
						'pl_zoom'      => $place_location->pl_zoom,
1853
						'sv_bearing'   => $place_location->sv_bearing,
1854
						'sv_elevation' => $place_location->sv_elevation,
1855
						'sv_lati'      => $place_location->sv_lati,
1856
						'sv_long'      => $place_location->sv_long,
1857
						'sv_zoom'      => $place_location->sv_zoom,
1858
						'events'       => '',
1859
					),
1860
				);
1861
			}
1862
		}
1863
1864
		return $result;
1865
	}
1866
1867
	/**
1868
	 * Build a map for an individual.
1869
	 *
1870
	 * @param Individual $indi
1871
	 */
1872
	private function buildIndividualMap(Individual $indi) {
1873
		$GM_MAX_ZOOM = $this->getSetting('GM_MAX_ZOOM');
1874
		$facts       = $indi->getFacts();
1875
		foreach ($indi->getSpouseFamilies() as $family) {
1876
			$facts = array_merge($facts, $family->getFacts());
1877
			// Add birth of children from this family to the facts array
1878
			foreach ($family->getChildren() as $child) {
1879
				$facts[] = $child->getFirstFact('BIRT');
1880
			}
1881
		}
1882
1883
		$facts = array_values(array_filter($facts, function ($item) {
1884
			// remove null facts (child without birth event) and
1885
			// facts without places
1886
			return !is_null($item) && !$item->getPlace()->isEmpty();
1887
		}));
1888
1889
		Functions::sortFacts($facts);
1890
1891
		// At this point we have an array of valid sorted facts
1892
		// so now build the data structures needed for the map display
1893
		$events        = array();
1894
		$unique_places = array();
1895
1896
		foreach ($facts as $fact) {
1897
			$place_data = $this->getPlaceData($fact);
1898
1899
			if (!empty($place_data)) {
1900
				$index = $place_data['index'];
1901
1902
				if ($place_data['mapdata']['pl_zoom']) {
1903
					$GM_MAX_ZOOM = min($GM_MAX_ZOOM, $place_data['mapdata']['pl_zoom']);
1904
				}
1905
				// Produce the html for the sidebar
1906
				$parent = $fact->getParent();
1907
				if ($parent instanceof Individual && $parent->getXref() !== $indi->getXref()) {
1908
					// Childs birth
1909
					$name   = '<a href="' . $parent->getHtmlUrl() . '">' . $parent->getFullName() . '</a>';
1910
					$label  = strtr($parent->getSex(), array('F' => I18N::translate('Birth of a daughter'), 'M' => I18N::translate('Birth of a son'), 'U' => I18N::translate('Birth of a child')));
1911
					$class  = 'person_box' . strtr($parent->getSex(), array('F' => 'F', 'M' => '', 'U' => 'NN'));
1912
					$evtStr = '<div class="gm-event">' . $label . '<div><strong>' . $name . '</strong></div>' . $fact->getDate()->display(true) . '</div>';
1913
				} else {
1914
					$spouse = $parent instanceof Family ? $parent->getSpouse($indi) : null;
1915
					$name   = $spouse ? '<a href="' . $spouse->getHtmlUrl() . '">' . $spouse->getFullName() . '</a>' : '';
1916
					$label  = $fact->getLabel();
1917
					$class  = 'optionbox';
1918
					if ($fact->getValue() && $spouse) {
1919
						$evtStr = '<div class="gm-event">' . $label . '<div>' . $fact->getValue() . '</div><strong>' . $name . '</strong>' . $fact->getDate()->display(true) . '</div>';
1920
					} elseif ($spouse) {
1921
						$evtStr = '<div class="gm-event">' . $label . '<div><strong>' . $name . '</strong></div>' . $fact->getDate()->display(true) . '</div>';
1922
					} elseif ($fact->getValue()) {
1923
						$evtStr = '<div class="gm-event">' . $label . '<div> ' . $fact->getValue() . '</div>' . $fact->getDate()->display(true) . '</div>';
1924
					} else {
1925
						$evtStr = '<div class="gm-event">' . $label . '<div>' . $fact->getDate()->display(true) . '</div></div>';
1926
					}
1927
				}
1928
1929
				if (empty($unique_places[$index])) {
1930
					$unique_places[$index] = $place_data['mapdata'];
1931
				}
1932
				$unique_places[$index]['events'] .= $evtStr;
1933
				$events[] = array(
1934
					'class'      => $class,
1935
					'fact_label' => $label,
1936
					'date'       => $fact->getDate()->display(true),
1937
					'info'       => $fact->getValue(),
1938
					'name'       => $name,
1939
					'place'      => '<a href="' . $fact->getPlace()->getURL() . '">' . $fact->getPlace()->getFullName() . '</a>',
1940
					'placeid'    => $index,
1941
				);
1942
			}
1943
		}
1944
1945
		if (!empty($events)) {
1946
			$places = array_keys($unique_places);
1947
			ob_start();
1948
			// Create the normal googlemap sidebar of events and children
1949
			echo '<div class="gm-events"><table class="facts_table">';
1950
1951
			foreach ($events as $event) {
1952
				$index = array_search($event['placeid'], $places);
1953
				echo '<tr>';
1954
				echo '<td class="facts_label">';
1955
				echo '<a href="#" onclick="return openInfowindow(\'', $index, '\')">', $event['fact_label'], '</a></td>';
0 ignored issues
show
Security Cross-Site Scripting introduced by
$index can contain request data and is used in output context(s) leading to a potential security vulnerability.

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
1956
				echo '<td class="', $event['class'], '">';
1957
				if ($event['info']) {
1958
					echo '<div><span class="field">', Filter::escapeHtml($event['info']), '</span></div>';
1959
				}
1960
				if ($event['name']) {
1961
					echo '<div>', $event['name'], '</div>';
1962
				}
1963
				echo '<div>', $event['place'], '</div>';
1964
				if ($event['date']) {
1965
					echo '<div>', $event['date'], '</div>';
1966
				}
1967
				echo '</td>';
1968
				echo '</tr>';
1969
			}
1970
1971
			echo '</table></div>';
1972
1973
			// *** ENABLE STREETVIEW ***
1974
			$STREETVIEW = (bool) $this->getSetting('GM_USE_STREETVIEW');
1975
			?>
1976
1977
			<script>
1978
				var map_center = new google.maps.LatLng(0, 0);
1979
				var gmarkers   = [];
1980
				var gicons     = [];
1981
				var map        = null;
1982
				var head       = '';
1983
				var dir        = '';
1984
				var svzoom     = '';
1985
1986
				var infowindow = new google.maps.InfoWindow({});
1987
1988
				gicons["red"] = new google.maps.MarkerImage("https://maps.google.com/mapfiles/marker.png",
1989
					new google.maps.Size(20, 34),
1990
					new google.maps.Point(0, 0),
1991
					new google.maps.Point(9, 34)
1992
				);
1993
1994
				var iconImage = new google.maps.MarkerImage("https://maps.google.com/mapfiles/marker.png",
1995
					new google.maps.Size(20, 34),
1996
					new google.maps.Point(0, 0),
1997
					new google.maps.Point(9, 34)
1998
				);
1999
2000
				var iconShadow = new google.maps.MarkerImage("https://www.google.com/mapfiles/shadow50.png",
2001
					new google.maps.Size(37, 34),
2002
					new google.maps.Point(0, 0),
2003
					new google.maps.Point(9, 34)
2004
				);
2005
2006
				var iconShape = {
2007
					coord: [9, 0, 6, 1, 4, 2, 2, 4, 0, 8, 0, 12, 1, 14, 2, 16, 5, 19, 7, 23, 8, 26, 9, 30, 9, 34, 11, 34, 11, 30, 12, 26, 13, 24, 14, 21, 16, 18, 18, 16, 20, 12, 20, 8, 18, 4, 16, 2, 15, 1, 13, 0],
2008
					type:  "poly"
2009
				};
2010
2011
				function getMarkerImage(iconColor) {
2012
					if (typeof(iconColor) === 'undefined' || iconColor === null) {
2013
						iconColor = 'red';
2014
					}
2015
					if (!gicons[iconColor]) {
2016
						gicons[iconColor] = new google.maps.MarkerImage(
2017
							'//maps.google.com/mapfiles/marker' + iconColor + '.png',
2018
							new google.maps.Size(20, 34),
2019
							new google.maps.Point(0, 0),
2020
							new google.maps.Point(9, 34)
2021
						);
2022
					}
2023
					return gicons[iconColor];
2024
				}
2025
2026
				var sv2_bear = null;
2027
				var sv2_elev = null;
2028
				var sv2_zoom = null;
2029
				var placer   = null;
2030
2031
				// A function to create the marker and set up the event window
2032
				function createMarker(latlng, html, tooltip, sv_lati, sv_long, sv_bearing, sv_elevation, sv_zoom, sv_point, marker_icon) {
2033
					// Use flag icon (if defined) instead of regular marker icon
2034
					if (marker_icon) {
2035
						var icon_image  = new google.maps.MarkerImage(WT_STATIC_URL + WT_MODULES_DIR + 'googlemap/' + marker_icon,
2036
							new google.maps.Size(25, 15),
2037
							new google.maps.Point(0, 0),
2038
							new google.maps.Point(12, 15));
2039
						var icon_shadow = new google.maps.MarkerImage(WT_STATIC_URL + WT_MODULES_DIR + 'googlemap/images/flag_shadow.png',
2040
							new google.maps.Size(35, 45), // Shadow size
2041
							new google.maps.Point(0, 0),  // Shadow origin
2042
							new google.maps.Point(1, 45)  // Shadow anchor is base of flagpole
2043
						);
2044
					} else {
2045
						var icon_image  = getMarkerImage('red');
2046
						var icon_shadow = iconShadow;
2047
					}
2048
2049
					// Decide if marker point is Regular (latlng) or StreetView (sv_point) derived
2050
					if (sv_point == '(0, 0)' || sv_point == '(null, null)') {
2051
						placer = latlng;
2052
					} else {
2053
						placer = sv_point;
2054
					}
2055
2056
					// Define the marker
2057
					var marker = new google.maps.Marker({
2058
						position: placer,
2059
						icon:     icon_image,
2060
						shadow:   icon_shadow,
2061
						map:      map,
2062
						title:    tooltip,
2063
						zIndex:   Math.round(latlng.lat() * -100000) << 5
2064
					});
2065
2066
					// Store the tab and event info as marker properties
2067
					marker.sv_lati  = sv_lati;
2068
					marker.sv_long  = sv_long;
2069
					marker.sv_point = sv_point;
2070
2071
					if (sv_bearing == '') {
2072
						marker.sv_bearing = 0;
2073
					} else {
2074
						marker.sv_bearing = sv_bearing;
2075
					}
2076
					if (sv_elevation == '') {
2077
						marker.sv_elevation = 5;
2078
					} else {
2079
						marker.sv_elevation = sv_elevation;
2080
					}
2081
					if (sv_zoom == '' || sv_zoom == 0 || sv_zoom == 1) {
2082
						marker.sv_zoom = 1.2;
2083
					} else {
2084
						marker.sv_zoom = sv_zoom;
2085
					}
2086
2087
					marker.sv_latlng = new google.maps.LatLng(sv_lati, sv_long);
2088
					gmarkers.push(marker);
2089
2090
					// Open infowindow when marker is clicked
2091
					google.maps.event.addListener(marker, 'click', function() {
2092
						infowindow.close();
2093
						infowindow.setContent(html);
2094
						infowindow.open(map, marker);
2095
						var panoramaOptions = {
2096
							position:          marker.position,
2097
							mode:              'html5',
2098
							navigationControl: false,
2099
							linksControl:      false,
2100
							addressControl:    false,
2101
							pov:               {
2102
								heading: sv_bearing,
2103
								pitch:   sv_elevation,
2104
								zoom:    sv_zoom
2105
							}
2106
						};
2107
2108
						// Tabs within the info-windows.
2109
						<?php if ($STREETVIEW) { ?>
2110
						google.maps.event.addListener(infowindow, 'domready', function() {
2111
							jQuery('#gm-tab-events').click(function () {
2112
								document.getElementById("gm-tab-events").classList.add('gm-tab-active');
2113
								document.getElementById("gm-tab-streetview").classList.remove('gm-tab-active');
2114
								document.getElementById("gm-pane-events").style.display = 'block';
2115
								document.getElementById("gm-pane-streetview").style.display = 'none';
2116
2117
								return false;
2118
							});
2119
							jQuery('#gm-tab-streetview').click(function () {
2120
								document.getElementById("gm-tab-events").classList.remove('gm-tab-active');
2121
								document.getElementById("gm-tab-streetview").classList.add('gm-tab-active');
2122
								document.getElementById("gm-pane-events").style.display = 'none';
2123
								document.getElementById("gm-pane-streetview").style.display = 'block';
2124
								var panorama = new google.maps.StreetViewPanorama(document.querySelector(".gm-streetview"), panoramaOptions);
2125
2126
								return false;
2127
							});
2128
						});
2129
						<?php } ?>
2130
					});
2131
				}
2132
2133
				// Opens Marker infowindow when corresponding Sidebar item is clicked
2134
				function openInfowindow(i) {
2135
					infowindow.close();
2136
					google.maps.event.trigger(gmarkers[i], 'click');
2137
					return false;
2138
				}
2139
2140
				function loadMap() {
2141
					// Create the map and mapOptions
2142
					var mapOptions = {
2143
						zoom:                     7,
2144
						center:                   map_center,
2145
						mapTypeId:                google.maps.MapTypeId.<?php echo $this->getSetting('GM_MAP_TYPE') ?>,
0 ignored issues
show
Security Cross-Site Scripting introduced by
$this->getSetting('GM_MAP_TYPE') can contain request data and is used in html attribute with single-quotes context(s) leading to a potential security vulnerability.

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
2146
						mapTypeControlOptions:    {
2147
							style: google.maps.MapTypeControlStyle.DROPDOWN_MENU  // DEFAULT, DROPDOWN_MENU, HORIZONTAL_BAR
2148
						},
2149
						navigationControl:        true,
2150
						navigationControlOptions: {
2151
							position: google.maps.ControlPosition.TOP_RIGHT,  // BOTTOM, BOTTOM_LEFT, LEFT, TOP, etc
2152
							style:    google.maps.NavigationControlStyle.SMALL  // ANDROID, DEFAULT, SMALL, ZOOM_PAN
2153
						},
2154
						streetViewControl:        true,
2155
						scrollwheel:              true
2156
					};
2157
					map = new google.maps.Map(document.querySelector('.gm-map'), mapOptions);
2158
2159
					// Close any infowindow when map is clicked
2160
					google.maps.event.addListener(map, 'click', function() {
2161
						infowindow.close();
2162
					});
2163
2164
					// Add the markers to the map
2165
2166
					// Group the markers by location
2167
					var locations = <?php echo json_encode($unique_places) ?>;
2168
2169
					// Set the Marker bounds
2170
					var bounds = new google.maps.LatLngBounds();
2171
2172
					jQuery.each(locations, function(index, location) {
2173
						var point     = new google.maps.LatLng(location.lat, location.lng); // Place Latitude, Longitude
2174
						var sv_point  = new google.maps.LatLng(location.sv_lati, location.sv_long); // StreetView Latitude and Longitide
2175
						var zoomLevel = <?php echo $GM_MAX_ZOOM ?>;
0 ignored issues
show
Security Cross-Site Scripting introduced by
$GM_MAX_ZOOM can contain request data and is used in html attribute with single-quotes context(s) leading to a potential security vulnerability.

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
2176
						var html      =
2177
					    '<div class="gm-info-window">' +
2178
					    '<div class="gm-info-window-header">' + location.place + '</div>' +
2179
					    '<ul class="gm-tabs">' +
2180
					    '<li class="gm-tab gm-tab-active" id="gm-tab-events"><a href="#"><?php echo I18N::translate('Events') ?></a></li>' +
2181
					    <?php if ($STREETVIEW) { ?>
2182
					    '<li class="gm-tab" id="gm-tab-streetview"><a href="#"><?php echo I18N::translate('Google Street View™') ?></a></li>' +
2183
					    <?php } ?>
2184
					    '</ul>' +
2185
						  '<div class="gm-panes">' +
2186
					    '<div class="gm-pane" id="gm-pane-events">' + location.events + '</div>' +
2187
					    <?php if ($STREETVIEW) { ?>
2188
					    '<div class="gm-pane" id="gm-pane-streetview" hidden><div class="gm-streetview"></div></div>' +
2189
					    <?php } ?>
2190
					    '</div>' +
2191
					    '</div>';
2192
2193
						createMarker(point, html, location.tooltip, location.sv_lati, location.sv_long, location.sv_bearing, location.sv_elevation, location.sv_zoom, sv_point, location.pl_icon);
2194
						// if streetview coordinates are available, use them for marker,
2195
						// else use the place coordinates
2196
						if (sv_point && sv_point != "(0, 0)") {
2197
							var myLatLng = sv_point;
2198
						} else {
2199
							var myLatLng = point;
2200
						}
2201
2202
						// Correct zoom level when only one marker is present
2203
						if (locations.length == 1) {
2204
							bounds.extend(myLatLng);
2205
							map.setZoom(zoomLevel);
2206
							map.setCenter(myLatLng);
2207
						} else {
2208
							bounds.extend(myLatLng);
2209
							map.fitBounds(bounds);
2210
							// Correct zoom level when multiple markers have the same coordinates
2211
							var listener1 = google.maps.event.addListenerOnce(map, "idle", function() {
2212
								if (map.getZoom() > zoomLevel) {
2213
									map.setZoom(zoomLevel);
2214
								}
2215
								google.maps.event.removeListener(listener1);
2216
							});
2217
						}
2218
					}); // end loop through location markers
2219
2220
				} // end loadMap()
2221
2222
			</script>
2223
			<?php
2224
			$html = ob_get_clean();
2225
		} else {
2226
			$html = '';
2227
		}
2228
2229
		return $html;
2230
	}
2231
2232
	/**
2233
	 * Get the Location ID.
2234
	 *
2235
	 * @param string $place
2236
	 *
2237
	 * @return int
2238
	 */
2239
	private function getPlaceLocationId($place) {
2240
		$par      = explode(',', strip_tags($place));
2241
		$par      = array_reverse($par);
2242
		$place_id = 0;
2243
		$pl_id    = 0;
2244
		$num_par  = count($par);
2245
		for ($i = 0; $i < $num_par; $i++) {
2246
			$par[$i] = trim($par[$i]);
2247
			if (empty($par[$i])) {
2248
				$par[$i] = 'unknown';
2249
			}
2250
			$placelist = $this->createPossiblePlaceNames($par[$i], $i + 1);
2251 View Code Duplication
			foreach ($placelist as $key => $placename) {
2252
				$pl_id = (int) Database::prepare(
2253
					"SELECT pl_id FROM `##placelocation` WHERE pl_level = :level AND pl_parent_id = :parent_id AND pl_place LIKE :placename"
2254
				)->execute(array(
2255
					'level'     => $i,
2256
					'parent_id' => $place_id,
2257
					'placename' => $placename,
2258
				))->fetchOne();
2259
				if ($pl_id) {
2260
					break;
2261
				}
2262
			}
2263
			if (!$pl_id) {
2264
				break;
2265
			}
2266
			$place_id = $pl_id;
2267
		}
2268
2269
		return $place_id;
2270
	}
2271
2272
	/**
2273
	 * Get the place ID.
2274
	 *
2275
	 * @param string $place
2276
	 *
2277
	 * @return int
2278
	 */
2279
	private function getPlaceId($place) {
2280
		global $WT_TREE;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
2281
2282
		$par      = explode(',', $place);
2283
		$par      = array_reverse($par);
2284
		$place_id = 0;
2285
		$pl_id    = 0;
2286
		$num_par  = count($par);
2287
		for ($i = 0; $i < $num_par; $i++) {
2288
			$par[$i]   = trim($par[$i]);
2289
			$placelist = $this->createPossiblePlaceNames($par[$i], $i + 1);
2290 View Code Duplication
			foreach ($placelist as $placename) {
2291
				$pl_id = (int) Database::prepare(
2292
					"SELECT p_id FROM `##places` WHERE p_parent_id = :place_id AND p_file = :tree_id AND p_place = :placename"
2293
				)->execute(array(
2294
					'place_id'  => $place_id,
2295
					'tree_id'   => $WT_TREE->getTreeId(),
2296
					'placename' => $placename,
2297
				))->fetchOne();
2298
				if ($pl_id) {
2299
					break;
2300
				}
2301
			}
2302
			if (!$pl_id) {
2303
				break;
2304
			}
2305
			$place_id = $pl_id;
2306
		}
2307
2308
		return $place_id;
2309
	}
2310
2311
	/**
2312
	 * Set the place IDs.
2313
	 *
2314
	 * @param int      $level
2315
	 * @param string[] $parent
2316
	 *
2317
	 * @return int
2318
	 */
2319
	private function setPlaceIdMap($level, $parent) {
2320
		$fullplace = '';
2321
		if ($level == 0) {
2322
			return 0;
2323
		} else {
2324
			for ($i = 1; $i <= $level; $i++) {
2325
				$fullplace .= $parent[$level - $i] . ', ';
2326
			}
2327
			$fullplace = substr($fullplace, 0, -2);
2328
2329
			return $this->getPlaceId($fullplace);
2330
		}
2331
	}
2332
2333
	/**
2334
	 * Set the map level.
2335
	 *
2336
	 * @param int      $level
2337
	 * @param string[] $parent
2338
	 *
2339
	 * @return int
2340
	 */
2341
	private function setLevelMap($level, $parent) {
2342
		$fullplace = '';
2343
		if ($level == 0) {
2344
			return 0;
2345
		} else {
2346
			for ($i = 1; $i <= $level; $i++) {
2347
				if ($parent[$level - $i] != '') {
2348
					$fullplace .= $parent[$level - $i] . ', ';
2349
				} else {
2350
					$fullplace .= 'Unknown, ';
2351
				}
2352
			}
2353
			$fullplace = substr($fullplace, 0, -2);
2354
2355
			return $this->getPlaceLocationId($fullplace);
2356
		}
2357
	}
2358
2359
	/**
2360
	 * Called by placelist.php
2361
	 */
2362
	public function createMap() {
2363
		global $level, $levelm, $plzoom, $WT_TREE;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
2364
2365
		Database::updateSchema(self::SCHEMA_MIGRATION_PREFIX, self::SCHEMA_SETTING_NAME, self::SCHEMA_TARGET_VERSION);
2366
2367
		$STREETVIEW = (bool) $this->getSetting('GM_USE_STREETVIEW');
2368
		$parent     = Filter::getArray('parent');
2369
		$levelm     = $this->setLevelMap($level, $parent);
2370
2371
		$latlng =
2372
			Database::prepare("SELECT pl_place, pl_id, pl_lati, pl_long, pl_zoom, sv_long, sv_lati, sv_bearing, sv_elevation, sv_zoom FROM `##placelocation` WHERE pl_id=?")
2373
			->execute(array($levelm))
2374
			->fetch(PDO::FETCH_ASSOC);
2375
2376
		echo '<table style="margin:auto; border-collapse: collapse;">';
2377
		echo '<tr style="vertical-align:top;"><td>';
2378
		if ($STREETVIEW && $level != 0) {
2379
			// Leave space for the Street View buttons, so that the maps align vertically
2380
			echo '<div id="place_map" style="margin-top:25px; border:1px solid gray; width: ', $this->getSetting('GM_PH_XSIZE'), 'px; height: ', $this->getSetting('GM_PH_YSIZE'), 'px; ';
0 ignored issues
show
Security Cross-Site Scripting introduced by
$this->getSetting('GM_PH_XSIZE') can contain request data and is used in output context(s) leading to a potential security vulnerability.

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
Security Cross-Site Scripting introduced by
$this->getSetting('GM_PH_YSIZE') can contain request data and is used in output context(s) leading to a potential security vulnerability.

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
2381
		} else {
2382
			echo '<div id="place_map" style="border:1px solid gray; width:', $this->getSetting('GM_PH_XSIZE'), 'px; height:', $this->getSetting('GM_PH_YSIZE'), 'px; ';
0 ignored issues
show
Security Cross-Site Scripting introduced by
$this->getSetting('GM_PH_XSIZE') can contain request data and is used in output context(s) leading to a potential security vulnerability.

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
Security Cross-Site Scripting introduced by
$this->getSetting('GM_PH_YSIZE') can contain request data and is used in output context(s) leading to a potential security vulnerability.

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
2383
		}
2384
		echo '"><i class="icon-loading-large"></i></div>';
2385
		echo '<script src="', $this->googleMapsScript(), '"></script>';
0 ignored issues
show
Security Cross-Site Scripting introduced by
$this->googleMapsScript() can contain request data and is used in output context(s) leading to a potential security vulnerability.

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
2386
2387
		$plzoom = $latlng['pl_zoom']; // Map zoom level
2388
2389
		if (Auth::isAdmin()) {
2390
			$placecheck_url = 'module.php?mod=googlemap&amp;mod_action=admin_placecheck';
2391
			if ($parent && isset($parent[0])) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $parent of type string[] is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
2392
				$placecheck_url .= '&amp;country=' . $parent[0];
2393
				if (isset($parent[1])) {
2394
					$placecheck_url .= '&amp;state=' . $parent[1];
2395
				}
2396
			}
2397
			$adminplaces_url = 'module.php?mod=googlemap&amp;mod_action=admin_places';
2398
			if ($latlng && isset($latlng['pl_id'])) {
2399
				$adminplaces_url .= '&amp;parent=' . $latlng['pl_id'];
2400
			}
2401
			$update_places_url = 'admin_trees_places.php?ged=' . $WT_TREE->getNameHtml() . '&amp;search=' . urlencode(implode(', ', array_reverse($parent)));
2402
			echo '<div class="gm-options">';
2403
			echo '<a href="module.php?mod=googlemap&amp;mod_action=admin_config">', I18N::translate('Google Maps™ preferences'), '</a>';
2404
			echo ' | <a href="' . $adminplaces_url . '">' . I18N::translate('Geographic data') . '</a>';
2405
			echo ' | <a href="' . $placecheck_url . '">' . I18N::translate('Place check') . '</a>';
2406
			echo ' | <a href="' . $update_places_url . '">' . I18N::translate('Update place names') . '</a>';
2407
			echo '</div>';
2408
		}
2409
		echo '</td>';
2410
2411
		if ($STREETVIEW) {
2412
			echo '<td>';
2413
2414
			global $pl_lati, $pl_long;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
2415
			if ($level >= 1) {
2416
				$pl_lati = strtr($latlng['pl_lati'], array('N' => '', 'S' => '-', ',' => '.')); // WT_placelocation lati
2417
				$pl_long = strtr($latlng['pl_long'], array('E' => '', 'W' => '-', ',' => '.')); // WT_placelocation long
2418
2419
				// Check if Streetview location parameters are stored in database
2420
				$placeid  = $latlng['pl_id']; // Placelocation place id
2421
				$sv_lat   = $latlng['sv_lati']; // StreetView Point of View Latitude
2422
				$sv_lng   = $latlng['sv_long']; // StreetView Point of View Longitude
2423
				$sv_dir   = $latlng['sv_bearing']; // StreetView Point of View Direction (degrees from North)
2424
				$sv_pitch = $latlng['sv_elevation']; // StreetView Point of View Elevation (+90 to -90 degrees (+=down, -=up)
2425
				$sv_zoom  = $latlng['sv_zoom']; // StreetView Point of View Zoom (0, 1, 2 or 3)
0 ignored issues
show
Unused Code Comprehensibility introduced by
38% 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...
2426
2427
				// Check if Street View Lati/Long are the default of 0, if so use regular Place Lati/Long to set an initial location for the panda
2428
				if ($latlng['sv_lati'] == 0 && $latlng['sv_long'] == 0) {
2429
						$sv_lat = $pl_lati;
2430
						$sv_lng = $pl_long;
2431
				}
2432
				$frameheight = $this->getSetting('GM_PH_YSIZE') + 35; // Add height of buttons
2433
2434
				?>
2435
				<iframe class="gm-streetview-frame" style="height: <?php echo $frameheight ?>px;" src="module.php?mod=googlemap&amp;mod_action=wt_street_view&amp;x=<?php echo $sv_lng ?>&amp;y=<?php echo $sv_lat ?>&amp;z=18&amp;t=2&amp;c=1&amp;s=1&amp;b=<?php echo $sv_dir ?>&amp;p=<?php echo $sv_pitch ?>&amp;m=<?php echo $sv_zoom ?>&amp;j=1&amp;k=1&amp;v=1"></iframe>
2436
				<?php
2437
				if (Auth::isAdmin()) {
2438
					?>
2439
					<div class="gm-streetview-parameters">
2440
						<form method="post" action="module.php?mod=googlemap&amp;mod_action=places_edit">
2441
							<?php echo Filter::getCsrf() ?>
2442
							<input type='hidden' name='placeid' value='<?php echo $placeid ?>'>
2443
							<input type='hidden' name='action' value='update_sv_params'>
2444
							<input type='hidden' name='destination' value='<?php echo Filter::server("REQUEST_URI") ?>'>
2445
							<label for='sv_latiText'><?php echo GedcomTag::getLabel('LATI') ?></label>
2446
							<input name='sv_latiText' id='sv_latiText' type='text' title="<?php echo $sv_lat ?>" style='width:42px;' value='<?php echo $sv_lat ?>'>
2447
							<label for='sv_longText'><?php echo GedcomTag::getLabel('LONG') ?></label>
2448
							<input name='sv_longText' id='sv_longText' type='text' title="<?php echo $sv_lng ?>" style='width:42px;' value='<?php echo $sv_lng ?>'>
2449
							<label for='sv_bearText'><?php echo /* I18N: Compass bearing (in degrees), for street-view mapping */ I18N::translate('Bearing') ?></label>
2450
							<input name='sv_bearText' id='sv_bearText' type='text' style='width:30px;' value='<?php echo $sv_dir ?>'>
2451
							<label for='sv_elevText'><?php echo /* I18N: Angle of elevation (in degrees), for street-view mapping */ I18N::translate('Elevation') ?></label>
2452
							<input name='sv_elevText' id='sv_elevText' type='text' style='width:30px;' value='<?php echo $sv_pitch ?>'>
2453
							<label for='sv_zoomText'><?php echo I18N::translate('Zoom') ?></label>
2454
							<input name='sv_zoomText' id='sv_zoomText' type='text' style='width:30px;' value='<?php echo $sv_zoom ?>'>
2455
							<input type="submit" name="Submit" value="<?php echo I18N::translate('save') ?>">
2456
						</form>
2457
					</div>
2458
					<?php
2459
				}
2460
			}
2461
			echo '</td>';
2462
		}
2463
		echo '</tr></table>';
2464
	}
2465
2466
	/**
2467
	 * Print the numbers of individuals.
2468
	 *
2469
	 * @param int      $level
2470
	 * @param string[] $parent
2471
	 */
2472
	private function printHowManyPeople($level, $parent) {
2473
		global $WT_TREE;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
2474
2475
		$stats = new Stats($WT_TREE);
2476
2477
		$place_count_indi = 0;
2478
		$place_count_fam  = 0;
2479
		if (!isset($parent[$level - 1])) {
2480
			$parent[$level - 1] = '';
2481
		}
2482
		$p_id = $this->setPlaceIdMap($level, $parent);
2483
		$indi = $stats->statsPlaces('INDI', false, $p_id);
0 ignored issues
show
Documentation introduced by
false is of type boolean, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
2484
		$fam  = $stats->statsPlaces('FAM', false, $p_id);
0 ignored issues
show
Documentation introduced by
false is of type boolean, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
2485
		foreach ($indi as $place) {
2486
			$place_count_indi = $place['tot'];
2487
		}
2488
		foreach ($fam as $place) {
2489
			$place_count_fam = $place['tot'];
2490
		}
2491
		echo '<br><br>', I18N::translate('Individuals'), ': ', $place_count_indi, ', ', I18N::translate('Families'), ': ', $place_count_fam;
2492
	}
2493
2494
	/**
2495
	 * Print the flags and markers.
2496
	 *
2497
	 * @param string[] $place2
2498
	 * @param int      $level
2499
	 * @param string[] $parent
2500
	 * @param int      $levelm
2501
	 * @param string   $linklevels
2502
	 */
2503
	private function printGoogleMapMarkers($place2, $level, $parent, $levelm, $linklevels) {
2504
		if (!$place2['lati'] || !$place2['long']) {
2505
			echo 'var icon_type = new google.maps.MarkerImage();';
2506
			echo 'icon_type.image = "', WT_STATIC_URL, WT_MODULES_DIR, 'googlemap/images/marker_yellow.png";';
2507
			echo 'icon_type.shadow = "', WT_STATIC_URL, WT_MODULES_DIR, 'googlemap/images/shadow50.png";';
2508
			echo 'icon_type.iconSize = google.maps.Size(20, 34);';
2509
			echo 'icon_type.shadowSize = google.maps.Size(37, 34);';
2510
			echo 'var point = new google.maps.LatLng(0, 0);';
2511
			echo 'var marker = createMarker(point, "<div style=\"width: 250px;\"><a href=\"?action=find', $linklevels, '&amp;parent[' . $level . ']=';
2512
			if ($place2['place'] == 'Unknown') {
2513
				echo '\"><br>';
2514
			} else {
2515
				echo addslashes($place2['place']), '\"><br>';
2516
			}
2517 View Code Duplication
			if (($place2['icon'] !== null) && ($place2['icon'] !== '')) {
2518
				echo '<img src=\"', WT_STATIC_URL, WT_MODULES_DIR, 'googlemap/', $place2['icon'], '\">&nbsp;&nbsp;';
2519
			}
2520
			if ($place2['place'] == 'Unknown') {
2521
					echo I18N::translate('unknown');
2522
			} else {
2523
				echo addslashes($place2['place']);
2524
			}
2525
			echo '</a>';
2526
			$parent[$level] = $place2['place'];
2527
			$this->printHowManyPeople($level + 1, $parent);
2528
			echo '<br>', I18N::translate('This place has no coordinates');
2529
			if (Auth::isAdmin()) {
2530
				echo '<br><a href=\"module.php?mod=googlemap&amp;mod_action=admin_places&amp;parent=', $levelm, '&amp;display=inactive\">', I18N::translate('Geographic data'), '</a>';
2531
			}
2532
			echo '</div>", icon_type, "', str_replace(array('&lrm;', '&rlm;'), array(WT_UTF8_LRM, WT_UTF8_RLM), addslashes($place2['place'])), '");';
2533
		} else {
2534
			$lati = strtr($place2['lati'], array('N' => '', 'S' => '-', ',' => '.'));
2535
			$long = strtr($place2['long'], array('E' => '', 'W' => '-', ',' => '.'));
2536
			//delete leading zero
2537 View Code Duplication
			if ($lati >= 0) {
2538
				$lati = abs($lati);
2539
			} elseif ($lati < 0) {
2540
				$lati = '-' . abs($lati);
2541
			}
2542 View Code Duplication
			if ($long >= 0) {
2543
				$long = abs($long);
2544
			} elseif ($long < 0) {
2545
				$long = '-' . abs($long);
2546
			}
2547
2548
			echo 'var icon_type = new google.maps.MarkerImage();';
2549
			if ($place2['icon'] !== null && $place2['icon'] !== '' && $this->getSetting('GM_PH_MARKER') === 'G_FLAG') {
2550
				echo ' icon_type.image = "', WT_STATIC_URL, WT_MODULES_DIR, 'googlemap/', $place2['icon'], '";';
2551
				echo ' icon_type.shadow = "', WT_STATIC_URL, WT_MODULES_DIR, 'googlemap/images/flag_shadow.png";';
2552
				echo ' icon_type.iconSize = new google.maps.Size(25, 15);';
2553
				echo ' icon_type.shadowSize = new google.maps.Size(35, 45);';
2554
			}
2555
			echo 'var point = new google.maps.LatLng(', $lati, ', ', $long, ');';
2556
			echo 'var marker = createMarker(point, "<div style=\"width: 250px;\"><a href=\"?action=find', $linklevels;
2557
			echo '&amp;parent[', $level, ']=';
2558
			if ($place2['place'] !== 'Unknown') {
2559
				echo Filter::escapeJs($place2['place']);
2560
			}
2561
			echo '\"><br>';
2562 View Code Duplication
			if ($place2['icon'] !== null && $place2['icon'] !== '') {
2563
				echo '<img src=\"', WT_STATIC_URL, WT_MODULES_DIR, 'googlemap/', $place2['icon'], '\">&nbsp;&nbsp;';
2564
			}
2565 View Code Duplication
				if ($place2['place'] === 'Unknown') {
2566
					echo I18N::translate('unknown');
2567
				} else {
2568
					echo Filter::escapeJs($place2['place']);
2569
				}
2570
			echo '</a>';
2571
			$parent[$level] = $place2['place'];
2572
			$this->printHowManyPeople($level + 1, $parent);
2573
			echo '</div>", icon_type, "', Filter::escapeJs($place2['place']), '");';
2574
		}
2575
	}
2576
2577
	/**
2578
	 * Called by placelist.php
2579
	 *
2580
	 * @param int      $numfound
2581
	 * @param int      $level
2582
	 * @param string[] $parent
2583
	 * @param string   $linklevels
2584
	 * @param string[] $place_names
2585
	 */
2586
	public function mapScripts($numfound, $level, $parent, $linklevels, $place_names) {
2587
		global $plzoom, $controller;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
2588
2589
		$controller->addInlineJavascript('
2590
			jQuery("head").append(\'<link rel="stylesheet" type="text/css" href="' . WT_STATIC_URL . WT_MODULES_DIR . 'googlemap/css/wt_v3_googlemap.css" />\');
2591
			var numMarkers = "' . $numfound . '";
2592
			var mapLevel   = "' . $level . '";
2593
			var placezoom  = "' . $plzoom . '";
2594
			var infowindow = new google.maps.InfoWindow({
2595
				// size: new google.maps.Size(150,50),
2596
				// maxWidth: 600
2597
			});
2598
2599
			var map_center = new google.maps.LatLng(0,0);
2600
			var map = "";
2601
			var bounds = new google.maps.LatLngBounds ();
2602
			var markers = [];
2603
			var gmarkers = [];
2604
			var i = 0;
2605
2606
			// Create the map and mapOptions
2607
			var mapOptions = {
2608
				zoom: 8,
2609
				center: map_center,
2610
				mapTypeId: google.maps.MapTypeId.' . $this->getSetting('GM_MAP_TYPE') . ',
2611
				mapTypeControlOptions: {
2612
					style: google.maps.MapTypeControlStyle.DROPDOWN_MENU // DEFAULT, DROPDOWN_MENU, HORIZONTAL_BAR
2613
				},
2614
				navigationControl: true,
2615
				navigationControlOptions: {
2616
					position: google.maps.ControlPosition.TOP_RIGHT, // BOTTOM, BOTTOM_LEFT, LEFT, TOP, etc
2617
					style: google.maps.NavigationControlStyle.SMALL  // ANDROID, DEFAULT, SMALL, ZOOM_PAN
2618
				},
2619
				streetViewControl: false, // Show Pegman or not
2620
				scrollwheel: true
2621
			};
2622
			map = new google.maps.Map(document.getElementById("place_map"), mapOptions);
2623
2624
			// Close any infowindow when map is clicked
2625
			google.maps.event.addListener(map, "click", function() {
2626
				infowindow.close();
2627
			});
2628
2629
			// If only one marker, set zoom level to that of place in database
2630
			if (mapLevel != 0) {
2631
				var pointZoom = placezoom;
2632
			} else {
2633
				var pointZoom = 1;
2634
			}
2635
2636
			// Creates a marker whose info window displays the given name
2637
			function createMarker(point, html, icon, name) {
2638
				// Choose icon and shadow ============
2639
				if (icon.image && ' . $level . '<=3) {
2640
					if (icon.image!="' . WT_STATIC_URL . WT_MODULES_DIR . 'googlemap/images/marker_yellow.png") {
2641
						var iconImage = new google.maps.MarkerImage(icon.image,
2642
						new google.maps.Size(25, 15),
2643
						new google.maps.Point(0,0),
2644
						new google.maps.Point(12, 15));
2645
						var iconShadow = new google.maps.MarkerImage("' . WT_STATIC_URL . WT_MODULES_DIR . 'googlemap/images/flag_shadow.png",
2646
						new google.maps.Size(35, 45),
2647
						new google.maps.Point(0,0),
2648
						new google.maps.Point(1, 45));
2649
					} else {
2650
						var iconImage = new google.maps.MarkerImage(icon.image,
2651
						new google.maps.Size(20, 34),
2652
						new google.maps.Point(0,0),
2653
						new google.maps.Point(9, 34));
2654
						var iconShadow = new google.maps.MarkerImage("https://www.google.com/mapfiles/shadow50.png",
2655
						new google.maps.Size(37, 34),
2656
						new google.maps.Point(0,0),
2657
						new google.maps.Point(9, 34));
2658
					}
2659
				} else {
2660
					var iconImage = new google.maps.MarkerImage("https://maps.google.com/mapfiles/marker.png",
2661
					new google.maps.Size(20, 34),
2662
					new google.maps.Point(0,0),
2663
					new google.maps.Point(9, 34));
2664
					var iconShadow = new google.maps.MarkerImage("https://www.google.com/mapfiles/shadow50.png",
2665
					new google.maps.Size(37, 34),
2666
					new google.maps.Point(0,0),
2667
					new google.maps.Point(9, 34));
2668
				}
2669
				var posn = new google.maps.LatLng(0,0);
2670
				var marker = new google.maps.Marker({
2671
					position: point,
2672
					icon: iconImage,
2673
					shadow: iconShadow,
2674
					map: map,
2675
					title: name
2676
				});
2677
				// Show this markers name in the info window when it is clicked
2678
				google.maps.event.addListener(marker, "click", function() {
2679
					infowindow.close();
2680
					infowindow.setContent(html);
2681
					infowindow.open(map, marker);
2682
				});
2683
				// === Store the tab, category and event info as marker properties ===
2684
				marker.mypoint = point;
2685
				marker.mytitle = name;
2686
				marker.myposn = posn;
2687
				gmarkers.push(marker);
2688
				bounds.extend(marker.position);
2689
2690
				// If only one marker use database place zoom level rather than fitBounds of markers
2691
				if (numMarkers > 1) {
2692
					map.fitBounds(bounds);
2693
				} else {
2694
					map.setCenter(bounds.getCenter());
2695
					map.setZoom(parseFloat(pointZoom));
2696
				}
2697
				return marker;
2698
			}
2699
		');
2700
2701
		$levelm = $this->setLevelMap($level, $parent);
2702
2703
		//create markers
2704
		ob_start();
2705
2706
		if ($numfound == 0 && $level > 0) {
2707
			// show the current place on the map
2708
2709
			$place = Database::prepare("SELECT pl_id AS place_id, pl_place AS place, pl_lati AS lati, pl_long AS `long`, pl_zoom AS zoom, pl_icon AS icon FROM `##placelocation` WHERE pl_id=?")
2710
				->execute(array($levelm))
2711
				->fetch(PDO::FETCH_ASSOC);
2712
2713
			if ($place) {
2714
				// re-calculate the hierarchy information required to display the current place
2715
				$thisloc = $parent;
2716
				array_pop($thisloc);
2717
				$thislevel      = $level - 1;
2718
				$thislinklevels = substr($linklevels, 0, strrpos($linklevels, '&amp;'));
2719
2720
				$this->printGoogleMapMarkers($place, $thislevel, $thisloc, $place['place_id'], $thislinklevels);
0 ignored issues
show
Documentation introduced by
$place is of type object<stdClass>|array, but the function expects a array<integer,string>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
2721
			}
2722
		}
2723
2724
		// display any sub-places
2725
		$placeidlist = array();
2726
		foreach ($place_names as $placename) {
2727
			$thisloc     = $parent;
2728
			$thisloc[]   = $placename;
2729
			$this_levelm = $this->setLevelMap($level + 1, $thisloc);
2730
			if ($this_levelm) {
2731
				$placeidlist[] = $this_levelm;
2732
			}
2733
		}
2734
2735
		// flip the array (thus removing duplicates)
2736
		$placeidlist = array_flip($placeidlist);
2737
		// remove entry for parent location
2738
		unset($placeidlist[$levelm]);
2739
2740
		if (!empty($placeidlist)) {
2741
			// the keys are all we care about (this reverses the earlier array_flip, and ensures there are no "holes" in the array)
2742
			$placeidlist = array_keys($placeidlist);
2743
			// note: this implode/array_fill code generates one '?' for each entry in the $placeidlist array
2744
			$placelist =
2745
				Database::prepare(
2746
					"SELECT pl_id as place_id, pl_place as place, pl_lati as lati, pl_long as `long`, pl_zoom as zoom, pl_icon as icon" .
2747
					" FROM `##placelocation` WHERE pl_id IN (" . implode(',', array_fill(0, count($placeidlist), '?')) . ')'
2748
				)->execute($placeidlist)
2749
				->fetchAll(PDO::FETCH_ASSOC);
2750
2751
			foreach ($placelist as $place) {
2752
				$this->printGoogleMapMarkers($place, $level, $parent, $place['place_id'], $linklevels);
0 ignored issues
show
Bug introduced by
It seems like $place defined by $place on line 2751 can also be of type object<stdClass>; however, Fisharebest\Webtrees\Mod...printGoogleMapMarkers() does only seem to accept array<integer,string>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
2753
			}
2754
		}
2755
		$controller->addInlineJavascript(ob_get_clean());
2756
	}
2757
2758
	/**
2759
	 * Take a place id and find its place in the hierarchy
2760
	 * Input: place ID
2761
	 * Output: ordered array of id=>name values, starting with the Top level
2762
	 * e.g. 0=>"Top level", 16=>"England", 19=>"London", 217=>"Westminster"
2763
	 *
2764
	 * @param int $id
2765
	 *
2766
	 * @return string[]
2767
	 */
2768
	private function placeIdToHierarchy($id) {
2769
		$statement = Database::prepare("SELECT pl_parent_id, pl_place FROM `##placelocation` WHERE pl_id=?");
2770
		$arr       = array();
2771
		while ($id != 0) {
2772
			$row = $statement->execute(array($id))->fetchOneRow();
2773
			$arr = array($id => $row->pl_place) + $arr;
2774
			$id  = $row->pl_parent_id;
2775
		}
2776
2777
		return $arr;
2778
	}
2779
2780
	/**
2781
	 * Get the highest index.
2782
	 *
2783
	 * @return int
2784
	 */
2785
	private function getHighestIndex() {
2786
		return (int) Database::prepare("SELECT MAX(pl_id) FROM `##placelocation`")->fetchOne();
2787
	}
2788
2789
	/**
2790
	 * Get the highest level.
2791
	 *
2792
	 * @return int
2793
	 */
2794
	private function getHighestLevel() {
2795
		return (int) Database::prepare("SELECT MAX(pl_level) FROM `##placelocation`")->fetchOne();
2796
	}
2797
2798
	/**
2799
	 * Find all of the places in the hierarchy
2800
	 *
2801
	 * @param int  $parent_id
2802
	 * @param bool $inactive
2803
	 *
2804
	 * @return array[]
2805
	 */
2806
	private function getPlaceListLocation($parent_id, $inactive = false) {
2807
		if ($inactive) {
2808
			$rows = Database::prepare(
2809
					"SELECT pl_id, pl_place, pl_lati, pl_long, pl_zoom, pl_icon" .
2810
					" FROM `##placelocation`" .
2811
					" WHERE pl_parent_id = :parent_id" .
2812
					" ORDER BY pl_place COLLATE :collation"
2813
				)->execute(array(
2814
					'parent_id' => $parent_id,
2815
					'collation' => I18N::collation(),
2816
				))->fetchAll();
2817
		} else {
2818
			$rows = Database::prepare(
2819
				"SELECT DISTINCT pl_id, pl_place, pl_lati, pl_long, pl_zoom, pl_icon" .
2820
				" FROM `##placelocation`" .
2821
				" INNER JOIN `##places` ON `##placelocation`.pl_place=`##places`.p_place" .
2822
				" WHERE pl_parent_id = :parent_id" .
2823
				" ORDER BY pl_place COLLATE :collation"
2824
			)->execute(array(
2825
				'parent_id' => $parent_id,
2826
				'collation' => I18N::collation(),
2827
			))->fetchAll();
2828
		}
2829
2830
		$placelist = array();
2831
		foreach ($rows as $row) {
2832
			$placelist[] = array(
2833
				'place_id' => $row->pl_id,
2834
				'place'    => $row->pl_place,
2835
				'lati'     => $row->pl_lati,
2836
				'long'     => $row->pl_long,
2837
				'zoom'     => $row->pl_zoom,
2838
				'icon'     => $row->pl_icon,
2839
			);
2840
		}
2841
2842
		return $placelist;
2843
	}
2844
2845
	/**
2846
	 * Set the output level.
2847
	 *
2848
	 * @param int $parent_id
2849
	 */
2850
	private function outputLevel($parent_id) {
2851
		$tmp      = $this->placeIdToHierarchy($parent_id);
2852
		$maxLevel = $this->getHighestLevel();
2853
		if ($maxLevel > 8) {
2854
			$maxLevel = 8;
2855
		}
2856
		$prefix = implode(';', $tmp);
2857
		if ($prefix != '') {
2858
			$prefix .= ';';
2859
		}
2860
		$suffix = str_repeat(';', $maxLevel - count($tmp));
2861
		$level  = count($tmp);
2862
2863
		$rows = Database::prepare(
2864
			"SELECT pl_id, pl_place, pl_long, pl_lati, pl_zoom, pl_icon FROM `##placelocation` WHERE pl_parent_id=? ORDER BY pl_place"
2865
		)->execute(array($parent_id))->fetchAll();
2866
2867
		foreach ($rows as $row) {
2868
			echo $level, ';', $prefix, $row->pl_place, $suffix, ';', $row->pl_long, ';', $row->pl_lati, ';', $row->pl_zoom, ';', $row->pl_icon, "\r\n";
2869
			if ($level < $maxLevel) {
2870
				$this->outputLevel($row->pl_id);
2871
			}
2872
		}
2873
	}
2874
2875
	/**
2876
	 * recursively find all of the csv files on the server
2877
	 *
2878
	 * @param string $path
2879
	 *
2880
	 * @return string[]
2881
	 */
2882
	private function findFiles($path) {
2883
		$placefiles = array();
2884
2885
		try {
2886
			$di = new \RecursiveDirectoryIterator($path, \RecursiveDirectoryIterator::SKIP_DOTS);
2887
			$it = new \RecursiveIteratorIterator($di);
2888
2889
			foreach ($it as $file) {
2890
				if ($file->getExtension() == "csv") {
2891
					$placefiles[] = '/' . $file->getFilename();
2892
				}
2893
			}
2894
		} catch (\Exception $e) {
2895
			Log::addErrorLog(basename($e->getFile()) . ' - line: ' . $e->getLine() . ' - ' . $e->getMessage());
2896
		}
2897
2898
		return $placefiles;
2899
	}
2900
2901
	/**
2902
	 * Edit places.
2903
	 */
2904
	private function placesEdit() {
2905
		$GM_MAX_ZOOM = $this->getSetting('GM_MAX_ZOOM');
2906
2907
		$action     = Filter::post('action', null, Filter::get('action'));
2908
		$placeid    = Filter::post('placeid', null, Filter::get('placeid'));
2909
		$place_name = Filter::post('place_name', null, Filter::get('place_name'));
2910
		$placeid    = (int) $placeid; // Convert empty string to zero
2911
2912
		// Update Street View fields fields
2913
		if ($action === 'update_sv_params' && Auth::isAdmin() && Filter::checkCsrf()) {
2914
			Database::prepare(
2915
				"UPDATE `##placelocation`" .
2916
				" SET sv_lati = :sv_latitude, sv_long = :sv_longitude, sv_bearing = :sv_bearing, sv_elevation = :sv_elevation, sv_zoom = :sv_zoom" .
2917
				" WHERE pl_id = :place_id"
2918
			)->execute(array(
2919
				'sv_latitude'  => (float) Filter::post('sv_latiText'),
2920
				'sv_longitude' => (float) Filter::post('sv_longText'),
2921
				'sv_bearing'   => (float) Filter::post('sv_bearText'),
2922
				'sv_elevation' => (float) Filter::post('sv_elevText'),
2923
				'sv_zoom'      => (float) Filter::post('sv_zoomText'),
2924
				'place_id'     => $placeid,
2925
			));
2926
			// TODO - submit this data via AJAX, so we won't need to redraw the page.
0 ignored issues
show
Coding Style Best Practice introduced by
Comments for TODO tasks are often forgotten in the code; it might be better to use a dedicated issue tracker.
Loading history...
2927
			header('Location: ' . Filter::post('destination', null, 'index.php'));
2928
2929
			return;
2930
		}
2931
2932
		$controller = new SimpleController;
2933
		$controller
2934
			->restrictAccess(Auth::isAdmin())
2935
			->setPageTitle(I18N::translate('Geographic data'))
2936
			->addInlineJavascript('jQuery("<link>", {rel: "stylesheet", type: "text/css", href: "' . WT_STATIC_URL . WT_MODULES_DIR . 'googlemap/css/wt_v3_googlemap.css"}).appendTo("head");')
2937
			->pageHeader();
2938
2939
		$where_am_i = $this->placeIdToHierarchy($placeid);
2940
		$level      = count($where_am_i);
2941
2942
		if ($action == 'addrecord' && Auth::isAdmin()) {
2943
			$statement =
2944
				Database::prepare("INSERT INTO `##placelocation` (pl_id, pl_parent_id, pl_level, pl_place, pl_long, pl_lati, pl_zoom, pl_icon) VALUES (?, ?, ?, ?, ?, ?, ?, ?)");
2945
2946
			if ((Filter::post('LONG_CONTROL') == '') || (Filter::post('NEW_PLACE_LONG') == '') || (Filter::post('NEW_PLACE_LATI') == '')) {
2947
				$statement->execute(array($this->getHighestIndex() + 1, $placeid, $level, Filter::post('NEW_PLACE_NAME'), null, null, Filter::post('NEW_ZOOM_FACTOR'), Filter::post('icon')));
2948 View Code Duplication
			} else {
2949
				$statement->execute(array($this->getHighestIndex() + 1, $placeid, $level, Filter::post('NEW_PLACE_NAME'), Filter::post('LONG_CONTROL') . Filter::post('NEW_PLACE_LONG'), Filter::post('LATI_CONTROL') . Filter::post('NEW_PLACE_LATI'), Filter::post('NEW_ZOOM_FACTOR'), Filter::post('icon')));
2950
			}
2951
2952
			$controller->addInlineJavascript('closePopupAndReloadParent();');
2953
2954
			return;
2955
		}
2956
2957
		if ($action == 'updaterecord' && Auth::isAdmin()) {
2958
			$statement =
2959
				Database::prepare("UPDATE `##placelocation` SET pl_place=?, pl_lati=?, pl_long=?, pl_zoom=?, pl_icon=? WHERE pl_id=?");
2960
2961
			if ((Filter::post('LONG_CONTROL') == '') || (Filter::post('NEW_PLACE_LONG') == '') || (Filter::post('NEW_PLACE_LATI') == '')) {
2962
				$statement->execute(array(Filter::post('NEW_PLACE_NAME'), null, null, Filter::post('NEW_ZOOM_FACTOR'), Filter::post('icon'), $placeid));
2963 View Code Duplication
			} else {
2964
				$statement->execute(array(Filter::post('NEW_PLACE_NAME'), Filter::post('LATI_CONTROL') . Filter::post('NEW_PLACE_LATI'), Filter::post('LONG_CONTROL') . Filter::post('NEW_PLACE_LONG'), Filter::post('NEW_ZOOM_FACTOR'), Filter::post('icon'), $placeid));
2965
			}
2966
2967
			$controller->addInlineJavascript('closePopupAndReloadParent();');
2968
2969
			return;
2970
		}
2971
2972
		if ($action === 'update') {
2973
			// --- find the place in the file
2974
			$row =
2975
				Database::prepare("SELECT pl_place, pl_lati, pl_long, pl_icon, pl_parent_id, pl_level, pl_zoom FROM `##placelocation` WHERE pl_id=?")
2976
				->execute(array($placeid))
2977
				->fetchOneRow();
2978
			$place_name       = $row->pl_place;
2979
			$place_icon       = $row->pl_icon;
2980
			$selected_country = explode("/", $place_icon);
2981
			if (isset($selected_country[1]) && $selected_country[1] !== 'flags') {
2982
				$selected_country = $selected_country[1];
2983
			} else {
2984
				$selected_country = 'Countries';
2985
			}
2986
			$parent_id   = $row->pl_parent_id;
2987
			$level       = $row->pl_level;
2988
			$zoomfactor  = $row->pl_zoom;
2989
			$parent_lati = 0.0;
2990
			$parent_long = 0.0;
2991
			if ($row->pl_lati !== null && $row->pl_long !== null) {
2992
				$place_lati = (float) (str_replace(array('N', 'S', ','), array('', '-', '.'), $row->pl_lati));
2993
				$place_long = (float) (str_replace(array('E', 'W', ','), array('', '-', '.'), $row->pl_long));
2994
			} else {
2995
				$place_lati = 0.0;
2996
				$place_long = 0.0;
2997
				$zoomfactor = 1;
2998
			}
2999
3000
			do {
3001
				$row =
3002
					Database::prepare("SELECT pl_lati, pl_long, pl_parent_id, pl_zoom FROM `##placelocation` WHERE pl_id=?")
3003
					->execute(array($parent_id))
3004
					->fetchOneRow();
3005
				if (!$row) {
3006
					break;
3007
				}
3008
				if ($row->pl_lati !== null && $row->pl_long !== null) {
3009
					$parent_lati = (float) (str_replace(array('N', 'S', ','), array('', '-', '.'), $row->pl_lati));
3010
					$parent_long = (float) (str_replace(array('E', 'W', ','), array('', '-', '.'), $row->pl_long));
3011
					if ($zoomfactor == 1) {
3012
						$zoomfactor = $row->pl_zoom;
3013
					}
3014
				}
3015
				$parent_id = $row->pl_parent_id;
3016
			} while ($row->pl_parent_id != 0 && $row->pl_lati === null && $row->pl_long === null);
3017
3018
			echo '<b>', Filter::escapeHtml(str_replace('Unknown', I18N::translate('unknown'), implode(I18N::$list_separator, array_reverse($where_am_i, true)))), '</b><br>';
3019
		}
3020
3021
		if ($action === 'add') {
3022
			// --- find the parent place in the file
3023
			if ($placeid != 0) {
3024
				$place_lati  = 0.0;
3025
				$place_long  = 0.0;
3026
				$zoomfactor  = 1;
3027
				$parent_lati = 0.0;
3028
				$parent_long = 0.0;
3029
				$place_icon  = '';
3030
				$parent_id   = $placeid;
3031
				do {
3032
					$row =
3033
						Database::prepare("SELECT pl_lati, pl_long, pl_parent_id, pl_zoom, pl_level FROM `##placelocation` WHERE pl_id=?")
3034
						->execute(array($parent_id))
3035
						->fetchOneRow();
3036
					if ($row->pl_lati !== null && $row->pl_long !== null) {
3037
						$parent_lati = strtr($row->pl_lati, array('N' => '', 'S' => '-', ',' => '.'));
3038
						$parent_long = strtr($row->pl_long, array('E' => '', 'W' => '-', ',' => '.'));
3039
						$zoomfactor  = min($row->pl_zoom, $GM_MAX_ZOOM);
3040
						$level       = $row->pl_level + 1;
3041
					}
3042
					$parent_id = $row->pl_parent_id;
3043
				} while ($row->pl_parent_id != 0 && $row->pl_lati === null && $row->pl_long === null);
3044
			} else {
3045
				$place_lati  = 0.0;
3046
				$place_long  = 0.0;
3047
				$parent_lati = 0.0;
3048
				$parent_long = 0.0;
3049
				$place_icon  = '';
3050
				$parent_id   = 0;
3051
				$level       = 0;
3052
				$zoomfactor  = $this->getSetting('GM_MIN_ZOOM');
3053
			}
3054
			$selected_country = 'Countries';
3055
3056
			if ($place_name == '') {
3057
				echo '<b>', I18N::translate('unknown');
3058
			} else {
3059
				echo '<b>', $place_name;
3060
			}
3061
			if (count($where_am_i) > 0) {
3062
				echo ', ', Filter::escapeHtml(str_replace('Unknown', I18N::translate('unknown'), implode(I18N::$list_separator, array_reverse($where_am_i, true)))), '</b><br>';
3063
			}
3064
			echo '</b><br>';
3065
		}
3066
3067
		?>
3068
3069
			<script src="<?php echo $this->googleMapsScript() ?>"></script>
0 ignored issues
show
Security Cross-Site Scripting introduced by
$this->googleMapsScript() can contain request data and is used in html attribute with double-quotes context(s) leading to a potential security vulnerability.

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
3070
			<script>
3071
			var map;
3072
			var marker;
3073
			var zoom;
3074
			var pl_name = '<?php echo Filter::escapeJs($place_name) ?>';
3075
			<?php if ($place_lati !== 0.0 && $place_long !== 0.0) { ?>
3076
				var latlng = new google.maps.LatLng(<?php echo $place_lati ?>, <?php echo $place_long ?>);
0 ignored issues
show
Bug introduced by
The variable $place_lati 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...
Bug introduced by
The variable $place_long 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...
3077
			<?php } else {?>
3078
				var latlng = new google.maps.LatLng(<?php echo $parent_lati ?>, <?php echo $parent_long ?>);
0 ignored issues
show
Bug introduced by
The variable $parent_lati 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...
Bug introduced by
The variable $parent_long 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...
3079
			<?php } ?>
3080
			var pl_zoom = <?php echo $zoomfactor ?>;
0 ignored issues
show
Bug introduced by
The variable $zoomfactor 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...
Security Cross-Site Scripting introduced by
$zoomfactor can contain request data and is used in output context(s) leading to a potential security vulnerability.

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
3081
			var polygon1;
3082
			var polygon2;
3083
			var geocoder;
3084
			var mapType;
3085
3086
			var infowindow = new google.maps.InfoWindow({
3087
				//
3088
			});
3089
3090
			function geocodePosition(pos) {
3091
				geocoder.geocode({
3092
					latLng: pos
3093
				}, function(responses) {
3094
					if (responses && responses.length > 0) {
3095
						updateMarkerAddress(responses[0].formatted_address);
3096
					} else {
3097
						updateMarkerAddress('Cannot determine address at this location.');
3098
					}
3099
				});
3100
			}
3101
3102
			/**
3103
			 * Redraw the map, centered and zoomed on the selected point.
3104
			 *
3105
			 * @param event
3106
			 */
3107
			function updateMap(event) {
3108
				var point;
3109
				var zoom = parseInt(document.editplaces.NEW_ZOOM_FACTOR.value);
3110
				var latitude;
3111
				var longitude;
3112
3113
				if ((document.editplaces.NEW_PLACE_LATI.value == '') ||
3114
					(document.editplaces.NEW_PLACE_LONG.value == '')) {
3115
					latitude = parseFloat(document.editplaces.parent_lati.value).toFixed(5);
3116
					longitude = parseFloat(document.editplaces.parent_long.value).toFixed(5);
3117
					point = new google.maps.LatLng(latitude, longitude);
3118
				} else {
3119
					latitude = parseFloat(document.editplaces.NEW_PLACE_LATI.value).toFixed(5);
3120
					longitude = parseFloat(document.editplaces.NEW_PLACE_LONG.value).toFixed(5);
3121
					document.editplaces.NEW_PLACE_LATI.value = latitude;
3122
					document.editplaces.NEW_PLACE_LONG.value = longitude;
3123
3124
					if (event == 'flag_drag') {
3125
						if (longitude < 0.0 ) {
3126
							longitude = longitude * -1;
3127
							document.editplaces.NEW_PLACE_LONG.value = longitude;
3128
							document.editplaces.LONG_CONTROL.value = 'W';
3129
						} else {
3130
							document.editplaces.NEW_PLACE_LONG.value = longitude;
3131
							document.editplaces.LONG_CONTROL.value = 'E';
3132
						}
3133
						if (latitude < 0.0 ) {
3134
							latitude = latitude * -1;
3135
							document.editplaces.NEW_PLACE_LATI.value = latitude;
3136
							document.editplaces.LATI_CONTROL.value = 'S';
3137
						} else {
3138
							document.editplaces.NEW_PLACE_LATI.value = latitude;
3139
							document.editplaces.LATI_CONTROL.value = 'N';
3140
						}
3141
3142
						if (document.editplaces.LATI_CONTROL.value == 'S') {
3143
							latitude = latitude * -1;
3144
						}
3145
						if (document.editplaces.LONG_CONTROL.value == 'W') {
3146
							longitude = longitude * -1;
3147
						}
3148
						point = new google.maps.LatLng(latitude, longitude);
3149
					} else {
3150
						if (latitude < 0.0) {
3151
							latitude = latitude * -1;
3152
							document.editplaces.NEW_PLACE_LATI.value = latitude;
3153
						}
3154
						if (longitude < 0.0) {
3155
							longitude = longitude * -1;
3156
							document.editplaces.NEW_PLACE_LONG.value = longitude;
3157
						}
3158
						if (document.editplaces.LATI_CONTROL.value == 'S') {
3159
							latitude = latitude * -1;
3160
						}
3161
						if (document.editplaces.LONG_CONTROL.value == 'W') {
3162
							longitude = longitude * -1;
3163
						}
3164
						point = new google.maps.LatLng(latitude, longitude);
3165
					}
3166
				}
3167
3168
				map.setCenter(point);
3169
				map.setZoom(zoom);
3170
				marker.setPosition(point);
3171
			}
3172
3173
			// === Create Borders for the UK Countries =========================================================
3174
			function overlays() {
3175
				// Define place LatLng arrays
3176
3177
				<?php
3178
				$coordsAsStr = array();
3179
				switch (strtoupper($place_name)) {
3180
				case 'ENGLAND':
3181
					$coordsAsStr[] = '-4.74361,50.66750|-4.78361,50.59361|-4.91584,50.57722|-5.01750,50.54264|-5.02569,50.47271|-5.04729,50.42750|-5.15208,50.34374|-5.26805,50.27389|-5.43194,50.19326|-5.49584,50.21695|-5.54639,50.20527|-5.71000,50.12916|-5.71681,50.06083|-5.66174,50.03631|-5.58278,50.04777|-5.54166,50.07055|-5.53416,50.11569|-5.47055,50.12499|-5.33361,50.09138|-5.27666,50.05972|-5.25674,50.00514|-5.19306,49.95527|-5.16070,50.00319|-5.06555,50.03750|-5.07090,50.08166|-5.04806,50.17111|-4.95278,50.19333|-4.85750,50.23166|-4.76250,50.31138|-4.67861,50.32583|-4.54334,50.32222|-4.48278,50.32583|-4.42972,50.35139|-4.38000,50.36388|-4.16555,50.37028|-4.11139,50.33027|-4.05708,50.29791|-3.94389,50.31346|-3.87764,50.28139|-3.83653,50.22972|-3.78944,50.21222|-3.70666,50.20972|-3.65195,50.23111|-3.55139,50.43833|-3.49416,50.54639|-3.46181,50.58792|-3.41139,50.61610|-3.24416,50.67444|-3.17347,50.68833|-3.09445,50.69222|-2.97806,50.70638|-2.92750,50.73125|-2.88278,50.73111|-2.82305,50.72027|-2.77139,50.70861|-2.66195,50.67334|-2.56305,50.63222|-2.45861,50.57500|-2.44666,50.62639|-2.39097,50.64166|-2.19722,50.62611|-2.12195,50.60722|-2.05445,50.58569|-1.96437,50.59674|-1.95441,50.66536|-2.06681,50.71430|-1.93416,50.71277|-1.81639,50.72306|-1.68445,50.73888|-1.59278,50.72416|-1.33139,50.79138|-1.11695,50.80694|-1.15889,50.84083|-1.09445,50.84584|-0.92842,50.83966|-0.86584,50.79965|-0.90826,50.77396|-0.78187,50.72722|-0.74611,50.76583|-0.67528,50.78111|-0.57722,50.79527|-0.25500,50.82638|-0.19084,50.82583|-0.13805,50.81833|0.05695,50.78083|0.12334,50.75944|0.22778,50.73944|0.28695,50.76500|0.37195,50.81638|0.43084,50.83111|0.56722,50.84777|0.67889,50.87681|0.71639,50.90500|0.79334,50.93610|0.85666,50.92556|0.97125,50.98111|0.99778,51.01903|1.04555,51.04944|1.10028,51.07361|1.26250,51.10166|1.36889,51.13583|1.41111,51.20111|1.42750,51.33111|1.38556,51.38777|1.19195,51.37861|1.05278,51.36722|0.99916,51.34777|0.90806,51.34069|0.70416,51.37749|0.61972,51.38304|0.55945,51.40596|0.64236,51.44042|0.69750,51.47084|0.59195,51.48777|0.53611,51.48806|0.48916,51.48445|0.45215,51.45562|0.38894,51.44822|0.46500,51.50306|0.65195,51.53680|0.76695,51.52138|0.82084,51.53556|0.87528,51.56110|0.95250,51.60923|0.94695,51.72556|0.90257,51.73465|0.86306,51.71166|0.76140,51.69164|0.70111,51.71847|0.86211,51.77361|0.93236,51.80583|0.98278,51.82527|1.03569,51.77416|1.08834,51.77056|1.13222,51.77694|1.18139,51.78972|1.22361,51.80888|1.26611,51.83916|1.28097,51.88096|1.20834,51.95083|1.16347,52.02361|1.27750,51.98555|1.33125,51.92875|1.39028,51.96999|1.58736,52.08388|1.63000,52.19527|1.68576,52.32630|1.73028,52.41138|1.74945,52.45583|1.74590,52.62021|1.70250,52.71583|1.64528,52.77111|1.50361,52.83749|1.43222,52.87472|1.35250,52.90972|1.28222,52.92750|1.18389,52.93889|0.99472,52.95111|0.94222,52.95083|0.88472,52.96638|0.66722,52.97611|0.54778,52.96618|0.49139,52.93430|0.44431,52.86569|0.42903,52.82403|0.36334,52.78027|0.21778,52.80694|0.16125,52.86250|0.05778,52.88916|0.00211,52.87985|0.03222,52.91722|0.20389,53.02805|0.27666,53.06694|0.33916,53.09236|0.35389,53.18722|0.33958,53.23472|0.23555,53.39944|0.14347,53.47527|0.08528,53.48638|0.02694,53.50972|-0.10084,53.57306|-0.20722,53.63083|-0.26445,53.69083|-0.30166,53.71319|-0.39022,53.70794|-0.51972,53.68527|-0.71653,53.69638|-0.65445,53.72527|-0.60584,53.72972|-0.54916,53.70611|-0.42261,53.71755|-0.35728,53.73056|-0.29389,53.73666|-0.23139,53.72166|-0.10584,53.63166|-0.03472,53.62555|0.04416,53.63916|0.08916,53.62666|0.14945,53.58847|0.12639,53.64527|0.06264,53.70389|-0.12750,53.86388|-0.16916,53.91847|-0.21222,54.00833|-0.20569,54.05153|-0.16111,54.08806|-0.11694,54.13222|-0.20053,54.15171|-0.26250,54.17444|-0.39334,54.27277|-0.42166,54.33222|-0.45750,54.37694|-0.51847,54.44749|-0.56472,54.48000|-0.87584,54.57027|-1.06139,54.61722|-1.16528,54.64972|-1.30445,54.77138|-1.34556,54.87138|-1.41278,54.99944|-1.48292,55.08625|-1.51500,55.14972|-1.56584,55.28722|-1.58097,55.48361|-1.63597,55.58194|-1.69000,55.60556|-1.74695,55.62499|-1.81764,55.63306|-1.97681,55.75416|-2.02166,55.80611|-2.08361,55.78054|-2.22000,55.66499|-2.27916,55.64472|-2.27416,55.57527|-2.21528,55.50583|-2.18278,55.45985|-2.21236,55.42777|-2.46305,55.36111|-2.63055,55.25500|-2.69945,55.17722|-2.96278,55.03889|-3.01500,55.05222|-3.05103,54.97986|-3.13292,54.93139|-3.20861,54.94944|-3.28931,54.93792|-3.39166,54.87639|-3.42916,54.81555|-3.56916,54.64249|-3.61306,54.48861|-3.49305,54.40333|-3.43389,54.34806|-3.41056,54.28014|-3.38055,54.24444|-3.21472,54.09555|-3.15222,54.08194|-2.93097,54.15333|-2.81361,54.22277|-2.81750,54.14277|-2.83361,54.08500|-2.93250,53.95055|-3.05264,53.90764|-3.03708,53.74944|-2.99278,53.73277|-2.89979,53.72499|-2.97729,53.69382|-3.07306,53.59805|-3.10563,53.55993|-3.00678,53.41738|-2.95389,53.36027|-2.85736,53.32083|-2.70493,53.35062|-2.77639,53.29250|-2.89972,53.28916|-2.94250,53.31056|-3.02889,53.38191|-3.07248,53.40936|-3.16695,53.35708|-3.12611,53.32500|-3.08860,53.26001|-3.02000,53.24722|-2.95528,53.21555|-2.91069,53.17014|-2.89389,53.10416|-2.85695,53.03249|-2.77792,52.98514|-2.73109,52.96873|-2.71945,52.91902|-2.79278,52.90207|-2.85069,52.93875|-2.99389,52.95361|-3.08639,52.91611|-3.13014,52.88486|-3.13708,52.79312|-3.06806,52.77027|-3.01111,52.71166|-3.06666,52.63527|-3.11750,52.58666|-3.07089,52.55702|-3.00792,52.56902|-2.98028,52.53083|-3.02736,52.49792|-3.11916,52.49194|-3.19514,52.46722|-3.19611,52.41027|-3.02195,52.34027|-2.95486,52.33117|-2.99750,52.28139|-3.05125,52.23347|-3.07555,52.14804|-3.12222,52.11805|-3.11250,52.06945|-3.08500,52.01930|-3.04528,51.97639|-2.98889,51.92555|-2.91757,51.91569|-2.86639,51.92889|-2.77861,51.88583|-2.65944,51.81806|-2.68334,51.76957|-2.68666,51.71889|-2.66500,51.61500|-2.62916,51.64416|-2.57889,51.67777|-2.46056,51.74666|-2.40389,51.74041|-2.47166,51.72445|-2.55305,51.65722|-2.65334,51.56389|-2.77055,51.48916|-2.85278,51.44472|-2.96000,51.37499|-3.00695,51.30722|-3.01278,51.25632|-3.02834,51.20611|-3.30139,51.18111|-3.39361,51.18138|-3.43729,51.20638|-3.50722,51.22333|-3.57014,51.23027|-3.63222,51.21805|-3.70028,51.23000|-3.79250,51.23916|-3.88389,51.22416|-3.98472,51.21695|-4.11666,51.21222|-4.22805,51.18777|-4.22028,51.11054|-4.23702,51.04659|-4.30361,51.00416|-4.37639,50.99110|-4.42736,51.00958|-4.47445,51.01416|-4.52132,51.01424|-4.54334,50.92694|-4.56139,50.77625|-4.65139,50.71527|-4.74361,50.66750';
3182
					break;
3183
				case 'SCOTLAND':
3184
					$coordsAsStr[] = '-2.02166,55.80611|-2.07972,55.86722|-2.13028,55.88583|-2.26028,55.91861|-2.37528,55.95694|-2.65722,56.05972|-2.82028,56.05694|-2.86618,56.02840|-2.89555,55.98861|-2.93500,55.96944|-3.01805,55.94944|-3.06750,55.94444|-3.25472,55.97166|-3.45472,55.99194|-3.66416,56.00652|-3.73722,56.05555|-3.57139,56.05360|-3.44111,56.01916|-3.39584,56.01083|-3.34403,56.02333|-3.13903,56.11084|-2.97611,56.19472|-2.91666,56.20499|-2.84695,56.18638|-2.78805,56.18749|-2.67937,56.21465|-2.58403,56.28264|-2.67208,56.32277|-2.76861,56.33180|-2.81528,56.37360|-2.81208,56.43958|-2.91653,56.45014|-2.99555,56.41416|-3.19042,56.35958|-3.27805,56.35750|-3.04055,56.45472|-2.95861,56.45611|-2.72084,56.48888|-2.64084,56.52250|-2.53126,56.57611|-2.48861,56.61416|-2.47805,56.71527|-2.39000,56.77166|-2.31986,56.79638|-2.21972,56.86777|-2.19708,56.94388|-2.16695,57.00055|-2.09334,57.07027|-2.05416,57.21861|-1.95889,57.33250|-1.85584,57.39889|-1.77334,57.45805|-1.78139,57.50555|-1.82195,57.57861|-1.86000,57.62138|-1.92972,57.67777|-2.02222,57.69388|-2.07555,57.69944|-2.14028,57.69056|-2.18611,57.66861|-2.39626,57.66638|-2.51000,57.67166|-2.78639,57.70222|-2.89806,57.70694|-2.96750,57.68027|-3.03847,57.66249|-3.12334,57.67166|-3.22334,57.69166|-3.28625,57.72499|-3.33972,57.72333|-3.48805,57.70945|-3.52222,57.66333|-3.59542,57.63666|-3.64063,57.63881|-3.75414,57.62504|-4.03986,57.55569|-4.19666,57.48584|-4.22889,57.51554|-4.17945,57.56249|-4.11139,57.59833|-4.08078,57.66533|-4.19139,57.67139|-4.25945,57.65527|-4.34361,57.60777|-4.41639,57.60166|-4.29666,57.67444|-4.08528,57.72611|-4.01908,57.70226|-3.96861,57.70250|-3.86556,57.76861|-3.81945,57.80458|-3.80681,57.85819|-3.85055,57.82000|-3.92639,57.80749|-4.04322,57.81438|-4.14973,57.82527|-4.29750,57.84638|-4.36250,57.89777|-4.24306,57.87028|-4.10666,57.85195|-4.01500,57.86777|-3.99166,57.90611|-3.99695,57.95056|-3.84500,58.02000|-3.56611,58.13916|-3.51319,58.16374|-3.45916,58.20305|-3.42028,58.24361|-3.33750,58.27694|-3.20555,58.30625|-3.10972,58.38166|-3.05792,58.45083|-3.02264,58.64653|-3.17639,58.64944|-3.35389,58.66055|-3.36931,58.59555|-3.57611,58.62194|-3.66028,58.61972|-3.71166,58.60374|-3.78264,58.56750|-3.84834,58.56000|-4.08056,58.55527|-4.27722,58.53361|-4.43653,58.54902|-4.50666,58.56777|-4.56055,58.57584|-4.59910,58.53027|-4.66805,58.48833|-4.76146,58.44604|-4.70195,58.50999|-4.70166,58.55861|-4.77014,58.60264|-5.00153,58.62416|-5.10945,58.50833|-5.16472,58.32527|-5.12639,58.28750|-5.07166,58.26472|-5.20361,58.25083|-5.39764,58.25055|-5.27389,58.11722|-5.31514,58.06416|-5.38416,58.08361|-5.45285,58.07416|-5.39805,58.03111|-5.26278,57.97111|-5.19334,57.95069|-5.12750,57.86944|-5.21750,57.90084|-5.33861,57.92083|-5.42876,57.90104|-5.45750,57.85889|-5.64445,57.89972|-5.62555,57.85222|-5.58153,57.81945|-5.60674,57.76618|-5.66305,57.78889|-5.71695,57.86944|-5.76695,57.86472|-5.81708,57.81944|-5.81084,57.63958|-5.69555,57.55944|-5.64361,57.55222|-5.53084,57.52833|-5.65305,57.50875|-5.75000,57.54834|-5.81569,57.57923|-5.85042,57.54972|-5.86695,57.46777|-5.81806,57.36250|-5.75111,57.34333|-5.50334,57.40111|-5.45126,57.41805|-5.49250,57.37083|-5.59884,57.33049|-5.57116,57.28411|-5.51266,57.27745|-5.40514,57.23097|-5.44972,57.22138|-5.49472,57.23888|-5.56066,57.25477|-5.64611,57.23499|-5.64751,57.16161|-5.55028,57.11639|-5.48166,57.11222|-5.40305,57.11062|-5.55945,57.09250|-5.65111,57.11611|-5.72472,57.11306|-5.77361,57.04556|-5.63139,56.98499|-5.56916,56.98972|-5.52403,56.99735|-5.57916,56.98000|-5.64611,56.97222|-5.73374,57.00909|-5.82584,57.00346|-5.91958,56.88708|-5.86528,56.87944|-5.74278,56.89374|-5.66292,56.86924|-5.73306,56.83916|-5.78584,56.83955|-5.85590,56.81430|-5.80208,56.79180|-5.84958,56.74444|-5.90500,56.75666|-5.96694,56.78027|-6.14000,56.75777|-6.19208,56.74888|-6.23452,56.71673|-6.19139,56.67972|-5.91916,56.67388|-5.82622,56.69156|-5.73945,56.71166|-5.55240,56.68886|-5.64861,56.68027|-5.69916,56.68278|-5.88261,56.65666|-5.97472,56.65138|-5.99584,56.61138|-5.93056,56.56972|-5.88416,56.55333|-5.79056,56.53805|-5.67695,56.49389|-5.56389,56.54056|-5.36334,56.66195|-5.23416,56.74333|-5.13236,56.79403|-5.31473,56.65666|-5.37405,56.55925|-5.31826,56.55633|-5.25080,56.55753|-5.37718,56.52112|-5.39866,56.47866|-5.19111,56.46194|-5.11556,56.51277|-5.07014,56.56069|-5.13555,56.48499|-5.22084,56.43583|-5.32764,56.43574|-5.42439,56.43091|-5.52611,56.37360|-5.57139,56.32833|-5.59653,56.25695|-5.57389,56.16000|-5.52000,56.16485|-5.56334,56.11333|-5.60139,56.07638|-5.64222,56.04305|-5.66039,55.98263|-5.62555,56.02055|-5.58014,56.01319|-5.63361,55.96611|-5.67697,55.88844|-5.64750,55.78139|-5.60986,55.75930|-5.66916,55.66166|-5.70166,55.58861|-5.71805,55.51500|-5.75916,55.41750|-5.79528,55.36027|-5.78166,55.29902|-5.73778,55.29222|-5.56694,55.31666|-5.51528,55.36347|-5.55520,55.41440|-5.48639,55.64306|-5.44597,55.70680|-5.38000,55.75027|-5.41889,55.90666|-5.39924,55.99972|-5.33895,56.03456|-5.30594,56.06922|-5.23889,56.11889|-5.03222,56.23250|-4.92229,56.27111|-4.97416,56.23333|-5.07222,56.18695|-5.20069,56.11861|-5.30906,56.00570|-5.34000,55.90201|-5.29250,55.84750|-5.20805,55.84444|-5.22458,55.90175|-5.17334,55.92916|-5.11000,55.90306|-5.01222,55.86694|-4.96195,55.88000|-4.89824,55.98145|-4.84623,56.08632|-4.86636,56.03178|-4.85461,55.98648|-4.77659,55.97977|-4.62723,55.94555|-4.52305,55.91861|-4.70972,55.93403|-4.75166,55.94611|-4.82406,55.94950|-4.87826,55.93653|-4.91639,55.70083|-4.87584,55.68194|-4.81361,55.64555|-4.68722,55.59750|-4.61361,55.49069|-4.63958,55.44264|-4.68250,55.43388|-4.74847,55.41055|-4.83715,55.31882|-4.84778,55.26944|-4.86542,55.22340|-4.93500,55.17860|-5.01250,55.13347|-5.05361,55.04902|-5.17834,54.98888|-5.18563,54.93622|-5.17000,54.89111|-5.11666,54.83180|-5.00500,54.76333|-4.96229,54.68125|-4.92250,54.64055|-4.85723,54.62958|-4.96076,54.79687|-4.92431,54.83708|-4.85222,54.86861|-4.80125,54.85556|-4.74055,54.82166|-4.68084,54.79972|-4.59861,54.78027|-4.55792,54.73903|-4.49639,54.69888|-4.37584,54.67666|-4.34569,54.70916|-4.35973,54.77111|-4.41111,54.82583|-4.42445,54.88152|-4.38479,54.90555|-4.35056,54.85903|-4.09555,54.76777|-3.95361,54.76749|-3.86972,54.80527|-3.81222,54.84888|-3.69250,54.88110|-3.61584,54.87527|-3.57111,54.99083|-3.44528,54.98638|-3.36056,54.97138|-3.14695,54.96500|-3.05103,54.97986|-3.01500,55.05222|-2.96278,55.03889|-2.69945,55.17722|-2.63055,55.25500|-2.46305,55.36111|-2.21236,55.42777|-2.18278,55.45985|-2.21528,55.50583|-2.27416,55.57527|-2.27916,55.64472|-2.22000,55.66499|-2.08361,55.78054|-2.02166,55.80611';
3185
					break;
3186
				case 'IRELAND':
3187
					$coordsAsStr[] = '-8.17166,54.46388|-8.06555,54.37277|-7.94139,54.29944|-7.87576,54.28499|-7.86834,54.22764|-7.81805,54.19916|-7.69972,54.20250|-7.55945,54.12694|-7.31334,54.11250|-7.14584,54.22527|-7.17555,54.28916|-7.16084,54.33666|-7.05834,54.41000|-6.97445,54.40166|-6.92695,54.37916|-6.87305,54.34208|-6.85111,54.28972|-6.73473,54.18361|-6.65556,54.06527|-6.60584,54.04444|-6.44750,54.05833|-6.33889,54.11555|-6.26697,54.09983|-6.17403,54.07222|-6.10834,54.03638|-6.04389,54.03139|-5.96834,54.06389|-5.88500,54.11639|-5.87347,54.20916|-5.82500,54.23958|-5.74611,54.24806|-5.65556,54.22701|-5.60834,54.24972|-5.55916,54.29084|-5.57334,54.37704|-5.64502,54.49267|-5.70472,54.53361|-5.68055,54.57306|-5.59972,54.54194|-5.55097,54.50083|-5.54216,54.44903|-5.54643,54.40527|-5.50672,54.36444|-5.46111,54.38555|-5.43132,54.48596|-5.47945,54.53638|-5.53521,54.65090|-5.57431,54.67722|-5.62916,54.67945|-5.73674,54.67383|-5.80305,54.66138|-5.88257,54.60652|-5.92445,54.63180|-5.86681,54.68972|-5.81903,54.70972|-5.74672,54.72452|-5.68775,54.76335|-5.70931,54.83166|-5.74694,54.85361|-5.79139,54.85139|-6.03611,55.05778|-6.04250,55.10277|-6.03444,55.15458|-6.10125,55.20945|-6.14584,55.22069|-6.25500,55.21194|-6.37639,55.23916|-6.51556,55.23305|-6.61334,55.20722|-6.73028,55.18027|-6.82472,55.16806|-6.88972,55.16777|-6.96695,55.15611|-6.99416,55.11027|-7.05139,55.04680|-7.09500,55.03694|-7.25251,55.07059|-7.32639,55.04527|-7.40639,54.95333|-7.45805,54.85777|-7.55334,54.76277|-7.73916,54.71054|-7.82576,54.73416|-7.92639,54.70054|-7.85236,54.63388|-7.77750,54.62694|-7.83361,54.55389|-7.95084,54.53222|-8.04695,54.50722|-8.17166,54.46388';
3188
					break;
3189
				case 'WALES':
3190
					$coordsAsStr[] = '-3.08860,53.26001|-3.33639,53.34722|-3.38806,53.34361|-3.60986,53.27944|-3.73014,53.28944|-3.85445,53.28444|-4.01861,53.23750|-4.06639,53.22639|-4.15334,53.22556|-4.19639,53.20611|-4.33028,53.11222|-4.36097,53.02888|-4.55278,52.92889|-4.61889,52.90916|-4.72195,52.83611|-4.72778,52.78139|-4.53945,52.79306|-4.47722,52.85500|-4.41416,52.88472|-4.31292,52.90499|-4.23334,52.91499|-4.13569,52.87888|-4.13056,52.77777|-4.05334,52.71666|-4.10639,52.65084|-4.12597,52.60375|-4.08056,52.55333|-4.05972,52.48584|-4.09666,52.38583|-4.14305,52.32027|-4.19361,52.27638|-4.23166,52.24888|-4.52722,52.13083|-4.66945,52.13027|-4.73695,52.10361|-4.76778,52.06444|-4.84445,52.01388|-5.09945,51.96056|-5.23916,51.91638|-5.25889,51.87056|-5.18500,51.86958|-5.11528,51.83333|-5.10257,51.77895|-5.16111,51.76222|-5.24694,51.73027|-5.19111,51.70888|-5.00739,51.70349|-4.90875,51.71249|-4.86111,51.71334|-4.97061,51.67577|-5.02128,51.66861|-5.05139,51.62028|-5.00528,51.60638|-4.94139,51.59416|-4.89028,51.62694|-4.83569,51.64534|-4.79063,51.63340|-4.69028,51.66666|-4.64584,51.72666|-4.57445,51.73416|-4.43611,51.73722|-4.26222,51.67694|-4.19750,51.67916|-4.06614,51.66804|-4.11639,51.63416|-4.17750,51.62235|-4.25055,51.62861|-4.29208,51.60743|-4.27778,51.55666|-4.20486,51.53527|-3.94972,51.61278|-3.83792,51.61999|-3.78166,51.56750|-3.75160,51.52931|-3.67194,51.47388|-3.54250,51.39777|-3.40334,51.37972|-3.27097,51.38014|-3.16458,51.40909|-3.15166,51.45305|-3.11875,51.48750|-3.02111,51.52527|-2.95472,51.53972|-2.89278,51.53861|-2.84778,51.54500|-2.71472,51.58083|-2.66500,51.61500|-2.68666,51.71889|-2.68334,51.76957|-2.65944,51.81806|-2.77861,51.88583|-2.86639,51.92889|-2.91757,51.91569|-2.98889,51.92555|-3.04528,51.97639|-3.08500,52.01930|-3.11250,52.06945|-3.12222,52.11805|-3.07555,52.14804|-3.05125,52.23347|-2.99750,52.28139|-2.95486,52.33117|-3.02195,52.34027|-3.19611,52.41027|-3.19514,52.46722|-3.11916,52.49194|-3.02736,52.49792|-2.98028,52.53083|-3.00792,52.56902|-3.07089,52.55702|-3.11750,52.58666|-3.06666,52.63527|-3.01111,52.71166|-3.06806,52.77027|-3.13708,52.79312|-3.13014,52.88486|-3.08639,52.91611|-2.99389,52.95361|-2.85069,52.93875|-2.79278,52.90207|-2.71945,52.91902|-2.73109,52.96873|-2.77792,52.98514|-2.85695,53.03249|-2.89389,53.10416|-2.91069,53.17014|-2.95528,53.21555|-3.02000,53.24722|-3.08860,53.26001';
3191
					break;
3192
				case 'NC':
3193
					$coordsAsStr[] = '-81.65876,36.60938|-81.70390,36.55513|-81.70639,36.50804|-81.74665,36.39777|-81.90723,36.30804|-82.03195,36.12694|-82.08416,36.10146|-82.12826,36.11020|-82.21500,36.15833|-82.36375,36.11347|-82.43472,36.06013|-82.46236,36.01708|-82.56006,35.96263|-82.60042,35.99638|-82.62308,36.06121|-82.73500,36.01833|-82.84612,35.94944|-82.90451,35.88819|-82.93555,35.83846|-83.16000,35.76236|-83.24222,35.71944|-83.49222,35.57111|-83.56847,35.55861|-83.64416,35.56471|-83.73499,35.56638|-83.88222,35.51791|-83.98361,35.44944|-84.03639,35.35444|-84.04964,35.29117|-84.09042,35.25986|-84.15084,35.25388|-84.20521,35.25722|-84.29284,35.22596|-84.32471,34.98701|-83.09778,35.00027|-82.77722,35.09138|-82.59639,35.14972|-82.37999,35.21500|-82.27362,35.20583|-81.41306,35.17416|-81.05915,35.15333|-80.92666,35.10695|-80.78751,34.95610|-80.79334,34.82555|-79.66777,34.80694|-79.11555,34.34527|-78.57222,33.88166|-78.51806,33.87999|-78.43721,33.89804|-78.23735,33.91986|-78.15389,33.91471|-78.06974,33.89500|-78.02597,33.88936|-77.97611,33.94276|-77.95299,33.99243|-77.94499,34.06499|-77.92728,34.11756|-77.92250,33.99194|-77.92264,33.93715|-77.88215,34.06166|-77.86222,34.15083|-77.83501,34.19194|-77.75724,34.28527|-77.68222,34.36555|-77.63667,34.39805|-77.57363,34.43694|-77.45527,34.50403|-77.38173,34.51646|-77.37905,34.56294|-77.38572,34.61260|-77.40944,34.68916|-77.38847,34.73304|-77.33097,34.63992|-77.35024,34.60099|-77.30958,34.55972|-77.09424,34.67742|-76.75994,34.76659|-76.68325,34.79749|-76.66097,34.75781|-76.62611,34.71014|-76.50063,34.73617|-76.48138,34.77638|-76.38305,34.86423|-76.34326,34.88194|-76.27181,34.96263|-76.35125,35.02221|-76.32354,34.97429|-76.45319,34.93524|-76.43395,34.98782|-76.45356,35.06676|-76.52917,35.00444|-76.63382,34.98242|-76.69722,34.94887|-76.75306,34.90526|-76.81636,34.93944|-76.89000,34.95388|-76.93180,34.96957|-76.96501,34.99777|-77.06816,35.14978|-76.97639,35.06806|-76.86722,35.00000|-76.80531,34.98559|-76.72708,35.00152|-76.60402,35.07416|-76.56555,35.11486|-76.57305,35.16013|-76.66489,35.16694|-76.56361,35.23361|-76.48750,35.22582|-76.46889,35.27166|-76.50298,35.30791|-76.83251,35.39222|-77.02305,35.48694|-77.04958,35.52694|-76.91292,35.46166|-76.65250,35.41499|-76.61611,35.45888|-76.63195,35.52249|-76.58820,35.55104|-76.51556,35.53194|-76.56711,35.48494|-76.52251,35.40416|-76.46195,35.37221|-76.13319,35.35986|-76.04111,35.42416|-76.00223,35.46610|-75.97958,35.51666|-75.89362,35.57555|-75.83834,35.56694|-75.78944,35.57138|-75.74076,35.61846|-75.72084,35.69263|-75.72084,35.81451|-75.74917,35.87791|-75.78333,35.91972|-75.85083,35.97527|-75.94333,35.91777|-75.98944,35.88054|-75.98854,35.79110|-75.99388,35.71027|-76.02875,35.65409|-76.10320,35.66041|-76.13563,35.69239|-76.04475,35.68436|-76.04167,35.74916|-76.05305,35.79361|-76.05305,35.87375|-76.02653,35.96222|-76.07751,35.99319|-76.17472,35.99596|-76.27917,35.91915|-76.37986,35.95763|-76.42014,35.97874|-76.55375,35.93971|-76.66222,35.93305|-76.72952,35.93984|-76.73392,36.04760|-76.75384,36.09477|-76.76028,36.14513|-76.74610,36.22818|-76.70458,36.24673|-76.72764,36.16736|-76.71021,36.11752|-76.69117,36.07165|-76.65979,36.03312|-76.49527,36.00958|-76.37138,36.07694|-76.37084,36.14999|-76.21417,36.09471|-76.07591,36.17910|-76.18361,36.26915|-76.19965,36.31739|-76.13986,36.28805|-76.04274,36.21974|-76.00465,36.18110|-75.95287,36.19241|-75.97604,36.31138|-75.93895,36.28381|-75.85271,36.11069|-75.79315,36.07385|-75.79639,36.11804|-75.88333,36.29554|-75.94665,36.37194|-75.98694,36.41166|-76.03473,36.49666|-76.02899,36.55000|-78.44234,36.54986|-78.56594,36.55799|-80.27556,36.55110|-81.15361,36.56499|-81.38722,36.57695|-81.65876,36.60938';
3194
					break;
3195
				default:
3196
				}
3197
				?>
3198
				var coordStr = <?php echo json_encode($coordsAsStr) ?>;
3199
				jQuery.each(coordStr, function(index, value) {
3200
					var coordXY = value.split('|');
3201
					var coords  = [];
3202
					var points  = [];
3203
					jQuery.each(coordXY, function(i, v) {
3204
						coords = v.split(',');
3205
						points.push(new google.maps.LatLng(parseFloat(coords[1]), parseFloat(coords[0])));
3206
					});
3207
					// Construct the polygon
3208
					new google.maps.Polygon({
3209
						paths:         points,
3210
						strokeColor:   "#888888",
3211
						strokeOpacity: 0.8,
3212
						strokeWeight:  1,
3213
						fillColor:     "#ff0000",
3214
						fillOpacity:   0.15
3215
					}).setMap(map);
3216
				});
3217
			}
3218
3219
			function loadMap(zoom, mapType) {
3220
				var mapTyp;
3221
3222
				if (mapType) {
3223
					mapTyp = mapType;
3224
				} else {
3225
					mapTyp = google.maps.MapTypeId.ROADMAP;
3226
				}
3227
				geocoder = new google.maps.Geocoder();
3228
				if (!zoom) {
3229
					zoom = pl_zoom;
3230
				}
3231
				// Define map
3232
				var myOptions = {
3233
					zoom: zoom,
3234
					center: latlng,
3235
					mapTypeId: mapTyp,// ROADMAP, SATELLITE, HYBRID, TERRAIN
3236
					// mapTypeId: google.maps.MapTypeId.ROADMAP, // ROADMAP, SATELLITE, HYBRID, TERRAIN
3237
					mapTypeControlOptions: {
3238
						style: google.maps.MapTypeControlStyle.DROPDOWN_MENU // DEFAULT, DROPDOWN_MENU, HORIZONTAL_BAR
3239
					},
3240
					navigationControlOptions: {
3241
					position: google.maps.ControlPosition.TOP_RIGHT, // BOTTOM, BOTTOM_LEFT, LEFT, TOP, etc
3242
					style: google.maps.NavigationControlStyle.SMALL // ANDROID, DEFAULT, SMALL, ZOOM_PAN
3243
					},
3244
					streetViewControl: false, // Show Pegman or not
3245
					scrollwheel: true
3246
				};
3247
3248
				map = new google.maps.Map(document.querySelector('.gm-map'), myOptions);
3249
3250
				overlays();
3251
3252
				// Close any infowindow when map is clicked
3253
				google.maps.event.addListener(map, 'click', function() {
3254
					infowindow.close();
3255
				});
3256
3257
				// Check for zoom changes
3258
				google.maps.event.addListener(map, 'zoom_changed', function() {
3259
					document.editplaces.NEW_ZOOM_FACTOR.value = map.zoom;
3260
				});
3261
3262
				// Create the Main Location Marker
3263
				<?php
3264
				if ($level < 3 && $place_icon != '') {
3265
					echo 'var image = new google.maps.MarkerImage("', WT_STATIC_URL, WT_MODULES_DIR, 'googlemap/', $place_icon, '",';
0 ignored issues
show
Bug introduced by
The variable $place_icon 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...
3266
						echo 'new google.maps.Size(25, 15),'; // Image size
3267
						echo 'new google.maps.Point(0, 0),'; // Image origin
3268
						echo 'new google.maps.Point(12, 15)'; // Image anchor
3269
					echo ');';
3270
					echo 'var iconShadow = new google.maps.MarkerImage("', WT_STATIC_URL, WT_MODULES_DIR, 'googlemap/images/flag_shadow.png",';
3271
						echo 'new google.maps.Size(35, 45),'; // Shadow size
3272
						echo 'new google.maps.Point(0,0),'; // Shadow origin
3273
						echo 'new google.maps.Point(1, 45)'; // Shadow anchor is base of flagpole
3274
					echo ');';
3275
					echo 'marker = new google.maps.Marker({';
3276
						echo 'icon: image,';
3277
						echo 'shadow: iconShadow,';
3278
						echo 'position: latlng,';
3279
						echo 'map: map,';
3280
						echo 'title: pl_name,';
3281
						echo 'draggable: true,';
3282
						echo 'zIndex:1';
3283
					echo '});';
3284
				} else {
3285
					echo 'marker = new google.maps.Marker({';
3286
						echo 'position: latlng,';
3287
						echo 'map: map,';
3288
						echo 'title: pl_name,';
3289
						echo 'draggable: true,';
3290
						echo 'zIndex: 1';
3291
					echo '});';
3292
				}
3293
				?>
3294
3295
				// Set marker by clicking on map ---
3296
				google.maps.event.addListener(map, 'click', function(event) {
3297
					clearMarks();
3298
					latlng = event.latLng;
3299
					marker = new google.maps.Marker({
3300
						position: latlng,
3301
						map: map,
3302
						title: pl_name,
3303
						draggable: true,
3304
						zIndex: 1
3305
					});
3306
					document.getElementById('NEW_PLACE_LATI').value = marker.getPosition().lat().toFixed(5);
3307
					document.getElementById('NEW_PLACE_LONG').value = marker.getPosition().lng().toFixed(5);
3308
					updateMap('flag_drag');
3309
					var currzoom = parseInt(document.editplaces.NEW_ZOOM_FACTOR.value);
3310
					mapType = map.getMapTypeId();
3311
					loadMap(currzoom, mapType);
3312
				});
3313
3314
				// If the marker is moved, update the location fields
3315
				google.maps.event.addListener(marker, 'drag', function() {
3316
					document.getElementById('NEW_PLACE_LATI').value = marker.getPosition().lat().toFixed(5);
3317
					document.getElementById('NEW_PLACE_LONG').value = marker.getPosition().lng().toFixed(5);
3318
				});
3319
				google.maps.event.addListener(marker, 'dragend', function() {
3320
					updateMap('flag_drag');
3321
				});
3322
			}
3323
3324
			function clearMarks() {
3325
				marker.setMap(null);
3326
			}
3327
3328
			/**
3329
			 * Called when we select one of the search results.
3330
			 *
3331
			 * @param lat
3332
			 * @param lng
3333
			 */
3334
			function setLoc(lat, lng) {
3335
				if (lat < 0.0) {
3336
					document.editplaces.NEW_PLACE_LATI.value = (lat.toFixed(5) * -1);
3337
					document.editplaces.LATI_CONTROL.value = 'S';
3338
				} else {
3339
					document.editplaces.NEW_PLACE_LATI.value = lat.toFixed(5);
3340
					document.editplaces.LATI_CONTROL.value = 'N';
3341
				}
3342
				if (lng < 0.0) {
3343
					document.editplaces.NEW_PLACE_LONG.value = (lng.toFixed(5) * -1);
3344
					document.editplaces.LONG_CONTROL.value = 'W';
3345
				} else {
3346
					document.editplaces.NEW_PLACE_LONG.value = lng.toFixed(5);
3347
					document.editplaces.LONG_CONTROL.value = 'E';
3348
				}
3349
				new google.maps.LatLng (lat.toFixed(5), lng.toFixed(5));
3350
				infowindow.close();
3351
				updateMap();
3352
			}
3353
3354
			function createMarker(i, point, name) {
3355
				<?php
3356
				echo 'var image = new google.maps.MarkerImage("', WT_STATIC_URL, WT_MODULES_DIR, 'googlemap/images/marker_yellow.png",';
3357
					echo 'new google.maps.Size(20, 34),'; // Image size
3358
					echo 'new google.maps.Point(0, 0),'; // Image origin
3359
					echo 'new google.maps.Point(10, 34)'; // Image anchor
3360
				echo ');';
3361
				echo 'var iconShadow = new google.maps.MarkerImage("', WT_STATIC_URL, WT_MODULES_DIR, 'googlemap/images/shadow50.png",';
3362
					echo 'new google.maps.Size(37, 34),'; // Shadow size
3363
					echo 'new google.maps.Point(0, 0),'; // Shadow origin
3364
					echo 'new google.maps.Point(10, 34)'; // Shadow anchor is base of image
3365
				echo ');';
3366
				?>
3367
				var marker = new google.maps.Marker({
3368
					icon: image,
3369
					shadow: iconShadow,
3370
					map: map,
3371
					position: point,
3372
					zIndex: 0
3373
				});
3374
3375
				google.maps.event.addListener(marker, 'click', function() {
3376
					infowindow.close();
3377
					infowindow.setContent(name);
3378
					infowindow.open(map, marker);
3379
				});
3380
3381
				google.maps.event.addListener(map, 'click', function() {
3382
					infowindow.close();
3383
				});
3384
3385
				return marker;
3386
			}
3387
3388
			function change_icon() {
3389
				window.open('module.php?mod=googlemap&mod_action=flags&countrySelected=<?php echo $selected_country ?>', '_blank', indx_window_specs);
0 ignored issues
show
Bug introduced by
The variable $selected_country 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...
3390
				return false;
3391
			}
3392
3393
			function remove_icon() {
3394
				document.editplaces.icon.value = '';
3395
				document.getElementById('flagsDiv').innerHTML = '<a href="#" onclick="change_icon();return false;"><?php echo I18N::translate('Change flag') ?></a>';
3396
			}
3397
3398
			function addAddressToMap(response) {
3399
				var bounds = new google.maps.LatLngBounds();
3400
				if (!response ) {
3401
					alert('<?php echo I18N::translate('No places found') ?>');
3402
				} else {
3403
					if (response.length > 0) {
3404
						for (var i=0; i<response.length; i++) {
3405
							// 5 decimal places is approx 1 metre accuracy.
3406
							var name =
3407
								'<div>' + response[i].address_components[0].short_name +
3408
								'<br><a href="#" onclick="setLoc(' + response[i].geometry.location.lat() + ', ' + response[i].geometry.location.lng() + ');">' +
3409
								'<?php echo I18N::translate('Use this value') ?></a>' +
3410
								'</div>';
3411
							var point = response[i].geometry.location;
3412
							var marker = createMarker(i, point, name);
3413
							bounds.extend(response[i].geometry.location);
3414
						}
3415
3416
						<?php if ($level > 0) { ?>
3417
							map.fitBounds(bounds);
3418
						<?php } ?>
3419
						var zoomlevel = map.getZoom();
3420
3421
						if (zoomlevel < <?php echo $this->getSetting('GM_MIN_ZOOM') ?>) {
0 ignored issues
show
Security Cross-Site Scripting introduced by
$this->getSetting('GM_MIN_ZOOM') can contain request data and is used in output context(s) leading to a potential security vulnerability.

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
3422
							zoomlevel = <?php echo $this->getSetting('GM_MIN_ZOOM') ?>;
0 ignored issues
show
Security Cross-Site Scripting introduced by
$this->getSetting('GM_MIN_ZOOM') can contain request data and is used in output context(s) leading to a potential security vulnerability.

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
3423
						}
3424
						if (zoomlevel > <?php echo $this->getSetting('GM_MAX_ZOOM') ?>) {
0 ignored issues
show
Security Cross-Site Scripting introduced by
$this->getSetting('GM_MAX_ZOOM') can contain request data and is used in output context(s) leading to a potential security vulnerability.

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
3425
							zoomlevel = <?php echo $this->getSetting('GM_MAX_ZOOM') ?>;
0 ignored issues
show
Security Cross-Site Scripting introduced by
$this->getSetting('GM_MAX_ZOOM') can contain request data and is used in output context(s) leading to a potential security vulnerability.

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
3426
						}
3427
						if (document.editplaces.NEW_ZOOM_FACTOR.value < zoomlevel) {
3428
							zoomlevel = document.editplaces.NEW_ZOOM_FACTOR.value;
3429
							if (zoomlevel < <?php echo $this->getSetting('GM_MIN_ZOOM') ?>) {
0 ignored issues
show
Security Cross-Site Scripting introduced by
$this->getSetting('GM_MIN_ZOOM') can contain request data and is used in output context(s) leading to a potential security vulnerability.

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
3430
								zoomlevel = <?php echo $this->getSetting('GM_MIN_ZOOM') ?>;
0 ignored issues
show
Security Cross-Site Scripting introduced by
$this->getSetting('GM_MIN_ZOOM') can contain request data and is used in output context(s) leading to a potential security vulnerability.

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
3431
							}
3432
							if (zoomlevel > <?php echo $this->getSetting('GM_MAX_ZOOM') ?>) {
0 ignored issues
show
Security Cross-Site Scripting introduced by
$this->getSetting('GM_MAX_ZOOM') can contain request data and is used in output context(s) leading to a potential security vulnerability.

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
3433
								zoomlevel = <?php echo $this->getSetting('GM_MAX_ZOOM') ?>;
0 ignored issues
show
Security Cross-Site Scripting introduced by
$this->getSetting('GM_MAX_ZOOM') can contain request data and is used in output context(s) leading to a potential security vulnerability.

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
3434
							}
3435
						}
3436
						map.setCenter(bounds.getCenter());
3437
						map.setZoom(zoomlevel);
3438
					}
3439
				}
3440
			}
3441
3442
			function showLocation_level(address) {
3443
				<?php if ($level > 0) {?>
3444
					address += '<?php echo ', ', addslashes(implode(', ', array_reverse($where_am_i, true))); ?>';
3445
				<?php } ?>
3446
					geocoder.geocode({'address': address}, addAddressToMap);
3447
			}
3448
3449
			function showLocation_all(address) {
3450
				geocoder.geocode({'address': address}, addAddressToMap);
3451
			}
3452
3453
			function paste_char(value) {
3454
				document.editplaces.NEW_PLACE_NAME.value += value;
3455
			}
3456
			window.onload = function() {
3457
				loadMap();
3458
			};
3459
		</script>
3460
3461
		<form method="post" id="editplaces" name="editplaces" action="module.php?mod=googlemap&amp;mod_action=places_edit">
3462
			<input type="hidden" name="action" value="<?php echo $action ?>record">
3463
			<input type="hidden" name="placeid" value="<?php echo $placeid ?>">
3464
			<input type="hidden" name="level" value="<?php echo $level ?>">
3465
			<input type="hidden" name="icon" value="<?php echo $place_icon ?>">
3466
			<input type="hidden" name="parent_id" value="<?php echo $parent_id ?>">
0 ignored issues
show
Bug introduced by
The variable $parent_id 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...
3467
			<input type="hidden" name="place_long" value="<?php echo $place_long ?>">
3468
			<input type="hidden" name="place_lati" value="<?php echo $place_lati ?>">
3469
			<input type="hidden" name="parent_long" value="<?php echo $parent_long ?>">
3470
			<input type="hidden" name="parent_lati" value="<?php echo $parent_lati ?>">
3471
3472
			<table class="facts_table">
3473
			<tr>
3474
				<td class="optionbox" colspan="3">
3475
					<div class="gm-map" style="width: 100%; height: 300px;"></div>
3476
				</td>
3477
			</tr>
3478
			<tr>
3479
				<td class="descriptionbox"><?php echo GedcomTag::getLabel('PLAC') ?></td>
3480
				<td class="optionbox"><input type="text" id="new_pl_name" name="NEW_PLACE_NAME" value="<?php echo Filter::escapeHtml($place_name) ?>" size="25" class="address_input">
3481
					<div id="INDI_PLAC_pop" style="display: inline;">
3482
					<?php echo FunctionsPrint::printSpecialCharacterLink('new_pl_name') ?></div></td><td class="optionbox">
3483
					<label for="new_pl_name"><a href="#" onclick="showLocation_all(document.getElementById('new_pl_name').value); return false"><?php echo I18N::translate('Search globally') ?></a></label>
3484
					|
3485
					<label for="new_pl_name"><a href="#" onclick="showLocation_level(document.getElementById('new_pl_name').value); return false"><?php echo I18N::translate('Search locally') ?></a></label>
3486
				</td>
3487
			</tr>
3488
			<tr>
3489
				<td class="descriptionbox"><?php echo GedcomTag::getLabel('LATI') ?></td>
3490
				<td class="optionbox" colspan="2">
3491
					<input type="text" id="NEW_PLACE_LATI" name="NEW_PLACE_LATI" placeholder="<?php echo /* I18N: Measure of latitude/longitude */ I18N::translate('degrees') ?>" value="<?php echo abs($place_lati) ?>" size="20" onchange="updateMap();">
3492
					<select name="LATI_CONTROL" id="LATI_CONTROL" onchange="updateMap();">
3493
						<option value="N"<?php echo $place_lati >= 0 ? ' selected' : '' ?>><?php echo I18N::translate('north') ?></option>
3494
						<option value="S"<?php echo $place_lati < 0 ? ' selected' : '' ?>><?php echo I18N::translate('south') ?></option>
3495
3496
					</select>
3497
				</td>
3498
			</tr>
3499
			<tr>
3500
				<td class="descriptionbox"><?php echo GedcomTag::getLabel('LONG') ?></td>
3501
				<td class="optionbox" colspan="2">
3502
					<input type="text" id="NEW_PLACE_LONG" name="NEW_PLACE_LONG" placeholder="<?php echo I18N::translate('degrees') ?>" value="<?php echo abs($place_long) ?>" size="20" onchange="updateMap();">
3503
					<select name="LONG_CONTROL" id="LONG_CONTROL" onchange="updateMap();">
3504
						<option value="E"<?php echo $place_long >= 0 ? ' selected' : '' ?>><?php echo I18N::translate('east') ?></option>
3505
						<option value="W"<?php echo $place_long < 0 ? ' selected' : '' ?>><?php echo I18N::translate('west') ?></option>
3506
3507
					</select>
3508
				</td>
3509
			</tr>
3510
			<tr>
3511
				<td class="descriptionbox">
3512
					<?php echo I18N::translate('Zoom level') ?>
3513
				</td>
3514
				<td class="optionbox" colspan="2">
3515
					<input type="text" id="NEW_ZOOM_FACTOR" name="NEW_ZOOM_FACTOR" value="<?php echo $zoomfactor ?>" size="20" onchange="updateMap();">
0 ignored issues
show
Security Cross-Site Scripting introduced by
$zoomfactor can contain request data and is used in html attribute with double-quotes context(s) leading to a potential security vulnerability.

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
3516
					<p class="small text-muted">
3517
						<?php echo I18N::translate('Here the zoom level can be entered. This value will be used as the minimal value when displaying this geographic location on a map.') ?>
3518
					</p>
3519
				</td>
3520
			</tr>
3521
			<tr>
3522
				<td class="descriptionbox">
3523
					<?php echo I18N::translate('Flag') ?>
3524
				</td>
3525
				<td class="optionbox" colspan="2">
3526
					<div id="flagsDiv">
3527
						<?php if ($place_icon) { ?>
3528
						<img alt="<?php echo /* I18N: The emblem of a country or region */ I18N::translate('Flag') ?>" src="<?php echo WT_STATIC_URL, WT_MODULES_DIR, 'googlemap/', $place_icon ?>">
3529
						<a href="#" onclick="change_icon();return false;"><?php echo I18N::translate('Change flag') ?></a>
3530
						<a href="#" onclick="remove_icon();return false;"><?php echo I18N::translate('Remove flag') ?></a>
3531
						<?php } else { ?>
3532
						<a href="#" onclick="change_icon();return false;"><?php echo I18N::translate('Change flag') ?></a>
3533
						<?php } ?>
3534
					</div>
3535
				</td>
3536
			</tr>
3537
			</table>
3538
			<p id="save-cancel">
3539
				<input type="submit" class="save" value="<?php echo I18N::translate('save') ?>">
3540
				<input type="button" class="cancel" value="<?php echo I18N::translate('close') ?>" onclick="window.close();">
3541
			</p>
3542
		</form>
3543
		<br>
3544
		<br>
3545
		<br>
3546
		<?php
3547
	}
3548
3549
	/**
3550
	 * Places administration.
3551
	 */
3552
	private function adminPlaces() {
0 ignored issues
show
Coding Style introduced by
adminPlaces uses the super-global variable $_FILES which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
Coding Style introduced by
adminPlaces uses the super-global variable $_REQUEST which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
3553
		global $WT_TREE;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
3554
3555
		$action       = Filter::get('action');
3556
		$parent       = Filter::get('parent');
3557
		$inactive     = Filter::getBool('inactive');
3558
		$deleteRecord = Filter::get('deleteRecord');
3559
3560
		if (!isset($parent)) {
3561
			$parent = 0;
3562
		}
3563
3564
		$controller = new PageController;
3565
		$controller->restrictAccess(Auth::isAdmin());
3566
3567
		if ($action == 'ExportFile' && Auth::isAdmin()) {
3568
			$tmp      = $this->placeIdToHierarchy($parent);
3569
			$maxLevel = $this->getHighestLevel();
3570
			if ($maxLevel > 8) {
3571
				$maxLevel = 8;
3572
			}
3573
			$tmp[0]         = 'places';
3574
			$outputFileName = preg_replace('/[:;\/\\\(\)\{\}\[\] $]/', '_', implode('-', $tmp)) . '.csv';
3575
			header('Content-Type: application/octet-stream');
3576
			header('Content-Disposition: attachment; filename="' . $outputFileName . '"');
3577
			echo '"', I18N::translate('Level'), '";"', I18N::translate('Country'), '";';
3578
			if ($maxLevel > 0) {
3579
				echo '"', I18N::translate('State'), '";';
3580
			}
3581
			if ($maxLevel > 1) {
3582
				echo '"', I18N::translate('County'), '";';
3583
			}
3584
			if ($maxLevel > 2) {
3585
				echo '"', I18N::translate('City'), '";';
3586
			}
3587
			if ($maxLevel > 3) {
3588
				echo '"', I18N::translate('Place'), '";';
3589
			}
3590
			if ($maxLevel > 4) {
3591
				echo '"', I18N::translate('Place'), '";';
3592
			}
3593
			if ($maxLevel > 5) {
3594
				echo '"', I18N::translate('Place'), '";';
3595
			}
3596
			if ($maxLevel > 6) {
3597
				echo '"', I18N::translate('Place'), '";';
3598
			}
3599
			if ($maxLevel > 7) {
3600
				echo '"', I18N::translate('Place'), '";';
3601
			}
3602
			echo '"', I18N::translate('Longitude'), '";"', I18N::translate('Latitude'), '";';
3603
			echo '"', I18N::translate('Zoom level'), '";"', I18N::translate('Icon'), '";', WT_EOL;
3604
			$this->outputLevel($parent);
3605
3606
			return;
3607
		}
3608
3609
		$controller
3610
			->setPageTitle(I18N::translate('Google Maps™'))
3611
			->pageHeader();
3612
3613
		?>
3614
		<ol class="breadcrumb small">
3615
			<li><a href="admin.php"><?php echo I18N::translate('Control panel') ?></a></li>
3616
			<li><a href="admin_modules.php"><?php echo I18N::translate('Module administration') ?></a></li>
3617
			<li class="active"><?php echo $controller->getPageTitle() ?></li>
3618
		</ol>
3619
3620
		<ul class="nav nav-tabs nav-justified" role="tablist">
3621
			<li role="presentation">
3622
				<a href="?mod=googlemap&amp;mod_action=admin_config" role="tab">
3623
					<?php echo I18N::translate('Google Maps™ preferences') ?>
3624
				</a>
3625
			</li>
3626
			<li role="presentation" class="active">
3627
				<a href="#">
3628
					<?php echo I18N::translate('Geographic data') ?>
3629
				</a>
3630
			</li>
3631
			<li role="presentation">
3632
				<a href="?mod=googlemap&amp;mod_action=admin_placecheck">
3633
					<?php echo I18N::translate('Place check') ?>
3634
				</a>
3635
			</li>
3636
		</ul>
3637
3638
		<h2><?php echo I18N::translate('Geographic data') ?></h2>
3639
		<?php
3640
3641
		if ($action == 'ImportGedcom') {
3642
			$placelist      = array();
3643
			$j              = 0;
3644
			$gedcom_records =
3645
				Database::prepare("SELECT i_gedcom FROM `##individuals` WHERE i_file=? UNION ALL SELECT f_gedcom FROM `##families` WHERE f_file=?")
3646
				->execute(array($WT_TREE->getTreeId(), $WT_TREE->getTreeId()))
3647
				->fetchOneColumn();
3648
			foreach ($gedcom_records as $gedrec) {
3649
				$i        = 1;
3650
				$placerec = Functions::getSubRecord(2, '2 PLAC', $gedrec, $i);
3651
				while (!empty($placerec)) {
3652
					if (preg_match("/2 PLAC (.+)/", $placerec, $match)) {
3653
						$placelist[$j]          = array();
3654
						$placelist[$j]['place'] = trim($match[1]);
3655 View Code Duplication
						if (preg_match("/4 LATI (.*)/", $placerec, $match)) {
3656
							$placelist[$j]['lati'] = trim($match[1]);
3657
							if (($placelist[$j]['lati'][0] != 'N') && ($placelist[$j]['lati'][0] != 'S')) {
3658
								if ($placelist[$j]['lati'] < 0) {
3659
									$placelist[$j]['lati'][0] = 'S';
3660
								} else {
3661
									$placelist[$j]['lati'] = 'N' . $placelist[$j]['lati'];
3662
								}
3663
							}
3664
						} else {
3665
							$placelist[$j]['lati'] = null;
3666
						}
3667 View Code Duplication
						if (preg_match("/4 LONG (.*)/", $placerec, $match)) {
3668
							$placelist[$j]['long'] = trim($match[1]);
3669
							if (($placelist[$j]['long'][0] != 'E') && ($placelist[$j]['long'][0] != 'W')) {
3670
								if ($placelist[$j]['long'] < 0) {
3671
									$placelist[$j]['long'][0] = 'W';
3672
								} else {
3673
									$placelist[$j]['long'] = 'E' . $placelist[$j]['long'];
3674
								}
3675
							}
3676
						} else {
3677
							$placelist[$j]['long'] = null;
3678
						}
3679
						$j = $j + 1;
3680
					}
3681
					$i        = $i + 1;
3682
					$placerec = Functions::getSubRecord(2, '2 PLAC', $gedrec, $i);
3683
				}
3684
			}
3685
			asort($placelist);
3686
3687
			$prevPlace     = '';
3688
			$prevLati      = '';
3689
			$prevLong      = '';
3690
			$placelistUniq = array();
3691
			$j             = 0;
3692
			foreach ($placelist as $k => $place) {
3693
				if ($place['place'] != $prevPlace) {
3694
					$placelistUniq[$j]          = array();
3695
					$placelistUniq[$j]['place'] = $place['place'];
3696
					$placelistUniq[$j]['lati']  = $place['lati'];
3697
					$placelistUniq[$j]['long']  = $place['long'];
3698
					$j                          = $j + 1;
3699
				} elseif (($place['place'] == $prevPlace) && (($place['lati'] != $prevLati) || ($place['long'] != $prevLong))) {
3700
					if (($placelistUniq[$j - 1]['lati'] == 0) || ($placelistUniq[$j - 1]['long'] == 0)) {
3701
						$placelistUniq[$j - 1]['lati'] = $place['lati'];
3702
						$placelistUniq[$j - 1]['long'] = $place['long'];
3703 View Code Duplication
					} elseif (($place['lati'] != '0') || ($place['long'] != '0')) {
3704
						echo 'Difference: previous value = ', $prevPlace, ', ', $prevLati, ', ', $prevLong, ' current = ', $place['place'], ', ', $place['lati'], ', ', $place['long'], '<br>';
3705
					}
3706
				}
3707
				$prevPlace = $place['place'];
3708
				$prevLati  = $place['lati'];
3709
				$prevLong  = $place['long'];
3710
			}
3711
3712
			$highestIndex = $this->getHighestIndex();
3713
3714
			$default_zoom_level = array(4, 7, 10, 12);
3715
			foreach ($placelistUniq as $k => $place) {
3716
				$parent     = preg_split('/ *, */', $place['place']);
3717
				$parent     = array_reverse($parent);
3718
				$parent_id  = 0;
3719
				$num_parent = count($parent);
3720
				for ($i = 0; $i < $num_parent; $i++) {
3721
					if (!isset($default_zoom_level[$i])) {
3722
						$default_zoom_level[$i] = $default_zoom_level[$i - 1];
3723
					}
3724
					$escparent = $parent[$i];
3725
					if ($escparent == '') {
3726
						$escparent = 'Unknown';
3727
					}
3728
					$row =
3729
						Database::prepare("SELECT pl_id, pl_long, pl_lati, pl_zoom FROM `##placelocation` WHERE pl_level=? AND pl_parent_id=? AND pl_place LIKE ?")
3730
						->execute(array($i, $parent_id, $escparent))
3731
						->fetchOneRow();
3732
					if ($i < $num_parent - 1) {
3733
						// Create higher-level places, if necessary
3734
						if (empty($row)) {
3735
							$highestIndex++;
3736
							Database::prepare("INSERT INTO `##placelocation` (pl_id, pl_parent_id, pl_level, pl_place, pl_zoom) VALUES (?, ?, ?, ?, ?)")
3737
								->execute(array($highestIndex, $parent_id, $i, $escparent, $default_zoom_level[$i]));
3738
							echo Filter::escapeHtml($escparent), '<br>';
3739
							$parent_id = $highestIndex;
3740
						} else {
3741
							$parent_id = $row->pl_id;
3742
						}
3743
					} else {
3744
						// Create lowest-level place, if necessary
3745
						if (empty($row->pl_id)) {
3746
							$highestIndex++;
3747
							Database::prepare("INSERT INTO `##placelocation` (pl_id, pl_parent_id, pl_level, pl_place, pl_long, pl_lati, pl_zoom) VALUES (?, ?, ?, ?, ?, ?, ?)")
3748
								->execute(array($highestIndex, $parent_id, $i, $escparent, $place['long'], $place['lati'], $default_zoom_level[$i]));
3749
							echo Filter::escapeHtml($escparent), '<br>';
3750
						} else {
3751
							if (empty($row->pl_long) && empty($row->pl_lati) && $place['lati'] != '0' && $place['long'] != '0') {
3752
								Database::prepare("UPDATE `##placelocation` SET pl_lati=?, pl_long=? WHERE pl_id=?")
3753
									->execute(array($place['lati'], $place['long'], $row->pl_id));
3754
								echo Filter::escapeHtml($escparent), '<br>';
3755
							}
3756
						}
3757
					}
3758
				}
3759
			}
3760
			$parent = 0;
3761
		}
3762
3763
		if ($action === 'ImportFile') {
3764
			$placefiles = $this->findFiles(WT_MODULES_DIR . 'googlemap/extra');
3765
			sort($placefiles);
3766
		?>
3767
		<form class="form-horizontal" method="post" enctype="multipart/form-data" id="importfile" name="importfile" action="module.php?mod=googlemap&amp;mod_action=admin_places&amp;action=ImportFile2">
3768
3769
			<!-- PLACES FILE -->
3770
			<div class="form-group">
3771
				<label class="control-label col-sm-4" for="placesfile">
3772
					<?php echo I18N::translate('File containing places (CSV)') ?>
3773
				</label>
3774
				<div class="col-sm-8">
3775
					<div class="btn btn-default">
3776
					<input id="placesfile" type="file" name="placesfile">
3777
					</div>
3778
				</div>
3779
			</div>
3780
3781
			<!-- LOCAL FILE -->
3782
			<div class="form-group">
3783
				<label class="control-label col-sm-4" for="localfile">
3784
					<?php echo I18N::translate('Server file containing places (CSV)') ?>
3785
				</label>
3786
				<div class="col-sm-8">
3787
					<div class="input-group">
3788
						<span class="input-group-addon">
3789
							<?php echo WT_MODULES_DIR . 'googlemap/extra/' ?>
3790
						</span>
3791
						<?php
3792
						foreach ($placefiles as $p => $placefile) {
3793
							unset($placefiles[$p]);
3794
							$p = Filter::escapeHtml($placefile);
3795
							if (substr($placefile, 0, 1) == "/") {
3796
								$placefiles[$p] = substr($placefile, 1);
3797
							} else {
3798
								$placefiles[$p] = $placefile;
3799
							}
3800
						}
3801
						echo FunctionsEdit::selectEditControl('localfile', $placefiles, '', '', 'class="form-control"');
3802
						?>
3803
					</div>
3804
				</div>
3805
			</div>
3806
3807
			<!-- CLEAR DATABASE -->
3808
			<fieldset class="form-group">
3809
				<legend class="control-label col-sm-4">
3810
					<?php echo I18N::translate('Delete all existing geographic data before importing the file.') ?>
3811
				</legend>
3812
				<div class="col-sm-8">
3813
					<?php echo FunctionsEdit::editFieldYesNo('cleardatabase', 0, 'class="radio-inline"') ?>
0 ignored issues
show
Documentation introduced by
0 is of type integer, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
3814
				</div>
3815
			</fieldset>
3816
3817
			<!-- UPDATE ONLY -->
3818
			<fieldset class="form-group">
3819
				<legend class="control-label col-sm-4">
3820
					<?php echo I18N::translate('Do not create new locations, just import coordinates for existing locations.') ?>
3821
				</legend>
3822
				<div class="col-sm-8">
3823
					<?php echo FunctionsEdit::editFieldYesNo('updateonly', 0, 'class="radio-inline"') ?>
0 ignored issues
show
Documentation introduced by
0 is of type integer, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
3824
				</div>
3825
			</fieldset>
3826
3827
			<!-- OVERWRITE DATA -->
3828
			<fieldset class="form-group">
3829
				<legend class="control-label col-sm-4">
3830
					<?php echo I18N::translate('Overwrite existing coordinates.') ?>
3831
				</legend>
3832
				<div class="col-sm-8">
3833
					<?php echo FunctionsEdit::editFieldYesNo('overwritedata', 0, 'class="radio-inline"') ?>
0 ignored issues
show
Documentation introduced by
0 is of type integer, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
3834
				</div>
3835
			</fieldset>
3836
3837
			<!-- SAVE BUTTON -->
3838
			<div class="form-group">
3839
				<div class="col-sm-offset-4 col-sm-8">
3840
					<button type="submit" class="btn btn-primary">
3841
						<i class="fa fa-check"></i>
3842
						<?php echo I18N::translate('continue') ?>
3843
					</button>
3844
				</div>
3845
			</div>
3846
		</form>
3847
		<?php
3848
			return;
3849
		}
3850
3851
		if ($action === 'ImportFile2') {
3852
			$country_names = array();
3853
			$stats         = new Stats($WT_TREE);
3854
			foreach ($stats->iso3166() as $key => $value) {
3855
				$country_names[$key] = I18N::translate($key);
3856
			}
3857
			if (Filter::postBool('cleardatabase')) {
3858
				Database::exec("DELETE FROM `##placelocation` WHERE 1=1");
3859
			}
3860
			if (!empty($_FILES['placesfile']['tmp_name'])) {
3861
				$lines = file($_FILES['placesfile']['tmp_name']);
3862
			} elseif (!empty($_REQUEST['localfile'])) {
3863
				$lines = file(WT_MODULES_DIR . 'googlemap/extra' . $_REQUEST['localfile']);
0 ignored issues
show
Security File Exposure introduced by
WT_MODULES_DIR . 'google... $_REQUEST['localfile'] can contain request data and is used in file inclusion context(s) leading to a potential security vulnerability.

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
3864
			}
3865
			// Strip BYTE-ORDER-MARK, if present
3866
			if (!empty($lines[0]) && substr($lines[0], 0, 3) === WT_UTF8_BOM) {
3867
				$lines[0] = substr($lines[0], 3);
3868
			}
3869
			asort($lines);
3870
			$highestIndex = $this->getHighestIndex();
3871
			$placelist    = array();
3872
			$j            = 0;
3873
			$maxLevel     = 0;
3874
			foreach ($lines as $p => $placerec) {
3875
				$fieldrec = explode(';', $placerec);
3876
				if ($fieldrec[0] > $maxLevel) {
3877
					$maxLevel = $fieldrec[0];
3878
				}
3879
			}
3880
			$fields   = count($fieldrec);
0 ignored issues
show
Bug introduced by
The variable $fieldrec 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...
3881
			$set_icon = true;
3882
			if (!is_dir(WT_MODULES_DIR . 'googlemap/places/flags/')) {
3883
				$set_icon = false;
3884
			}
3885
			foreach ($lines as $p => $placerec) {
3886
				$fieldrec = explode(';', $placerec);
3887
				if (is_numeric($fieldrec[0]) && $fieldrec[0] <= $maxLevel) {
3888
					$placelist[$j]          = array();
3889
					$placelist[$j]['place'] = '';
3890
					for ($ii = $fields - 4; $ii > 1; $ii--) {
3891
						if ($fieldrec[0] > $ii - 2) {
3892
							$placelist[$j]['place'] .= $fieldrec[$ii] . ',';
3893
						}
3894
					}
3895
					foreach ($country_names as $countrycode => $countryname) {
3896
						if ($countrycode == strtoupper($fieldrec[1])) {
3897
							$fieldrec[1] = $countryname;
3898
							break;
3899
						}
3900
					}
3901
					$placelist[$j]['place'] .= $fieldrec[1];
3902
					$placelist[$j]['long'] = $fieldrec[$fields - 4];
3903
					$placelist[$j]['lati'] = $fieldrec[$fields - 3];
3904
					$placelist[$j]['zoom'] = $fieldrec[$fields - 2];
3905
					if ($set_icon) {
3906
						$placelist[$j]['icon'] = trim($fieldrec[$fields - 1]);
3907
					} else {
3908
						$placelist[$j]['icon'] = '';
3909
					}
3910
					$j = $j + 1;
3911
				}
3912
			}
3913
3914
			$prevPlace     = '';
3915
			$prevLati      = '';
3916
			$prevLong      = '';
3917
			$placelistUniq = array();
3918
			$j             = 0;
3919
			foreach ($placelist as $k => $place) {
3920
				if ($place['place'] != $prevPlace) {
3921
					$placelistUniq[$j]          = array();
3922
					$placelistUniq[$j]['place'] = $place['place'];
3923
					$placelistUniq[$j]['lati']  = $place['lati'];
3924
					$placelistUniq[$j]['long']  = $place['long'];
3925
					$placelistUniq[$j]['zoom']  = $place['zoom'];
3926
					$placelistUniq[$j]['icon']  = $place['icon'];
3927
					$j                          = $j + 1;
3928
				} elseif (($place['place'] == $prevPlace) && (($place['lati'] != $prevLati) || ($place['long'] != $prevLong))) {
3929
					if (($placelistUniq[$j - 1]['lati'] == 0) || ($placelistUniq[$j - 1]['long'] == 0)) {
3930
						$placelistUniq[$j - 1]['lati'] = $place['lati'];
3931
						$placelistUniq[$j - 1]['long'] = $place['long'];
3932
						$placelistUniq[$j - 1]['zoom'] = $place['zoom'];
3933
						$placelistUniq[$j - 1]['icon'] = $place['icon'];
3934 View Code Duplication
					} elseif (($place['lati'] != '0') || ($place['long'] != '0')) {
3935
						echo 'Difference: previous value = ', $prevPlace, ', ', $prevLati, ', ', $prevLong, ' current = ', $place['place'], ', ', $place['lati'], ', ', $place['long'], '<br>';
0 ignored issues
show
Security Cross-Site Scripting introduced by
$prevPlace can contain request data and is used in output context(s) leading to a potential security vulnerability.

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
Security Cross-Site Scripting introduced by
$prevLati can contain request data and is used in output context(s) leading to a potential security vulnerability.

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
Security Cross-Site Scripting introduced by
$prevLong can contain request data and is used in output context(s) leading to a potential security vulnerability.

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
Security Cross-Site Scripting introduced by
$place['place'] can contain request data and is used in output context(s) leading to a potential security vulnerability.

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
Security Cross-Site Scripting introduced by
$place['lati'] can contain request data and is used in output context(s) leading to a potential security vulnerability.

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
Security Cross-Site Scripting introduced by
$place['long'] can contain request data and is used in output context(s) leading to a potential security vulnerability.

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
3936
					}
3937
				}
3938
				$prevPlace = $place['place'];
3939
				$prevLati  = $place['lati'];
3940
				$prevLong  = $place['long'];
3941
			}
3942
3943
			$default_zoom_level    = array();
3944
			$default_zoom_level[0] = 4;
3945
			$default_zoom_level[1] = 7;
3946
			$default_zoom_level[2] = 10;
3947
			$default_zoom_level[3] = 12;
3948
			foreach ($placelistUniq as $k => $place) {
3949
				$parent     = explode(',', $place['place']);
3950
				$parent     = array_reverse($parent);
3951
				$parent_id  = 0;
3952
				$num_parent = count($parent);
3953
				for ($i = 0; $i < $num_parent; $i++) {
3954
					$escparent = $parent[$i];
3955
					if ($escparent == '') {
3956
						$escparent = 'Unknown';
3957
					}
3958
					$row =
3959
						Database::prepare("SELECT pl_id, pl_long, pl_lati, pl_zoom, pl_icon FROM `##placelocation` WHERE pl_level=? AND pl_parent_id=? AND pl_place LIKE ? ORDER BY pl_place")
3960
						->execute(array($i, $parent_id, $escparent))
3961
						->fetchOneRow();
3962
					if (empty($row)) {
3963
						// this name does not yet exist: create entry
3964
						if (!Filter::postBool('updateonly')) {
3965
							$highestIndex = $highestIndex + 1;
3966
							if (($i + 1) == $num_parent) {
3967
								$zoomlevel = $place['zoom'];
3968
							} elseif (isset($default_zoom_level[$i])) {
3969
								$zoomlevel = $default_zoom_level[$i];
3970
							} else {
3971
								$zoomlevel = $this->getSetting('GM_MAX_ZOOM');
3972
							}
3973
							if (($place['lati'] == '0') || ($place['long'] == '0') || (($i + 1) < $num_parent)) {
3974
								Database::prepare("INSERT INTO `##placelocation` (pl_id, pl_parent_id, pl_level, pl_place, pl_zoom, pl_icon) VALUES (?, ?, ?, ?, ?, ?)")
3975
									->execute(array($highestIndex, $parent_id, $i, $escparent, $zoomlevel, $place['icon']));
3976
							} else {
3977
								//delete leading zero
3978
								$pl_lati = str_replace(array('N', 'S', ','), array('', '-', '.'), $place['lati']);
3979
								$pl_long = str_replace(array('E', 'W', ','), array('', '-', '.'), $place['long']);
3980
								if ($pl_lati >= 0) {
3981
									$place['lati'] = 'N' . abs($pl_lati);
3982
								} elseif ($pl_lati < 0) {
3983
									$place['lati'] = 'S' . abs($pl_lati);
3984
								}
3985
								if ($pl_long >= 0) {
3986
									$place['long'] = 'E' . abs($pl_long);
3987
								} elseif ($pl_long < 0) {
3988
									$place['long'] = 'W' . abs($pl_long);
3989
								}
3990
								Database::prepare("INSERT INTO `##placelocation` (pl_id, pl_parent_id, pl_level, pl_place, pl_long, pl_lati, pl_zoom, pl_icon) VALUES (?, ?, ?, ?, ?, ?, ?, ?)")
3991
									->execute(array($highestIndex, $parent_id, $i, $escparent, $place['long'], $place['lati'], $zoomlevel, $place['icon']));
3992
							}
3993
							$parent_id = $highestIndex;
3994
						}
3995
					} else {
3996
						$parent_id = $row->pl_id;
3997
						if (Filter::postBool('overwritedata') && ($i + 1 == count($parent))) {
3998
							Database::prepare("UPDATE `##placelocation` SET pl_lati = ?, pl_long = ?, pl_zoom = ?, pl_icon = ? WHERE pl_id = ?")
3999
								->execute(array($place['lati'], $place['long'], $place['zoom'], $place['icon'], $parent_id));
4000
						} else {
4001
							// Update only if existing data is missing
4002
							if (!$row->pl_long && !$row->pl_lati) {
4003
								Database::prepare("UPDATE `##placelocation` SET pl_lati = ?, pl_long = ? WHERE pl_id = ?")
4004
									->execute(array($place['lati'], $place['long'], $parent_id));
4005
							}
4006
							if (!$row->pl_icon && $place['icon']) {
4007
								Database::prepare("UPDATE `##placelocation` SET pl_icon = ? WHERE pl_id = ?")
4008
									->execute(array($place['icon'], $parent_id));
4009
							}
4010
						}
4011
					}
4012
				}
4013
			}
4014
			$parent = 0;
4015
		}
4016
4017
		if ($action == 'DeleteRecord') {
4018
			$exists =
4019
				Database::prepare("SELECT 1 FROM `##placelocation` WHERE pl_parent_id=?")
4020
				->execute(array($deleteRecord))
4021
				->fetchOne();
4022
4023
			if (!$exists) {
4024
				Database::prepare("DELETE FROM `##placelocation` WHERE pl_id=?")
4025
					->execute(array($deleteRecord));
4026
			} else {
4027
				echo '<table class="facts_table"><tr><td>', I18N::translate('Location not removed: this location contains sub-locations'), '</td></tr></table>';
4028
			}
4029
		}
4030
4031
		?>
4032
		<script>
4033
		function updateList(inactive) {
4034
			window.location.href='<?php if (strstr(Filter::server('REQUEST_URI'), '&inactive', true)) { $uri = strstr(Filter::server('REQUEST_URI'), '&inactive', true); } else { $uri = Filter::server('REQUEST_URI'); } echo $uri, '&inactive=' ?>'+inactive;
4035
		}
4036
4037
		function edit_place_location(placeid) {
4038
			window.open('module.php?mod=googlemap&mod_action=places_edit&action=update&placeid='+placeid, '_blank', gmap_window_specs);
4039
			return false;
4040
		}
4041
4042
		function add_place_location(placeid) {
4043
			window.open('module.php?mod=googlemap&mod_action=places_edit&action=add&placeid='+placeid, '_blank', gmap_window_specs);
4044
			return false;
4045
		}
4046
4047
		function delete_place(placeid) {
4048
			var answer=confirm('<?php echo I18N::translate('Remove this location?') ?>');
4049
			if (answer == true) {
4050
				window.location = '<?php echo Functions::getQueryUrl(array('action' => 'DeleteRecord')) ?>&action=DeleteRecord&deleteRecord=' + placeid;
4051
			}
4052
		}
4053
		</script>
4054
		<p id="gm_breadcrumb">
4055
			<?php
4056
			$where_am_i = $this->placeIdToHierarchy($parent);
4057
			foreach (array_reverse($where_am_i, true) as $id => $place) {
4058
				if ($id == $parent) {
4059
					if ($place != 'Unknown') {
4060
						echo Filter::escapeHtml($place);
4061
					} else {
4062
						echo I18N::translate('unknown');
4063
					}
4064
				} else {
4065
					echo '<a href="module.php?mod=googlemap&mod_action=admin_places&parent=', $id, '&inactive=', $inactive, '">';
4066
					if ($place != 'Unknown') {
4067
						echo Filter::escapeHtml($place), '</a>';
4068
					} else {
4069
						echo I18N::translate('unknown'), '</a>';
4070
					}
4071
				}
4072
				echo ' - ';
4073
			}
4074
			?>
4075
			<a href="module.php?mod=googlemap&mod_action=admin_places&parent=0&inactive=', $inactive, '"><?php echo I18N::translate('Top level') ?></a>
4076
		</p>
4077
4078
		<form class="form-inline" name="active" method="post" action="module.php?mod=googlemap&mod_action=admin_places&parent=', $parent, '&inactive=', $inactive, '">
4079
			<div class="checkbox">
4080
				<label for="inactive">
4081
				   <?php echo FunctionsEdit::checkbox('inactive', $inactive, 'onclick="updateList(this.checked)"') ?>
4082
				   <?php echo I18N::translate('Show inactive places') ?>
4083
				</label>
4084
			</div>
4085
			<p class="small text-muted">
4086
				<?php echo I18N::translate('By default, the list shows only those places which can be found in your family trees. You may have details for other places, such as those imported in bulk from an external file. Selecting this option will show all places, including ones that are not currently used.') ?>
4087
				<?php echo I18N::translate('If you have a large number of inactive places, it can be slow to generate the list.') ?>
4088
			</p>
4089
		</form>
4090
4091
		<?php
4092
		$placelist = $this->getPlaceListLocation($parent, $inactive);
4093
		echo '<div class="gm_plac_edit">';
4094
		echo '<table class="table table-bordered table-condensed table-hover"><tr>';
4095
		echo '<th>', GedcomTag::getLabel('PLAC'), '</th>';
4096
		echo '<th>', GedcomTag::getLabel('LATI'), '</th>';
4097
		echo '<th>', GedcomTag::getLabel('LONG'), '</th>';
4098
		echo '<th>', I18N::translate('Zoom level'), '</th>';
4099
		echo '<th>', I18N::translate('Icon'), '</th>';
4100
		echo '<th>';
4101
		echo I18N::translate('Edit'), '</th><th>', I18N::translate('Delete'), '</th></tr>';
4102
		if (count($placelist) == 0) {
4103
			echo '<tr><td colspan="7">', I18N::translate('No places found'), '</td></tr>';
4104
		}
4105
		foreach ($placelist as $place) {
4106
			echo '<tr><td><a href="module.php?mod=googlemap&mod_action=admin_places&parent=', $place['place_id'], '&inactive=', $inactive, '">';
4107 View Code Duplication
			if ($place['place'] != 'Unknown') {
4108
				echo Filter::escapeHtml($place['place']), '</a></td>';
4109
			} else {
4110
				echo I18N::translate('unknown'), '</a></td>';
4111
			}
4112
			echo '<td>', $place['lati'], '</td>';
4113
			echo '<td>', $place['long'], '</td>';
4114
			echo '<td>', $place['zoom'], '</td>';
4115
			echo '<td>';
4116
			if ($place['icon']) {
4117
				echo '<img src="', WT_STATIC_URL, WT_MODULES_DIR, 'googlemap/', $place['icon'], '" width="25" height="15">';
4118
			} else {
4119
				if ($place['lati'] || $place['long']) {
4120
					echo '<img src="', WT_STATIC_URL, WT_MODULES_DIR, 'googlemap/images/mm_20_red.png">';
4121
				} else {
4122
					echo '<img src="', WT_STATIC_URL, WT_MODULES_DIR, 'googlemap/images/mm_20_yellow.png">';
4123
				}
4124
			}
4125
			echo '</td>';
4126
			echo '<td class="narrow"><a href="#" onclick="edit_place_location(', $place['place_id'], ');return false;" class="icon-edit" title="', I18N::translate('Edit'), '"></a></td>';
4127
			$noRows =
4128
				Database::prepare("SELECT COUNT(pl_id) FROM `##placelocation` WHERE pl_parent_id=?")
4129
				->execute(array($place['place_id']))
4130
				->fetchOne();
4131
			if ($noRows == 0) { ?>
4132
				<td><a href="#" onclick="delete_place(<?php echo $place['place_id'] ?>);return false;" class="icon-delete" title="<?php echo I18N::translate('Remove') ?>"></a></td>
4133
		<?php } else { ?>
4134
				<td><i class="icon-delete-grey"></i></td>
4135
		<?php } ?>
4136
			</tr>
4137
			<?php
4138
		}
4139
		?>
4140
		</table>
4141
		</div>
4142
4143
		<hr>
4144
		<form class="form-horizontal" action="?" onsubmit="add_place_location(this.parent_id.options[this.parent_id.selectedIndex].value); return false;">
4145
			<div class="form-group">
4146
				<label class="form-control-static col-sm-4" for="parent_id">
4147
					<?php echo I18N::translate('Add a geographic location') ?>
4148
				</label>
4149
				<div class="col-sm-8">
4150
					<div class="col-sm-6">
4151
						<?php echo FunctionsEdit::selectEditControl('parent_id', $where_am_i, I18N::translate('Top level'), $parent, 'class="form-control"') ?>
4152
					</div>
4153
					<button type="submit" class="btn btn-default">
4154
						<i class="fa fa-plus"></i>
4155
						<?php echo /* I18N: A button label. */ I18N::translate('add') ?>
4156
					</button>
4157
				</div>
4158
			</div>
4159
		</form>
4160
4161
		<form class="form-horizontal" action="module.php" method="get">
4162
			<input type="hidden" name="mod" value="googlemap">
4163
			<input type="hidden" name="mod_action" value="admin_places">
4164
			<input type="hidden" name="action" value="ImportGedcom">
4165
			<div class="form-group">
4166
				<label class="form-control-static col-sm-4" for="ged">
4167
					<?php echo I18N::translate('Import all places from a family tree') ?>
4168
				</label>
4169
				<div class="col-sm-8">
4170
					<div class="col-sm-6">
4171
						<?php echo FunctionsEdit::selectEditControl('ged', Tree::getNameList(), null, $WT_TREE->getName(), 'class="form-control"') ?>
4172
					</div>
4173
					<button type="submit" class="btn btn-default">
4174
						<i class="fa fa-upload"></i>
4175
						<?php echo I18N::translate('Import') ?>
4176
					</button>
4177
				</div>
4178
			</div>
4179
		</form>
4180
4181
		<form class="form-horizontal" action="module.php" method="get">
4182
			<input type="hidden" name="mod" value="googlemap">
4183
			<input type="hidden" name="mod_action" value="admin_places">
4184
			<input type="hidden" name="action" value="ImportFile">
4185
			<div class="form-group">
4186
				<label class="form-control-static col-sm-4">
4187
					<?php echo I18N::translate('Upload geographic data') ?>
4188
				</label>
4189
				<div class="col-sm-8">
4190
					<div class="col-sm-6">
4191
						<button type="submit" class="btn btn-default">
4192
							<i class="fa fa-upload"></i>
4193
							<?php echo I18N::translate('Upload') ?>
4194
						</button>
4195
					</div>
4196
				</div>
4197
			</div>
4198
		</form>
4199
4200
		<form class="form-horizontal" action="module.php" method="get">
4201
			<input type="hidden" name="mod" value="googlemap">
4202
			<input type="hidden" name="mod_action" value="admin_places">
4203
			<input type="hidden" name="action" value="ExportFile">
4204
			<div class="form-group">
4205
				<label class="form-control-static col-sm-4">
4206
					<?php echo I18N::translate('Download geographic data') ?>
4207
				</label>
4208
				<div class="col-sm-8">
4209
					<div class="col-sm-6">
4210
						<?php echo FunctionsEdit::selectEditControl('parent', $where_am_i, I18N::translate('All'), $WT_TREE->getTreeId(), 'class="form-control"') ?>
4211
					</div>
4212
					<button type="submit" class="btn btn-default">
4213
						<i class="fa fa-download"></i>
4214
						<?php echo /* I18N: A button label. */ I18N::translate('download') ?>
4215
					</button>
4216
				</div>
4217
			</div>
4218
		</form>
4219
		<?php
4220
	}
4221
4222
	/**
4223
	 * Generate the streetview window.
4224
	 */
4225
	private function wtStreetView() {
4226
		header('Content-type: text/html; charset=UTF-8');
4227
4228
		?>
4229
		<html>
4230
			<head>
4231
				<meta name="viewport" content="initial-scale=1.0, user-scalable=no">
4232
				<link type="text/css" href="<?php echo WT_STATIC_URL, WT_MODULES_DIR; ?>googlemap/css/gm_streetview.css" rel="stylesheet">
4233
				<script src="<?php echo $this->googleMapsScript() ?>"></script>
0 ignored issues
show
Security Cross-Site Scripting introduced by
$this->googleMapsScript() can contain request data and is used in html attribute with double-quotes context(s) leading to a potential security vulnerability.

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
4234
				<script>
4235
4236
		// Following function creates an array of the google map parameters passed ---------------------
4237
		var qsParm = [];
4238
		function qs() {
4239
			var query = window.location.search.substring(1);
4240
			var parms = query.split('&');
4241
			for (var i=0; i<parms.length; i++) {
4242
				var pos = parms[i].indexOf('=');
4243
				if (pos > 0) {
4244
					var key = parms[i].substring(0,pos);
4245
					qsParm[key] = parms[i].substring(pos + 1);
4246
				}
4247
			}
4248
		}
4249
		qsParm['x'] = null;
4250
		qsParm['y'] = null;
4251
		qs();
4252
4253
		var geocoder = new google.maps.Geocoder();
4254
4255
		function geocodePosition(pos) {
4256
			geocoder.geocode({
4257
					latLng: pos
4258
			}, function(responses) {
4259
				if (responses && responses.length > 0) {
4260
					updateMarkerAddress(responses[0].formatted_address);
4261
				} else {
4262
					updateMarkerAddress('Cannot determine address at this location.');
4263
				}
4264
			});
4265
		}
4266
4267
		function updateMarkerStatus(str) {
4268
			document.getElementById('markerStatus').innerHTML = str;
4269
		}
4270
4271
		function updateMarkerPosition(latLng) {
4272
			document.getElementById('info').innerHTML = [
4273
				latLng.lat(),
4274
				latLng.lng()
4275
			].join(', ');
4276
		}
4277
4278
		function updateMarkerAddress(str) {
4279
			document.getElementById('address').innerHTML = str;
4280
		}
4281
4282
		function roundNumber(num, dec) {
4283
			return Math.round(num * Math.pow(10, dec)) / Math.pow(10, dec);
4284
		}
4285
4286
		function initialize() {
4287
			var x = qsParm['x'];
4288
			var y = qsParm['y'];
4289
			var b = parseFloat(qsParm['b']);
4290
			var p = parseFloat(qsParm['p']);
4291
			var m = parseFloat(qsParm['m']);
4292
4293
			var latLng = new google.maps.LatLng(y, x);
4294
4295
			// Create the map and mapOptions
4296
			var mapOptions = {
4297
				zoom: 16,
4298
				center: latLng,
4299
				mapTypeId: google.maps.MapTypeId.ROADMAP,
4300
				mapTypeControlOptions: {
4301
					mapTypeIds: [] // Disable the map type (road/satellite) selector.
4302
				},
4303
				navigationControl: true,
4304
				navigationControlOptions: {
4305
					position: google.maps.ControlPosition.TOP_RIGHT,
4306
					style: google.maps.NavigationControlStyle.SMALL
4307
				},
4308
				streetViewControl: false,
4309
				scrollwheel: true
4310
			};
4311
4312
			var map = new google.maps.Map(document.getElementById('mapCanvas'), mapOptions);
4313
4314
			var bearing = b;
4315
			if (bearing < 0) {
4316
				bearing=bearing+360;
4317
			}
4318
			var pitch = p;
4319
			var svzoom = m;
4320
4321
			var imageNum = Math.round(bearing/22.5) % 16;
4322
4323
			var image = new google.maps.MarkerImage('<?php echo WT_BASE_URL . WT_MODULES_DIR ?>googlemap/images/panda-icons/panda-' + imageNum + '.png',
4324
				// This marker is 50 pixels wide by 50 pixels tall.
4325
				new google.maps.Size(50, 50),
4326
				// The origin for this image is 0,0.
4327
				new google.maps.Point(0, 0),
4328
				// The anchor for this image is the base of the flagpole at 0,32.
4329
				new google.maps.Point(26, 36)
4330
			);
4331
4332
			var shape = {
4333
				coord: [1, 1, 1, 20, 18, 20, 18 , 1],
4334
				type: 'poly'
4335
			};
4336
4337
			var marker = new google.maps.Marker({
4338
				icon: image,
4339
				// shape: shape,
4340
				position: latLng,
4341
				title: 'Drag me to a Blue Street',
4342
				map: map,
4343
				draggable: true
4344
			});
4345
4346
			// ===Next, get the map’s default panorama and set up some defaults. ===========================
4347
4348
			// --- First check if Browser supports html5 ---
4349
			var browserName=navigator.appName;
4350
			if (browserName=='Microsoft Internet Explorer') {
4351
				var render_type = '';
4352
			} else {
4353
				var render_type = 'html5';
4354
			}
4355
4356
			// --- Create the panorama ---
4357
			var panoramaOptions = {
4358
				navigationControl: true,
4359
				navigationControlOptions: {
4360
					position: google.maps.ControlPosition.TOP_RIGHT,  // BOTTOM, BOTTOM_LEFT, LEFT, TOP, etc
4361
					style: google.maps.NavigationControlStyle.SMALL   // ANDROID, DEFAULT, SMALL, ZOOM_PAN
4362
				},
4363
				linksControl: true,
4364
				addressControl: true,
4365
				addressControlOptions: {
4366
					style: {
4367
						// display: 'none'
4368
						// backgroundColor: 'red'
4369
					}
4370
				},
4371
				position: latLng,
4372
				mode: render_type,
4373
				pov: {
4374
					heading: bearing,
4375
					pitch: pitch,
4376
					zoom: svzoom
4377
				}
4378
			};
4379
			panorama = new google.maps.StreetViewPanorama(document.getElementById('mapCanvas'), panoramaOptions);
4380
			panorama.setPosition(latLng);
4381
			setTimeout(function() { panorama.setVisible(true); }, 1000);
4382
			setTimeout(function() { panorama.setVisible(true); }, 2000);
4383
			setTimeout(function() { panorama.setVisible(true); }, 3000);
4384
4385
			// Enable navigator contol and address control to be toggled with right mouse button -------
4386
			var aLink = document.createElement('a');
4387
			aLink.href = 'javascript:void(0)'; onmousedown=function(e) {
4388
				if (parseInt(navigator.appVersion)>3) {
4389
					var clickType=1;
4390
					if (navigator.appName=='Netscape') {
4391
						clickType=e.which;
4392
					} else {
4393
						clickType=event.button;
4394
					}
4395
					if (clickType==1) {
4396
						self.status='Left button!';
4397
					}
4398
					if (clickType!=1) {
4399
						if (panorama.get('addressControl') == false) {
4400
							panorama.set('navigationControl', false);
4401
							panorama.set('addressControl', true);
4402
							panorama.set('linksControl', true);
4403
						} else {
4404
							panorama.set('navigationControl', false);
4405
							panorama.set('addressControl', false);
4406
							panorama.set('linksControl', false);
4407
						}
4408
					}
4409
				}
4410
				return true;
4411
			};
4412
			panorama.controls[google.maps.ControlPosition.TOP_RIGHT].push(aLink);
4413
4414
			// Update current position info.
4415
			updateMarkerPosition(latLng);
4416
			geocodePosition(latLng);
4417
4418
			// Add dragging event listeners.
4419
			google.maps.event.addListener(marker, 'dragstart', function() {
4420
				updateMarkerAddress('Dragging...');
4421
			});
4422
4423
			google.maps.event.addListener(marker, 'drag', function() {
4424
				updateMarkerStatus('Dragging...');
4425
				updateMarkerPosition(marker.getPosition());
4426
				panorama.setPosition(marker.getPosition());
4427
			});
4428
4429
			google.maps.event.addListener(marker, 'dragend', function() {
4430
				updateMarkerStatus('Drag ended');
4431
				geocodePosition(marker.getPosition());
4432
			});
4433
4434
			google.maps.event.addListener(panorama, 'pov_changed', function() {
4435
				var povLevel = panorama.getPov();
4436
				parent.document.getElementById('sv_bearText').value = roundNumber(povLevel.heading, 2);
4437
				parent.document.getElementById('sv_elevText').value = roundNumber(povLevel.pitch, 2);
4438
				parent.document.getElementById('sv_zoomText').value = roundNumber(povLevel.zoom, 2);
4439
			});
4440
4441
			google.maps.event.addListener(panorama, 'position_changed', function() {
4442
				var pos = panorama.getPosition();
4443
				marker.setPosition(pos);
4444
				parent.document.getElementById('sv_latiText').value = pos.lat();
4445
				parent.document.getElementById('sv_longText').value = pos.lng();
4446
			});
4447
4448
			var streetViewLayer = new google.maps.StreetViewCoverageLayer();
4449
			streetViewLayer.setMap(map);		}
4450
4451
		function toggleStreetView() {
4452
			var toggle = panorama.getVisible();
4453
			if (toggle == false) {
4454
				panorama.setVisible(true);
4455
				document.getElementById('butt1').value = "<?php echo I18N::translate('Google Maps™') ?>";
4456
			} else {
4457
				panorama.setVisible(false);
4458
				document.getElementById('butt1').value = "<?php echo I18N::translate('Google Street View™') ?>";
4459
			}
4460
		}
4461
4462
		// Onload handler to fire off the app.
4463
		google.maps.event.addDomListener(window, 'load', initialize);
4464
4465
				</script>
4466
			</head>
4467
			<body>
4468
				<div id="toggle">
4469
					<button id="butt1" type="button" onclick="toggleStreetView();">
4470
						<?php echo I18N::translate('Google Maps™') ?>
4471
					</button>
4472
					<button type="button" onclick="initialize();">
4473
						<?php echo I18N::translate('reset') ?>
4474
					</button>
4475
				</div>
4476
4477
				<div id="mapCanvas" style="height: <?php echo $this->getSetting('GM_PH_YSIZE') ?>;">
0 ignored issues
show
Security Cross-Site Scripting introduced by
$this->getSetting('GM_PH_YSIZE') can contain request data and is used in html attribute with double-quotes context(s) leading to a potential security vulnerability.

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
4478
				</div>
4479
4480
				<div id="infoPanel">
4481
					<div id="markerStatus"><em>Click and drag the marker.</em></div>
4482
					<div id="info" ></div>
4483
					<div id="address"></div>
4484
				</div>
4485
			</body>
4486
		</html>
4487
		<?php
4488
	}
4489
}
4490