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