1<?php 2/** 3 * webtrees: online genealogy 4 * Copyright (C) 2018 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 */ 16namespace Fisharebest\Webtrees\Module; 17 18use Fisharebest\Webtrees\Auth; 19use Fisharebest\Webtrees\Filter; 20use Fisharebest\Webtrees\Functions\FunctionsDb; 21use Fisharebest\Webtrees\Functions\FunctionsPrintLists; 22use Fisharebest\Webtrees\I18N; 23use Fisharebest\Webtrees\Query\QueryName; 24use Fisharebest\Webtrees\Tree; 25 26/** 27 * Class TopSurnamesModule 28 */ 29class TopSurnamesModule extends AbstractModule implements ModuleBlockInterface 30{ 31 // Default values for new blocks. 32 const DEFAULT_NUMBER = 10; 33 const DEFAULT_STYLE = 'table'; 34 35 /** 36 * How should this module be labelled on tabs, menus, etc.? 37 * 38 * @return string 39 */ 40 public function getTitle() 41 { 42 return /* I18N: Name of a module. Top=Most common */ 43 I18N::translate('Top surnames'); 44 } 45 46 /** 47 * A sentence describing what this module does. 48 * 49 * @return string 50 */ 51 public function getDescription() 52 { 53 return /* I18N: Description of the “Top surnames” module */ 54 I18N::translate('A list of the most popular surnames.'); 55 } 56 57 /** 58 * Generate the HTML content of this block. 59 * 60 * @param Tree $tree 61 * @param int $block_id 62 * @param bool $template 63 * @param string[] $cfg 64 * 65 * @return string 66 */ 67 public function getBlock(Tree $tree, int $block_id, bool $template = true, array $cfg = []): string 68 { 69 global $ctype; 70 71 $num = $this->getBlockSetting($block_id, 'num', self::DEFAULT_NUMBER); 72 $infoStyle = $this->getBlockSetting($block_id, 'infoStyle', self::DEFAULT_STYLE); 73 74 extract($cfg, EXTR_OVERWRITE); 75 76 // This next function is a bit out of date, and doesn't cope well with surname variants 77 $top_surnames = FunctionsDb::getTopSurnames($tree->getTreeId(), 0, $num); 78 79 $all_surnames = []; 80 $i = 0; 81 foreach (array_keys($top_surnames) as $top_surname) { 82 $all_surnames = array_merge($all_surnames, QueryName::surnames($tree, $top_surname, '', false, false)); 83 if (++$i == $num) { 84 break; 85 } 86 } 87 if ($i < $num) { 88 $num = $i; 89 } 90 91 switch ($infoStyle) { 92 case 'tagcloud': 93 uksort($all_surnames, '\Fisharebest\Webtrees\I18N::strcasecmp'); 94 $content = FunctionsPrintLists::surnameTagCloud($all_surnames, 'individual-list', true, $tree); 95 break; 96 case 'list': 97 uasort($all_surnames, '\Fisharebest\Webtrees\Module\TopSurnamesModule::surnameCountSort'); 98 $content = FunctionsPrintLists::surnameList($all_surnames, 1, true, 'individual-list', $tree); 99 break; 100 case 'array': 101 uasort($all_surnames, '\Fisharebest\Webtrees\Module\TopSurnamesModule::surnameCountSort'); 102 $content = FunctionsPrintLists::surnameList($all_surnames, 2, true, 'individual-list', $tree); 103 break; 104 case 'table': 105 default: 106 uasort($all_surnames, '\Fisharebest\Webtrees\Module\TopSurnamesModule::surnameCountSort'); 107 $content = view('lists/surnames-table', [ 108 'surnames' => $all_surnames, 109 'route' => 'individual-list', 110 'tree' => $tree, 111 ]); 112 break; 113 } 114 115 if ($template) { 116 if ($num == 1) { 117 // I18N: i.e. most popular surname. 118 $title = I18N::translate('Top surname'); 119 } else { 120 // I18N: Title for a list of the most common surnames, %s is a number. Note that a separate translation exists when %s is 1 121 $title = I18N::plural('Top %s surname', 'Top %s surnames', $num, I18N::number($num)); 122 } 123 124 if ($ctype === 'gedcom' && Auth::isManager($tree)) { 125 $config_url = route('tree-page-block-edit', [ 126 'block_id' => $block_id, 127 'ged' => $tree->getName(), 128 ]); 129 } elseif ($ctype === 'user' && Auth::check()) { 130 $config_url = route('user-page-block-edit', [ 131 'block_id' => $block_id, 132 'ged' => $tree->getName(), 133 ]); 134 } else { 135 $config_url = ''; 136 } 137 138 return view('modules/block-template', [ 139 'block' => str_replace('_', '-', $this->getName()), 140 'id' => $block_id, 141 'config_url' => $config_url, 142 'title' => $title, 143 'content' => $content, 144 ]); 145 } else { 146 return $content; 147 } 148 } 149 150 /** {@inheritdoc} */ 151 public function loadAjax(): bool 152 { 153 return false; 154 } 155 156 /** {@inheritdoc} */ 157 public function isUserBlock(): bool 158 { 159 return true; 160 } 161 162 /** {@inheritdoc} */ 163 public function isGedcomBlock(): bool 164 { 165 return true; 166 } 167 168 /** 169 * An HTML form to edit block settings 170 * 171 * @param Tree $tree 172 * @param int $block_id 173 * 174 * @return void 175 */ 176 public function configureBlock(Tree $tree, int $block_id) 177 { 178 if ($_SERVER['REQUEST_METHOD'] === 'POST') { 179 $this->setBlockSetting($block_id, 'num', Filter::postInteger('num', 1, 10000, 10)); 180 $this->setBlockSetting($block_id, 'infoStyle', Filter::post('infoStyle', 'list|array|table|tagcloud', self::DEFAULT_STYLE)); 181 182 return; 183 } 184 185 $num = $this->getBlockSetting($block_id, 'num', self::DEFAULT_NUMBER); 186 $infoStyle = $this->getBlockSetting($block_id, 'infoStyle', self::DEFAULT_STYLE); 187 188 $info_styles = [ 189 'list' => /* I18N: An option in a list-box */ 190 I18N::translate('bullet list'), 191 'array' => /* I18N: An option in a list-box */ 192 I18N::translate('compact list'), 193 'table' => /* I18N: An option in a list-box */ 194 I18N::translate('table'), 195 'tagcloud' => /* I18N: An option in a list-box */ 196 I18N::translate('tag cloud'), 197 ]; 198 199 echo view('modules/top10_surnames/config', [ 200 'num' => $num, 201 'infoStyle' => $infoStyle, 202 'info_styles' => $info_styles, 203 ]); 204 } 205 206 /** 207 * Sort (lists of counts of similar) surname by total count. 208 * 209 * @param string[][] $a 210 * @param string[][] $b 211 * 212 * @return int 213 */ 214 private static function surnameCountSort($a, $b) 215 { 216 $counta = 0; 217 foreach ($a as $x) { 218 $counta += count($x); 219 } 220 $countb = 0; 221 foreach ($b as $x) { 222 $countb += count($x); 223 } 224 225 return $countb - $counta; 226 } 227} 228