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