xref: /webtrees/app/Http/RequestHandlers/ControlPanel.php (revision ab6971f7c60169bd49b369ec3340e17c287a548b)
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