1<?php 2 3/** 4 * webtrees: online genealogy 5 * Copyright (C) 2019 webtrees development team 6 * This program is free software: you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation, either version 3 of the License, or 9 * (at your option) any later version. 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * You should have received a copy of the GNU General Public License 15 * along with this program. If not, see <http://www.gnu.org/licenses/>. 16 */ 17 18declare(strict_types=1); 19 20namespace Fisharebest\Webtrees\Http\RequestHandlers; 21 22use Fisharebest\Webtrees\Http\ViewResponseTrait; 23use Fisharebest\Webtrees\I18N; 24use Fisharebest\Webtrees\Module\FamilyListModule; 25use Fisharebest\Webtrees\Module\IndividualListModule; 26use Fisharebest\Webtrees\Module\MediaListModule; 27use Fisharebest\Webtrees\Module\ModuleAnalyticsInterface; 28use Fisharebest\Webtrees\Module\ModuleBlockInterface; 29use Fisharebest\Webtrees\Module\ModuleChartInterface; 30use Fisharebest\Webtrees\Module\ModuleDataFixInterface; 31use Fisharebest\Webtrees\Module\ModuleFooterInterface; 32use Fisharebest\Webtrees\Module\ModuleHistoricEventsInterface; 33use Fisharebest\Webtrees\Module\ModuleLanguageInterface; 34use Fisharebest\Webtrees\Module\ModuleListInterface; 35use Fisharebest\Webtrees\Module\ModuleMenuInterface; 36use Fisharebest\Webtrees\Module\ModuleReportInterface; 37use Fisharebest\Webtrees\Module\ModuleSidebarInterface; 38use Fisharebest\Webtrees\Module\ModuleTabInterface; 39use Fisharebest\Webtrees\Module\ModuleThemeInterface; 40use Fisharebest\Webtrees\Module\NoteListModule; 41use Fisharebest\Webtrees\Module\RepositoryListModule; 42use Fisharebest\Webtrees\Module\SourceListModule; 43use Fisharebest\Webtrees\Module\SubmitterListModule; 44use Fisharebest\Webtrees\Note; 45use Fisharebest\Webtrees\Repository; 46use Fisharebest\Webtrees\Services\HousekeepingService; 47use Fisharebest\Webtrees\Services\ModuleService; 48use Fisharebest\Webtrees\Services\ServerCheckService; 49use Fisharebest\Webtrees\Services\TreeService; 50use Fisharebest\Webtrees\Services\UpgradeService; 51use Fisharebest\Webtrees\Services\UserService; 52use Fisharebest\Webtrees\Submitter; 53use Fisharebest\Webtrees\Webtrees; 54use Illuminate\Database\Capsule\Manager as DB; 55use Illuminate\Database\Query\Expression; 56use Illuminate\Database\Query\JoinClause; 57use Illuminate\Support\Collection; 58use League\Flysystem\Adapter\Local; 59use League\Flysystem\Filesystem; 60use Psr\Http\Message\ResponseInterface; 61use Psr\Http\Message\ServerRequestInterface; 62use Psr\Http\Server\RequestHandlerInterface; 63 64/** 65 * The control panel shows a summary of the site and links to admin functions. 66 */ 67class ControlPanel implements RequestHandlerInterface 68{ 69 use ViewResponseTrait; 70 71 /** @var ModuleService */ 72 private $module_service; 73 74 /** @var HousekeepingService */ 75 private $housekeeping_service; 76 77 /** @var ServerCheckService */ 78 private $server_check_service; 79 80 /** @var TreeService */ 81 private $tree_service; 82 83 /** @var UpgradeService */ 84 private $upgrade_service; 85 86 /** @var UserService */ 87 private $user_service; 88 89 /** 90 * ControlPanel constructor. 91 * 92 * @param HousekeepingService $housekeeping_service 93 * @param ModuleService $module_service 94 * @param ServerCheckService $server_check_service 95 * @param TreeService $tree_service 96 * @param UpgradeService $upgrade_service 97 * @param UserService $user_service 98 */ 99 public function __construct( 100 HousekeepingService $housekeeping_service, 101 ModuleService $module_service, 102 ServerCheckService $server_check_service, 103 TreeService $tree_service, 104 UpgradeService $upgrade_service, 105 UserService $user_service 106 ) { 107 $this->module_service = $module_service; 108 $this->housekeeping_service = $housekeeping_service; 109 $this->server_check_service = $server_check_service; 110 $this->tree_service = $tree_service; 111 $this->upgrade_service = $upgrade_service; 112 $this->user_service = $user_service; 113 } 114 115 /** 116 * @param ServerRequestInterface $request 117 * 118 * @return ResponseInterface 119 */ 120 public function handle(ServerRequestInterface $request): ResponseInterface 121 { 122 $this->layout = 'layouts/administration'; 123 124 $filesystem = new Filesystem(new Local(Webtrees::ROOT_DIR)); 125 $files_to_delete = $this->housekeeping_service->deleteOldWebtreesFiles($filesystem); 126 127 return $this->viewResponse('admin/control-panel', [ 128 'title' => I18N::translate('Control panel'), 129 'server_errors' => $this->server_check_service->serverErrors(), 130 'server_warnings' => $this->server_check_service->serverWarnings(), 131 'latest_version' => $this->upgrade_service->latestVersion(), 132 'all_users' => $this->user_service->all(), 133 'administrators' => $this->user_service->administrators(), 134 'managers' => $this->user_service->managers(), 135 'moderators' => $this->user_service->moderators(), 136 'unapproved' => $this->user_service->unapproved(), 137 'unverified' => $this->user_service->unverified(), 138 'all_trees' => $this->tree_service->all(), 139 'changes' => $this->totalChanges(), 140 'individuals' => $this->totalIndividuals(), 141 'families' => $this->totalFamilies(), 142 'sources' => $this->totalSources(), 143 'media' => $this->totalMediaObjects(), 144 'repositories' => $this->totalRepositories(), 145 'notes' => $this->totalNotes(), 146 'submitters' => $this->totalSubmitters(), 147 'individual_list_module' => $this->module_service->findByInterface(IndividualListModule::class)->first(), 148 'family_list_module' => $this->module_service->findByInterface(FamilyListModule::class)->first(), 149 'media_list_module' => $this->module_service->findByInterface(MediaListModule::class)->first(), 150 'note_list_module' => $this->module_service->findByInterface(NoteListModule::class)->first(), 151 'repository_list_module' => $this->module_service->findByInterface(RepositoryListModule::class)->first(), 152 'source_list_module' => $this->module_service->findByInterface(SourceListModule::class)->first(), 153 'submitter_list_module' => $this->module_service->findByInterface(SubmitterListModule::class)->first(), 154 'files_to_delete' => $files_to_delete, 155 'all_modules_disabled' => $this->module_service->all(true), 156 'all_modules_enabled' => $this->module_service->all(), 157 'deleted_modules' => $this->module_service->deletedModules(), 158 'analytics_modules_disabled' => $this->module_service->findByInterface(ModuleAnalyticsInterface::class, true), 159 'analytics_modules_enabled' => $this->module_service->findByInterface(ModuleAnalyticsInterface::class), 160 'block_modules_disabled' => $this->module_service->findByInterface(ModuleBlockInterface::class, true), 161 'block_modules_enabled' => $this->module_service->findByInterface(ModuleBlockInterface::class), 162 'chart_modules_disabled' => $this->module_service->findByInterface(ModuleChartInterface::class, true), 163 'chart_modules_enabled' => $this->module_service->findByInterface(ModuleChartInterface::class), 164 'data_fix_modules_disabled' => $this->module_service->findByInterface(ModuleDataFixInterface::class, true), 165 'data_fix_modules_enabled' => $this->module_service->findByInterface(ModuleDataFixInterface::class), 166 'other_modules' => $this->module_service->otherModules(true), 167 'footer_modules_disabled' => $this->module_service->findByInterface(ModuleFooterInterface::class, true), 168 'footer_modules_enabled' => $this->module_service->findByInterface(ModuleFooterInterface::class), 169 'history_modules_disabled' => $this->module_service->findByInterface(ModuleHistoricEventsInterface::class, true), 170 'history_modules_enabled' => $this->module_service->findByInterface(ModuleHistoricEventsInterface::class), 171 'language_modules_disabled' => $this->module_service->findByInterface(ModuleLanguageInterface::class, true), 172 'language_modules_enabled' => $this->module_service->findByInterface(ModuleLanguageInterface::class), 173 'list_modules_disabled' => $this->module_service->findByInterface(ModuleListInterface::class, true), 174 'list_modules_enabled' => $this->module_service->findByInterface(ModuleListInterface::class), 175 'menu_modules_disabled' => $this->module_service->findByInterface(ModuleMenuInterface::class, true), 176 'menu_modules_enabled' => $this->module_service->findByInterface(ModuleMenuInterface::class), 177 'report_modules_disabled' => $this->module_service->findByInterface(ModuleReportInterface::class, true), 178 'report_modules_enabled' => $this->module_service->findByInterface(ModuleReportInterface::class), 179 'sidebar_modules_disabled' => $this->module_service->findByInterface(ModuleSidebarInterface::class, true), 180 'sidebar_modules_enabled' => $this->module_service->findByInterface(ModuleSidebarInterface::class), 181 'tab_modules_disabled' => $this->module_service->findByInterface(ModuleTabInterface::class, true), 182 'tab_modules_enabled' => $this->module_service->findByInterface(ModuleTabInterface::class), 183 'theme_modules_disabled' => $this->module_service->findByInterface(ModuleThemeInterface::class, true), 184 'theme_modules_enabled' => $this->module_service->findByInterface(ModuleThemeInterface::class), 185 ]); 186 } 187 188 /** 189 * Count the number of pending changes in each tree. 190 * 191 * @return string[] 192 */ 193 private function totalChanges(): array 194 { 195 return DB::table('gedcom') 196 ->leftJoin('change', static function (JoinClause $join): void { 197 $join 198 ->on('change.gedcom_id', '=', 'gedcom.gedcom_id') 199 ->where('change.status', '=', 'pending'); 200 }) 201 ->groupBy(['gedcom.gedcom_id']) 202 ->pluck(new Expression('COUNT(change_id)'), 'gedcom.gedcom_id') 203 ->all(); 204 } 205 206 /** 207 * Count the number of individuals in each tree. 208 * 209 * @return Collection<string,int> 210 */ 211 private function totalIndividuals(): Collection 212 { 213 return DB::table('gedcom') 214 ->leftJoin('individuals', 'i_file', '=', 'gedcom_id') 215 ->groupBy(['gedcom_id']) 216 ->pluck(new Expression('COUNT(i_id)'), 'gedcom_id') 217 ->map(static function (string $count) { 218 return (int) $count; 219 }); 220 } 221 222 /** 223 * Count the number of families in each tree. 224 * 225 * @return Collection<string,int> 226 */ 227 private function totalFamilies(): Collection 228 { 229 return DB::table('gedcom') 230 ->leftJoin('families', 'f_file', '=', 'gedcom_id') 231 ->groupBy(['gedcom_id']) 232 ->pluck(new Expression('COUNT(f_id)'), 'gedcom_id') 233 ->map(static function (string $count) { 234 return (int) $count; 235 }); 236 } 237 238 /** 239 * Count the number of sources in each tree. 240 * 241 * @return Collection<string,int> 242 */ 243 private function totalSources(): Collection 244 { 245 return DB::table('gedcom') 246 ->leftJoin('sources', 's_file', '=', 'gedcom_id') 247 ->groupBy(['gedcom_id']) 248 ->pluck(new Expression('COUNT(s_id)'), 'gedcom_id') 249 ->map(static function (string $count) { 250 return (int) $count; 251 }); 252 } 253 254 /** 255 * Count the number of media objects in each tree. 256 * 257 * @return Collection<string,int> 258 */ 259 private function totalMediaObjects(): Collection 260 { 261 return DB::table('gedcom') 262 ->leftJoin('media', 'm_file', '=', 'gedcom_id') 263 ->groupBy(['gedcom_id']) 264 ->pluck(new Expression('COUNT(m_id)'), 'gedcom_id') 265 ->map(static function (string $count) { 266 return (int) $count; 267 }); 268 } 269 270 /** 271 * Count the number of repositorie in each tree. 272 * 273 * @return Collection<string,int> 274 */ 275 private function totalRepositories(): Collection 276 { 277 return DB::table('gedcom') 278 ->leftJoin('other', static function (JoinClause $join): void { 279 $join 280 ->on('o_file', '=', 'gedcom_id') 281 ->where('o_type', '=', Repository::RECORD_TYPE); 282 }) 283 ->groupBy(['gedcom_id']) 284 ->pluck(new Expression('COUNT(o_id)'), 'gedcom_id') 285 ->map(static function (string $count) { 286 return (int) $count; 287 }); 288 } 289 290 /** 291 * Count the number of notes in each tree. 292 * 293 * @return Collection<string,int> 294 */ 295 private function totalNotes(): Collection 296 { 297 return DB::table('gedcom') 298 ->leftJoin('other', static function (JoinClause $join): void { 299 $join 300 ->on('o_file', '=', 'gedcom_id') 301 ->where('o_type', '=', Note::RECORD_TYPE); 302 }) 303 ->groupBy(['gedcom_id']) 304 ->pluck(new Expression('COUNT(o_id)'), 'gedcom_id') 305 ->map(static function (string $count) { 306 return (int) $count; 307 }); 308 } 309 310 /** 311 * Count the number of submitters in each tree. 312 * 313 * @return Collection<string,int> 314 */ 315 private function totalSubmitters(): Collection 316 { 317 return DB::table('gedcom') 318 ->leftJoin('other', static function (JoinClause $join): void { 319 $join 320 ->on('o_file', '=', 'gedcom_id') 321 ->where('o_type', '=', Submitter::RECORD_TYPE); 322 }) 323 ->groupBy(['gedcom_id']) 324 ->pluck(new Expression('COUNT(o_id)'), 'gedcom_id') 325 ->map(static function (string $count) { 326 return (int) $count; 327 }); 328 } 329} 330