1<?php 2namespace Fisharebest\Webtrees; 3 4/** 5 * webtrees: online genealogy 6 * Copyright (C) 2015 webtrees development team 7 * This program is free software: you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License as published by 9 * the Free Software Foundation, either version 3 of the License, or 10 * (at your option) any later version. 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * You should have received a copy of the GNU General Public License 16 * along with this program. If not, see <http://www.gnu.org/licenses/>. 17 */ 18 19/** 20 * Class Place - Gedcom Place functionality. 21 */ 22class Place { 23 const GEDCOM_SEPARATOR = ', '; 24 25 /** 26 * @var string[] e.g. array('Westminster', 'London', 'England') 27 */ 28 private $gedcom_place; 29 30 /** 31 * @var Tree We may have the same place name in different trees 32 */ 33 private $tree; 34 35 /** 36 * @param string $gedcom_place 37 * @param Tree $tree 38 */ 39 public function __construct($gedcom_place, Tree $tree) { 40 if ($gedcom_place) { 41 $this->gedcom_place = explode(self::GEDCOM_SEPARATOR, $gedcom_place); 42 } else { 43 // Empty => "Top level" 44 $this->gedcom_place = array(); 45 } 46 $this->tree = $tree; 47 } 48 49 /** 50 * @return int 51 */ 52 public function getPlaceId() { 53 $place_id = 0; 54 foreach (array_reverse($this->gedcom_place) as $place) { 55 $place_id = Database::prepare( 56 "SELECT SQL_CACHE p_id FROM `##places` WHERE p_parent_id = :parent_id AND p_place = :place AND p_file = :tree_id" 57 )->execute(array( 58 'parent_id' => $place_id, 59 'place' => $place, 60 'tree_id' => $this->tree->getTreeId(), 61 ))->fetchOne(); 62 } 63 64 return $place_id; 65 } 66 67 /** 68 * @return Place 69 */ 70 public function getParentPlace() { 71 return new self(implode(self::GEDCOM_SEPARATOR, array_slice($this->gedcom_place, 1)), $this->tree); 72 } 73 74 /** 75 * @return Place[] 76 */ 77 public function getChildPlaces() { 78 $children = array(); 79 if ($this->getPlaceId()) { 80 $parent_text = self::GEDCOM_SEPARATOR . $this->getGedcomName(); 81 } else { 82 $parent_text = ''; 83 } 84 85 $rows = Database::prepare( 86 "SELECT SQL_CACHE p_place FROM `##places`" . 87 " WHERE p_parent_id = :parent_id AND p_file = :tree_id" . 88 " ORDER BY p_place COLLATE :collation" 89 )->execute(array( 90 'parent_id' => $this->getPlaceId(), 91 'tree_id' => $this->tree->getTreeId(), 92 'collation' => I18N::collation(), 93 ))->fetchOneColumn(); 94 foreach ($rows as $row) { 95 $children[] = new self($row . $parent_text, $this->tree); 96 } 97 98 return $children; 99 } 100 101 /** 102 * @return string 103 */ 104 public function getURL() { 105 if (Auth::isSearchEngine()) { 106 return '#'; 107 } else { 108 $url = 'placelist.php'; 109 foreach (array_reverse($this->gedcom_place) as $n => $place) { 110 $url .= $n ? '&' : '?'; 111 $url .= 'parent%5B%5D=' . rawurlencode($place); 112 } 113 $url .= '&ged=' . $this->tree->getNameHtml(); 114 115 return $url; 116 } 117 } 118 119 /** 120 * @return string 121 */ 122 public function getGedcomName() { 123 return implode(self::GEDCOM_SEPARATOR, $this->gedcom_place); 124 } 125 126 /** 127 * @return string 128 */ 129 public function getPlaceName() { 130 $place = reset($this->gedcom_place); 131 132 return $place ? '<span dir="auto">' . Filter::escapeHtml($place) . '</span>' : I18N::translate('unknown'); 133 } 134 135 /** 136 * @return bool 137 */ 138 public function isEmpty() { 139 return empty($this->gedcom_place); 140 } 141 142 /** 143 * @return string 144 */ 145 public function getFullName() { 146 if (true) { 147 // If a place hierarchy is a single entity 148 return '<span dir="auto">' . Filter::escapeHtml(implode(I18N::$list_separator, $this->gedcom_place)) . '</span>'; 149 } else { 150 // If a place hierarchy is a list of distinct items 151 $tmp = array(); 152 foreach ($this->gedcom_place as $place) { 153 $tmp[] = '<span dir="auto">' . Filter::escapeHtml($place) . '</span>'; 154 } 155 156 return implode(I18N::$list_separator, $tmp); 157 } 158 } 159 160 /** 161 * For lists and charts, where the full name won’t fit. 162 * 163 * @return string 164 */ 165 public function getShortName() { 166 $SHOW_PEDIGREE_PLACES = $this->tree->getPreference('SHOW_PEDIGREE_PLACES'); 167 168 if ($SHOW_PEDIGREE_PLACES >= count($this->gedcom_place)) { 169 // A short place name - no need to abbreviate 170 return $this->getFullName(); 171 } else { 172 // Abbreviate the place name, for lists 173 if ($this->tree->getPreference('SHOW_PEDIGREE_PLACES_SUFFIX')) { 174 // The *last* $SHOW_PEDIGREE_PLACES components 175 $short_name = implode(self::GEDCOM_SEPARATOR, array_slice($this->gedcom_place, -$SHOW_PEDIGREE_PLACES)); 176 } else { 177 // The *first* $SHOW_PEDIGREE_PLACES components 178 $short_name = implode(self::GEDCOM_SEPARATOR, array_slice($this->gedcom_place, 0, $SHOW_PEDIGREE_PLACES)); 179 } 180 // Add a tool-tip showing the full name 181 return '<span title="' . Filter::escapeHtml($this->getGedcomName()) . '" dir="auto">' . Filter::escapeHtml($short_name) . '</span>'; 182 } 183 } 184 185 /** 186 * For the "view all" option of placelist.php and find.php 187 * 188 * @return string 189 */ 190 public function getReverseName() { 191 $tmp = array(); 192 foreach (array_reverse($this->gedcom_place) as $place) { 193 $tmp[] = '<span dir="auto">' . Filter::escapeHtml($place) . '</span>'; 194 } 195 196 return implode(I18N::$list_separator, $tmp); 197 } 198 199 /** 200 * @param Tree $tree 201 * 202 * @return string[] 203 */ 204 public static function allPlaces(Tree $tree) { 205 $places = array(); 206 $rows = 207 Database::prepare( 208 "SELECT SQL_CACHE CONCAT_WS(', ', p1.p_place, p2.p_place, p3.p_place, p4.p_place, p5.p_place, p6.p_place, p7.p_place, p8.p_place, p9.p_place)" . 209 " FROM `##places` AS p1" . 210 " LEFT JOIN `##places` AS p2 ON (p1.p_parent_id = p2.p_id)" . 211 " LEFT JOIN `##places` AS p3 ON (p2.p_parent_id = p3.p_id)" . 212 " LEFT JOIN `##places` AS p4 ON (p3.p_parent_id = p4.p_id)" . 213 " LEFT JOIN `##places` AS p5 ON (p4.p_parent_id = p5.p_id)" . 214 " LEFT JOIN `##places` AS p6 ON (p5.p_parent_id = p6.p_id)" . 215 " LEFT JOIN `##places` AS p7 ON (p6.p_parent_id = p7.p_id)" . 216 " LEFT JOIN `##places` AS p8 ON (p7.p_parent_id = p8.p_id)" . 217 " LEFT JOIN `##places` AS p9 ON (p8.p_parent_id = p9.p_id)" . 218 " WHERE p1.p_file = :tree_id" . 219 " ORDER BY CONCAT_WS(', ', p9.p_place, p8.p_place, p7.p_place, p6.p_place, p5.p_place, p4.p_place, p3.p_place, p2.p_place, p1.p_place) COLLATE :collate" 220 ) 221 ->execute(array( 222 'tree_id' => $tree->getTreeId(), 223 'collate' => I18N::collation(), 224 ))->fetchOneColumn(); 225 foreach ($rows as $row) { 226 $places[] = new self($row, $tree); 227 } 228 229 return $places; 230 } 231 232 /** 233 * @param string $filter 234 * @param Tree $tree 235 * 236 * @return Place[] 237 */ 238 public static function findPlaces($filter, Tree $tree) { 239 $places = array(); 240 $rows = 241 Database::prepare( 242 "SELECT SQL_CACHE CONCAT_WS(', ', p1.p_place, p2.p_place, p3.p_place, p4.p_place, p5.p_place, p6.p_place, p7.p_place, p8.p_place, p9.p_place)" . 243 " FROM `##places` AS p1" . 244 " LEFT JOIN `##places` AS p2 ON (p1.p_parent_id = p2.p_id)" . 245 " LEFT JOIN `##places` AS p3 ON (p2.p_parent_id = p3.p_id)" . 246 " LEFT JOIN `##places` AS p4 ON (p3.p_parent_id = p4.p_id)" . 247 " LEFT JOIN `##places` AS p5 ON (p4.p_parent_id = p5.p_id)" . 248 " LEFT JOIN `##places` AS p6 ON (p5.p_parent_id = p6.p_id)" . 249 " LEFT JOIN `##places` AS p7 ON (p6.p_parent_id = p7.p_id)" . 250 " LEFT JOIN `##places` AS p8 ON (p7.p_parent_id = p8.p_id)" . 251 " LEFT JOIN `##places` AS p9 ON (p8.p_parent_id = p9.p_id)" . 252 " WHERE CONCAT_WS(', ', p1.p_place, p2.p_place, p3.p_place, p4.p_place, p5.p_place, p6.p_place, p7.p_place, p8.p_place, p9.p_place) LIKE CONCAT('%', :filter_1, '%') AND CONCAT_WS(', ', p1.p_place, p2.p_place, p3.p_place, p4.p_place, p5.p_place, p6.p_place, p7.p_place, p8.p_place, p9.p_place) NOT LIKE CONCAT('%,%', :filter_2, '%') AND p1.p_file = :tree_id" . 253 " ORDER BY CONCAT_WS(', ', p1.p_place, p2.p_place, p3.p_place, p4.p_place, p5.p_place, p6.p_place, p7.p_place, p8.p_place, p9.p_place) COLLATE :collation" 254 )->execute(array( 255 'filter_1' => preg_quote($filter), 256 'filter_2' => preg_quote($filter), 257 'tree_id' => $tree->getTreeId(), 258 'collation' => I18N::collation(), 259 ))->fetchOneColumn(); 260 foreach ($rows as $row) { 261 $places[] = new self($row, $tree); 262 } 263 264 return $places; 265 } 266} 267