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