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 private $gedcom_place; // e.g. array("Westminster", "London", "England") 25 private $gedcom_id; // We may have the same place in different trees 26 27 /** 28 * @param string $gedcom_place 29 * @param integer $gedcom_id 30 */ 31 public function __construct($gedcom_place, $gedcom_id) { 32 if ($gedcom_place) { 33 $this->gedcom_place = explode(self::GEDCOM_SEPARATOR, $gedcom_place); 34 } else { 35 // Empty => "Top level" 36 $this->gedcom_place = array(); 37 } 38 $this->gedcom_id = $gedcom_id; 39 } 40 41 /** 42 * @return integer 43 */ 44 public function getPlaceId() { 45 $place_id = 0; 46 foreach (array_reverse($this->gedcom_place) as $place) { 47 $place_id = Database::prepare( 48 "SELECT SQL_CACHE p_id FROM `##places` WHERE p_parent_id = :parent_id AND p_place = :place AND p_file = :tree_id" 49 )->execute(array( 50 'parent_id' => $place_id, 51 'place' => $place, 52 'tree_id' => $this->gedcom_id, 53 ))->fetchOne(); 54 } 55 56 return $place_id; 57 } 58 59 /** 60 * @return Place 61 */ 62 public function getParentPlace() { 63 return new Place(implode(self::GEDCOM_SEPARATOR, array_slice($this->gedcom_place, 1)), $this->gedcom_id); 64 } 65 66 /** 67 * @return Place[] 68 */ 69 public function getChildPlaces() { 70 $children = array(); 71 if ($this->getPlaceId()) { 72 $parent_text = self::GEDCOM_SEPARATOR . $this->getGedcomName(); 73 } else { 74 $parent_text = ''; 75 } 76 77 $rows = Database::prepare( 78 "SELECT SQL_CACHE p_place FROM `##places`" . 79 " WHERE p_parent_id = :parent_id AND p_file = :tree_id" . 80 " ORDER BY p_place COLLATE :collation" 81 )->execute(array( 82 'parent_id' => $this->getPlaceId(), 83 'tree_id' => $this->gedcom_id, 84 'collation' => I18N::$collation, 85 ))->fetchOneColumn(); 86 foreach ($rows as $row) { 87 $children[] = new Place($row . $parent_text, $this->gedcom_id); 88 } 89 90 return $children; 91 } 92 93 /** 94 * @return string 95 */ 96 public function getURL() { 97 $url = 'placelist.php'; 98 foreach (array_reverse($this->gedcom_place) as $n => $place) { 99 $url .= $n ? '&' : '?'; 100 $url .= 'parent%5B%5D=' . rawurlencode($place); 101 } 102 $url .= '&ged=' . rawurlencode(get_gedcom_from_id($this->gedcom_id)); 103 104 return $url; 105 } 106 107 /** 108 * @return string 109 */ 110 public function getGedcomName() { 111 return implode(self::GEDCOM_SEPARATOR, $this->gedcom_place); 112 } 113 114 /** 115 * @return string 116 */ 117 public function getPlaceName() { 118 $place = reset($this->gedcom_place); 119 120 return $place ? '<span dir="auto">' . Filter::escapeHtml($place) . '</span>' : I18N::translate('unknown'); 121 } 122 123 /** 124 * @return bool 125 */ 126 public function isEmpty() { 127 return empty($this->gedcom_place); 128 } 129 130 /** 131 * @return string 132 */ 133 public function getFullName() { 134 if (true) { 135 // If a place hierarchy is a single entity 136 return '<span dir="auto">' . Filter::escapeHtml(implode(I18N::$list_separator, $this->gedcom_place)) . '</span>'; 137 } else { 138 // If a place hierarchy is a list of distinct items 139 $tmp = array(); 140 foreach ($this->gedcom_place as $place) { 141 $tmp[] = '<span dir="auto">' . Filter::escapeHtml($place) . '</span>'; 142 } 143 144 return implode(I18N::$list_separator, $tmp); 145 } 146 } 147 148 /** 149 * For lists and charts, where the full name won’t fit. 150 * 151 * @return string 152 */ 153 public function getShortName() { 154 global $WT_TREE; 155 156 $SHOW_PEDIGREE_PLACES = $WT_TREE->getPreference('SHOW_PEDIGREE_PLACES'); 157 158 if ($SHOW_PEDIGREE_PLACES >= count($this->gedcom_place)) { 159 // A short place name - no need to abbreviate 160 return $this->getFullName(); 161 } else { 162 // Abbreviate the place name, for lists 163 if ($WT_TREE->getPreference('SHOW_PEDIGREE_PLACES_SUFFIX')) { 164 // The *last* $SHOW_PEDIGREE_PLACES components 165 $short_name = implode(self::GEDCOM_SEPARATOR, array_slice($this->gedcom_place, -$SHOW_PEDIGREE_PLACES)); 166 } else { 167 // The *first* $SHOW_PEDIGREE_PLACES components 168 $short_name = implode(self::GEDCOM_SEPARATOR, array_slice($this->gedcom_place, 0, $SHOW_PEDIGREE_PLACES)); 169 } 170 // Add a tool-tip showing the full name 171 return '<span title="' . Filter::escapeHtml($this->getGedcomName()) . '" dir="auto">' . Filter::escapeHtml($short_name) . '</span>'; 172 } 173 } 174 175 /** 176 * For the "view all" option of placelist.php and find.php 177 * 178 * @return string 179 */ 180 public function getReverseName() { 181 $tmp = array(); 182 foreach (array_reverse($this->gedcom_place) as $place) { 183 $tmp[] = '<span dir="auto">' . Filter::escapeHtml($place) . '</span>'; 184 } 185 186 return implode(I18N::$list_separator, $tmp); 187 } 188 189 /** 190 * @param integer $gedcom_id 191 * 192 * @return string[] 193 */ 194 public static function allPlaces($gedcom_id) { 195 $places = array(); 196 $rows = 197 Database::prepare( 198 "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)" . 199 " FROM `##places` AS p1" . 200 " LEFT JOIN `##places` AS p2 ON (p1.p_parent_id=p2.p_id)" . 201 " LEFT JOIN `##places` AS p3 ON (p2.p_parent_id=p3.p_id)" . 202 " LEFT JOIN `##places` AS p4 ON (p3.p_parent_id=p4.p_id)" . 203 " LEFT JOIN `##places` AS p5 ON (p4.p_parent_id=p5.p_id)" . 204 " LEFT JOIN `##places` AS p6 ON (p5.p_parent_id=p6.p_id)" . 205 " LEFT JOIN `##places` AS p7 ON (p6.p_parent_id=p7.p_id)" . 206 " LEFT JOIN `##places` AS p8 ON (p7.p_parent_id=p8.p_id)" . 207 " LEFT JOIN `##places` AS p9 ON (p8.p_parent_id=p9.p_id)" . 208 " WHERE p1.p_file=?" . 209 " 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 '" . I18N::$collation . "'" 210 ) 211 ->execute(array($gedcom_id)) 212 ->fetchOneColumn(); 213 foreach ($rows as $row) { 214 $places[] = new Place($row, $gedcom_id); 215 } 216 return $places; 217 } 218 219 /** 220 * @param string $filter 221 * @param integer $gedcom_id 222 * 223 * @return Place[] 224 */ 225 public static function findPlaces($filter, $gedcom_id) { 226 $places = array(); 227 $rows = 228 Database::prepare( 229 "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)" . 230 " FROM `##places` AS p1" . 231 " LEFT JOIN `##places` AS p2 ON (p1.p_parent_id=p2.p_id)" . 232 " LEFT JOIN `##places` AS p3 ON (p2.p_parent_id=p3.p_id)" . 233 " LEFT JOIN `##places` AS p4 ON (p3.p_parent_id=p4.p_id)" . 234 " LEFT JOIN `##places` AS p5 ON (p4.p_parent_id=p5.p_id)" . 235 " LEFT JOIN `##places` AS p6 ON (p5.p_parent_id=p6.p_id)" . 236 " LEFT JOIN `##places` AS p7 ON (p6.p_parent_id=p7.p_id)" . 237 " LEFT JOIN `##places` AS p8 ON (p7.p_parent_id=p8.p_id)" . 238 " LEFT JOIN `##places` AS p9 ON (p8.p_parent_id=p9.p_id)" . 239 " 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('%', ?, '%') 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('%,%', ?, '%') AND p1.p_file=?" . 240 " 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 '" . I18N::$collation . "'" 241 ) 242 ->execute(array($filter, preg_quote($filter), $gedcom_id)) 243 ->fetchOneColumn(); 244 foreach ($rows as $row) { 245 $places[] = new Place($row, $gedcom_id); 246 } 247 return $places; 248 } 249} 250