Passed
Push — validation ( 1c78f5...5db15e )
by Greg
08:11
created

FamilyTreeStatisticsModule::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 1
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * webtrees: online genealogy
5
 * Copyright (C) 2021 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 <https://www.gnu.org/licenses/>.
16
 */
17
18
declare(strict_types=1);
19
20
namespace Fisharebest\Webtrees\Module;
21
22
use Fisharebest\Webtrees\Auth;
23
use Fisharebest\Webtrees\I18N;
24
use Fisharebest\Webtrees\Individual;
25
use Fisharebest\Webtrees\Services\ModuleService;
26
use Fisharebest\Webtrees\Statistics;
27
use Fisharebest\Webtrees\Tree;
28
use Illuminate\Database\Capsule\Manager as DB;
29
use Illuminate\Database\Query\Expression;
30
use Illuminate\Support\Str;
31
use Psr\Http\Message\ServerRequestInterface;
32
33
use function app;
34
use function extract;
35
use function uksort;
36
use function view;
37
38
use const EXTR_OVERWRITE;
39
40
/**
41
 * Class FamilyTreeStatisticsModule
42
 */
43
class FamilyTreeStatisticsModule extends AbstractModule implements ModuleBlockInterface
44
{
45
    use ModuleBlockTrait;
46
47
    /** Show this number of surnames by default */
48
    private const DEFAULT_NUMBER_OF_SURNAMES = '10';
49
50
    private ModuleService $module_service;
51
52
    /**
53
     * @param ModuleService $module_service
54
     */
55
    public function __construct(ModuleService $module_service)
56
    {
57
        $this->module_service = $module_service;
58
    }
59
60
    /**
61
     * How should this module be identified in the control panel, etc.?
62
     *
63
     * @return string
64
     */
65
    public function title(): string
66
    {
67
        /* I18N: Name of a module */
68
        return I18N::translate('Statistics');
69
    }
70
71
    /**
72
     * A sentence describing what this module does.
73
     *
74
     * @return string
75
     */
76
    public function description(): string
77
    {
78
        /* I18N: Description of “Statistics” module */
79
        return I18N::translate('The size of the family tree, earliest and latest events, common names, etc.');
80
    }
81
82
    /**
83
     * Generate the HTML content of this block.
84
     *
85
     * @param Tree                 $tree
86
     * @param int                  $block_id
87
     * @param string               $context
88
     * @param array<string,string> $config
89
     *
90
     * @return string
91
     */
92
    public function getBlock(Tree $tree, int $block_id, string $context, array $config = []): string
93
    {
94
        $statistics = app(Statistics::class);
95
96
        $show_last_update     = $this->getBlockSetting($block_id, 'show_last_update', '1');
97
        $show_common_surnames = $this->getBlockSetting($block_id, 'show_common_surnames', '1');
98
        $number_of_surnames   = (int) $this->getBlockSetting($block_id, 'number_of_surnames', self::DEFAULT_NUMBER_OF_SURNAMES);
99
        $stat_indi            = $this->getBlockSetting($block_id, 'stat_indi', '1');
100
        $stat_fam             = $this->getBlockSetting($block_id, 'stat_fam', '1');
101
        $stat_sour            = $this->getBlockSetting($block_id, 'stat_sour', '1');
102
        $stat_media           = $this->getBlockSetting($block_id, 'stat_media', '1');
103
        $stat_repo            = $this->getBlockSetting($block_id, 'stat_repo', '1');
104
        $stat_surname         = $this->getBlockSetting($block_id, 'stat_surname', '1');
105
        $stat_events          = $this->getBlockSetting($block_id, 'stat_events', '1');
106
        $stat_users           = $this->getBlockSetting($block_id, 'stat_users', '1');
107
        $stat_first_birth     = $this->getBlockSetting($block_id, 'stat_first_birth', '1');
108
        $stat_last_birth      = $this->getBlockSetting($block_id, 'stat_last_birth', '1');
109
        $stat_first_death     = $this->getBlockSetting($block_id, 'stat_first_death', '1');
110
        $stat_last_death      = $this->getBlockSetting($block_id, 'stat_last_death', '1');
111
        $stat_long_life       = $this->getBlockSetting($block_id, 'stat_long_life', '1');
112
        $stat_avg_life        = $this->getBlockSetting($block_id, 'stat_avg_life', '1');
113
        $stat_most_chil       = $this->getBlockSetting($block_id, 'stat_most_chil', '1');
114
        $stat_avg_chil        = $this->getBlockSetting($block_id, 'stat_avg_chil', '1');
115
116
        extract($config, EXTR_OVERWRITE);
117
118
        if ($show_common_surnames) {
119
            // Use the count of base surnames.
120
            $top_surnames = DB::table('name')
121
                ->where('n_file', '=', $tree->id())
122
                ->where('n_type', '<>', '_MARNM')
123
                ->whereNotIn('n_surn', [Individual::NOMEN_NESCIO, ''])
124
                ->groupBy(['n_surn'])
125
                ->orderByDesc(new Expression('COUNT(n_surn)'))
126
                ->take($number_of_surnames)
127
                ->pluck('n_surn');
128
129
            $all_surnames = [];
130
131
            foreach ($top_surnames as $top_surname) {
132
                $variants = DB::table('name')
133
                    ->where('n_file', '=', $tree->id())
134
                    ->where(new Expression('n_surn /*! COLLATE utf8_bin */'), '=', $top_surname)
135
                    ->groupBy(['surname'])
136
                    ->select([new Expression('n_surname /*! COLLATE utf8_bin */ AS surname'), new Expression('count(*) AS total')])
137
                    ->pluck('total', 'surname')
138
                    ->all();
139
140
                $all_surnames[$top_surname] = $variants;
141
            }
142
143
            uksort($all_surnames, I18N::comparator());
144
145
            // Find a module providing individual lists
146
            $module = $this->module_service
147
                ->findByComponent(ModuleListInterface::class, $tree, Auth::user())
148
                ->first(static fn (ModuleInterface $module): bool => $module instanceof IndividualListModule);
149
150
            $surnames = view('lists/surnames-compact-list', [
151
                'module'   => $module,
152
                'totals'   => false,
153
                'surnames' => $all_surnames,
154
                'tree'     => $tree,
155
            ]);
156
        } else {
157
            $surnames = '';
158
        }
159
160
        $content = view('modules/gedcom_stats/statistics', [
161
            'show_last_update'     => $show_last_update,
162
            'show_common_surnames' => $show_common_surnames,
163
            'number_of_surnames'   => $number_of_surnames,
164
            'stat_indi'            => $stat_indi,
165
            'stat_fam'             => $stat_fam,
166
            'stat_sour'            => $stat_sour,
167
            'stat_media'           => $stat_media,
168
            'stat_repo'            => $stat_repo,
169
            'stat_surname'         => $stat_surname,
170
            'stat_events'          => $stat_events,
171
            'stat_users'           => $stat_users,
172
            'stat_first_birth'     => $stat_first_birth,
173
            'stat_last_birth'      => $stat_last_birth,
174
            'stat_first_death'     => $stat_first_death,
175
            'stat_last_death'      => $stat_last_death,
176
            'stat_long_life'       => $stat_long_life,
177
            'stat_avg_life'        => $stat_avg_life,
178
            'stat_most_chil'       => $stat_most_chil,
179
            'stat_avg_chil'        => $stat_avg_chil,
180
            'stats'                => $statistics,
181
            'surnames'             => $surnames,
182
        ]);
183
184
        if ($context !== self::CONTEXT_EMBED) {
185
            return view('modules/block-template', [
186
                'block'      => Str::kebab($this->name()),
187
                'id'         => $block_id,
188
                'config_url' => $this->configUrl($tree, $context, $block_id),
189
                'title'      => $this->title(),
190
                'content'    => $content,
191
            ]);
192
        }
193
194
        return $content;
195
    }
196
197
    /**
198
     * Should this block load asynchronously using AJAX?
199
     *
200
     * Simple blocks are faster in-line, more complex ones can be loaded later.
201
     *
202
     * @return bool
203
     */
204
    public function loadAjax(): bool
205
    {
206
        return true;
207
    }
208
209
    /**
210
     * Can this block be shown on the user’s home page?
211
     *
212
     * @return bool
213
     */
214
    public function isUserBlock(): bool
215
    {
216
        return true;
217
    }
218
219
    /**
220
     * Can this block be shown on the tree’s home page?
221
     *
222
     * @return bool
223
     */
224
    public function isTreeBlock(): bool
225
    {
226
        return true;
227
    }
228
229
    /**
230
     * Update the configuration for a block.
231
     *
232
     * @param ServerRequestInterface $request
233
     * @param int     $block_id
234
     *
235
     * @return void
236
     */
237
    public function saveBlockConfiguration(ServerRequestInterface $request, int $block_id): void
238
    {
239
        $params = (array) $request->getParsedBody();
240
241
        $this->setBlockSetting($block_id, 'show_last_update', $params['show_last_update'] ?? '');
242
        $this->setBlockSetting($block_id, 'show_common_surnames', $params['show_common_surnames'] ?? '');
243
        $this->setBlockSetting($block_id, 'number_of_surnames', $params['number_of_surnames']);
244
        $this->setBlockSetting($block_id, 'stat_indi', $params['stat_indi'] ?? '');
245
        $this->setBlockSetting($block_id, 'stat_fam', $params['stat_fam'] ?? '');
246
        $this->setBlockSetting($block_id, 'stat_sour', $params['stat_sour'] ?? '');
247
        $this->setBlockSetting($block_id, 'stat_other', $params['stat_other'] ?? '');
248
        $this->setBlockSetting($block_id, 'stat_media', $params['stat_media'] ?? '');
249
        $this->setBlockSetting($block_id, 'stat_repo', $params['stat_repo'] ?? '');
250
        $this->setBlockSetting($block_id, 'stat_surname', $params['stat_surname'] ?? '');
251
        $this->setBlockSetting($block_id, 'stat_events', $params['stat_events'] ?? '');
252
        $this->setBlockSetting($block_id, 'stat_users', $params['stat_users'] ?? '');
253
        $this->setBlockSetting($block_id, 'stat_first_birth', $params['stat_first_birth'] ?? '');
254
        $this->setBlockSetting($block_id, 'stat_last_birth', $params['stat_last_birth'] ?? '');
255
        $this->setBlockSetting($block_id, 'stat_first_death', $params['stat_first_death'] ?? '');
256
        $this->setBlockSetting($block_id, 'stat_last_death', $params['stat_last_death'] ?? '');
257
        $this->setBlockSetting($block_id, 'stat_long_life', $params['stat_long_life'] ?? '');
258
        $this->setBlockSetting($block_id, 'stat_avg_life', $params['stat_avg_life'] ?? '');
259
        $this->setBlockSetting($block_id, 'stat_most_chil', $params['stat_most_chil'] ?? '');
260
        $this->setBlockSetting($block_id, 'stat_avg_chil', $params['stat_avg_chil'] ?? '');
261
    }
262
263
    /**
264
     * An HTML form to edit block settings
265
     *
266
     * @param Tree $tree
267
     * @param int  $block_id
268
     *
269
     * @return string
270
     */
271
    public function editBlockConfiguration(Tree $tree, int $block_id): string
272
    {
273
        $show_last_update     = $this->getBlockSetting($block_id, 'show_last_update', '1');
274
        $show_common_surnames = $this->getBlockSetting($block_id, 'show_common_surnames', '1');
275
        $number_of_surnames   = $this->getBlockSetting($block_id, 'number_of_surnames', self::DEFAULT_NUMBER_OF_SURNAMES);
276
        $stat_indi            = $this->getBlockSetting($block_id, 'stat_indi', '1');
277
        $stat_fam             = $this->getBlockSetting($block_id, 'stat_fam', '1');
278
        $stat_sour            = $this->getBlockSetting($block_id, 'stat_sour', '1');
279
        $stat_media           = $this->getBlockSetting($block_id, 'stat_media', '1');
280
        $stat_repo            = $this->getBlockSetting($block_id, 'stat_repo', '1');
281
        $stat_surname         = $this->getBlockSetting($block_id, 'stat_surname', '1');
282
        $stat_events          = $this->getBlockSetting($block_id, 'stat_events', '1');
283
        $stat_users           = $this->getBlockSetting($block_id, 'stat_users', '1');
284
        $stat_first_birth     = $this->getBlockSetting($block_id, 'stat_first_birth', '1');
285
        $stat_last_birth      = $this->getBlockSetting($block_id, 'stat_last_birth', '1');
286
        $stat_first_death     = $this->getBlockSetting($block_id, 'stat_first_death', '1');
287
        $stat_last_death      = $this->getBlockSetting($block_id, 'stat_last_death', '1');
288
        $stat_long_life       = $this->getBlockSetting($block_id, 'stat_long_life', '1');
289
        $stat_avg_life        = $this->getBlockSetting($block_id, 'stat_avg_life', '1');
290
        $stat_most_chil       = $this->getBlockSetting($block_id, 'stat_most_chil', '1');
291
        $stat_avg_chil        = $this->getBlockSetting($block_id, 'stat_avg_chil', '1');
292
293
        return view('modules/gedcom_stats/config', [
294
            'show_last_update'     => $show_last_update,
295
            'show_common_surnames' => $show_common_surnames,
296
            'number_of_surnames'   => $number_of_surnames,
297
            'stat_indi'            => $stat_indi,
298
            'stat_fam'             => $stat_fam,
299
            'stat_sour'            => $stat_sour,
300
            'stat_media'           => $stat_media,
301
            'stat_repo'            => $stat_repo,
302
            'stat_surname'         => $stat_surname,
303
            'stat_events'          => $stat_events,
304
            'stat_users'           => $stat_users,
305
            'stat_first_birth'     => $stat_first_birth,
306
            'stat_last_birth'      => $stat_last_birth,
307
            'stat_first_death'     => $stat_first_death,
308
            'stat_last_death'      => $stat_last_death,
309
            'stat_long_life'       => $stat_long_life,
310
            'stat_avg_life'        => $stat_avg_life,
311
            'stat_most_chil'       => $stat_most_chil,
312
            'stat_avg_chil'        => $stat_avg_chil,
313
        ]);
314
    }
315
}
316