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