Passed
Push — feature/code-analysis ( 4fe35d...c99b5b )
by Jonathan
11:08 queued 07:29
created
app/Module/AdminTasks/Tasks/HealthCheckEmailTask.php 1 patch
Indentation   +162 added lines, -162 removed lines patch added patch discarded remove patch
@@ -41,166 +41,166 @@
 block discarded – undo
41 41
  */
42 42
 class HealthCheckEmailTask implements TaskInterface, ConfigurableTaskInterface
43 43
 {
44
-    /**
45
-     * Name of the Tree preference to check if the task is enabled for that tree
46
-     * @var string
47
-     */
48
-    public const TREE_PREFERENCE_NAME = 'MAJ_AT_HEALTHCHECK_ENABLED';
49
-
50
-    private ?AdminTasksModule $module;
51
-    private HealthCheckService $healthcheck_service;
52
-    private EmailService $email_service;
53
-    private UserService $user_service;
54
-    private TreeService $tree_service;
55
-    private UpgradeService $upgrade_service;
56
-
57
-    /**
58
-     * Constructor for HealthCheckTask
59
-     *
60
-     * @param ModuleService $module_service
61
-     * @param HealthCheckService $healthcheck_service
62
-     * @param EmailService $email_service
63
-     * @param UserService $user_service
64
-     * @param TreeService $tree_service
65
-     * @param UpgradeService $upgrade_service
66
-     */
67
-    public function __construct(
68
-        ModuleService $module_service,
69
-        HealthCheckService $healthcheck_service,
70
-        EmailService $email_service,
71
-        UserService $user_service,
72
-        TreeService $tree_service,
73
-        UpgradeService $upgrade_service
74
-    ) {
75
-        $this->module = $module_service->findByInterface(AdminTasksModule::class)->first();
76
-        $this->healthcheck_service = $healthcheck_service;
77
-        $this->email_service = $email_service;
78
-        $this->user_service = $user_service;
79
-        $this->tree_service = $tree_service;
80
-        $this->upgrade_service = $upgrade_service;
81
-    }
82
-
83
-
84
-    /**
85
-     * {@inheritDoc}
86
-     * @see \MyArtJaub\Webtrees\Contracts\Tasks\TaskInterface::name()
87
-     */
88
-    public function name(): string
89
-    {
90
-        return I18N::translate('Healthcheck Email');
91
-    }
92
-
93
-    /**
94
-     * {@inheritDoc}
95
-     * @see \MyArtJaub\Webtrees\Contracts\Tasks\TaskInterface::defaultFrequency()
96
-     */
97
-    public function defaultFrequency(): int
98
-    {
99
-        return 10080; // = 1 week = 7 * 24 * 60 min
100
-    }
101
-
102
-    /**
103
-     * {@inheritDoc}
104
-     * @see \MyArtJaub\Webtrees\Contracts\Tasks\TaskInterface::run()
105
-     */
106
-    public function run(TaskSchedule $task_schedule): bool
107
-    {
108
-        if ($this->module === null) {
109
-            return false;
110
-        }
111
-
112
-        $res = true;
113
-
114
-        // Compute the number of days to compute
115
-        $interval_lastrun =
116
-            Registry::timestampFactory()->now()->timestamp() -  $task_schedule->lastRunTime()->timestamp();
117
-        $task_freq_seconds = 60 * $task_schedule->frequency();
118
-        $interval = $interval_lastrun > $task_freq_seconds ? $interval_lastrun : $task_freq_seconds;
119
-        $nb_days = (int) CarbonInterval::seconds($interval)->ceilDay()->totalDays;
120
-
121
-        $view_params_site = [
122
-            'nb_days'               =>  $nb_days,
123
-            'upgrade_available'     =>  $this->upgrade_service->isUpgradeAvailable(),
124
-            'latest_version'        =>  $this->upgrade_service->latestVersion(),
125
-            'download_url'          =>  $this->upgrade_service->downloadUrl(),
126
-            'all_users'             =>  $this->user_service->all(),
127
-            'unapproved'            =>  $this->user_service->unapproved(),
128
-            'unverified'            =>  $this->user_service->unverified(),
129
-        ];
130
-
131
-        foreach ($this->tree_service->all() as $tree) {
132
-        /** @var Tree $tree */
133
-
134
-            if ($tree->getPreference(self::TREE_PREFERENCE_NAME) !== '1') {
135
-                continue;
136
-            }
137
-
138
-            $webmaster = $this->user_service->find((int) $tree->getPreference('WEBMASTER_USER_ID'));
139
-            if ($webmaster === null) {
140
-                continue;
141
-            }
142
-            I18N::init($webmaster->getPreference('language'));
143
-
144
-            $error_logs = $this->healthcheck_service->errorLogs($tree, $nb_days);
145
-            $nb_errors = $error_logs->sum('nblogs');
146
-
147
-            $view_params = $view_params_site + [
148
-                'tree'              =>  $tree,
149
-                'total_by_type'     =>  $this->healthcheck_service->countByRecordType($tree),
150
-                'change_by_type'    =>  $this->healthcheck_service->changesByRecordType($tree, $nb_days),
151
-                'error_logs'        =>  $error_logs,
152
-                'nb_errors'         =>  $nb_errors
153
-            ];
154
-
155
-            $res = $res && $this->email_service->send(
156
-                new TreeUser($tree),
157
-                $webmaster,
158
-                new NoReplyUser(),
159
-                I18N::translate('Health Check Report') . ' - ' . I18N::translate('Tree %s', $tree->name()),
160
-                view($this->module->name() . '::tasks/healthcheck/email-healthcheck-text', $view_params),
161
-                view($this->module->name() . '::tasks/healthcheck/email-healthcheck-html', $view_params)
162
-            );
163
-        }
164
-
165
-        return $res;
166
-    }
167
-
168
-    /**
169
-     * {@inheritDoc}
170
-     * @see \MyArtJaub\Webtrees\Contracts\Tasks\ConfigurableTaskInterface::configView()
171
-     */
172
-    public function configView(ServerRequestInterface $request): string
173
-    {
174
-        return $this->module === null ? '' : view($this->module->name() . '::tasks/healthcheck/config', [
175
-            'all_trees'     =>  $this->tree_service->all()
176
-        ]);
177
-    }
178
-
179
-    /**
180
-     * {@inheritDoc}
181
-     * @see \MyArtJaub\Webtrees\Contracts\Tasks\ConfigurableTaskInterface::updateConfig()
182
-     */
183
-    public function updateConfig(ServerRequestInterface $request, TaskSchedule $task_schedule): bool
184
-    {
185
-        try {
186
-            $validator = Validator::parsedBody($request);
187
-
188
-            foreach ($this->tree_service->all() as $tree) {
189
-                if (Auth::isManager($tree)) {
190
-                    $tree_enabled = $validator->boolean('HEALTHCHECK_ENABLED_' . $tree->id(), false);
191
-                    $tree->setPreference(self::TREE_PREFERENCE_NAME, $tree_enabled ? '1' : '0');
192
-                }
193
-            }
194
-            return true;
195
-        } catch (Throwable $ex) {
196
-            Log::addErrorLog(
197
-                sprintf(
198
-                    'Error while updating the Task schedule "%s". Exception: %s',
199
-                    $task_schedule->id(),
200
-                    $ex->getMessage()
201
-                )
202
-            );
203
-        }
204
-        return false;
205
-    }
44
+	/**
45
+	 * Name of the Tree preference to check if the task is enabled for that tree
46
+	 * @var string
47
+	 */
48
+	public const TREE_PREFERENCE_NAME = 'MAJ_AT_HEALTHCHECK_ENABLED';
49
+
50
+	private ?AdminTasksModule $module;
51
+	private HealthCheckService $healthcheck_service;
52
+	private EmailService $email_service;
53
+	private UserService $user_service;
54
+	private TreeService $tree_service;
55
+	private UpgradeService $upgrade_service;
56
+
57
+	/**
58
+	 * Constructor for HealthCheckTask
59
+	 *
60
+	 * @param ModuleService $module_service
61
+	 * @param HealthCheckService $healthcheck_service
62
+	 * @param EmailService $email_service
63
+	 * @param UserService $user_service
64
+	 * @param TreeService $tree_service
65
+	 * @param UpgradeService $upgrade_service
66
+	 */
67
+	public function __construct(
68
+		ModuleService $module_service,
69
+		HealthCheckService $healthcheck_service,
70
+		EmailService $email_service,
71
+		UserService $user_service,
72
+		TreeService $tree_service,
73
+		UpgradeService $upgrade_service
74
+	) {
75
+		$this->module = $module_service->findByInterface(AdminTasksModule::class)->first();
76
+		$this->healthcheck_service = $healthcheck_service;
77
+		$this->email_service = $email_service;
78
+		$this->user_service = $user_service;
79
+		$this->tree_service = $tree_service;
80
+		$this->upgrade_service = $upgrade_service;
81
+	}
82
+
83
+
84
+	/**
85
+	 * {@inheritDoc}
86
+	 * @see \MyArtJaub\Webtrees\Contracts\Tasks\TaskInterface::name()
87
+	 */
88
+	public function name(): string
89
+	{
90
+		return I18N::translate('Healthcheck Email');
91
+	}
92
+
93
+	/**
94
+	 * {@inheritDoc}
95
+	 * @see \MyArtJaub\Webtrees\Contracts\Tasks\TaskInterface::defaultFrequency()
96
+	 */
97
+	public function defaultFrequency(): int
98
+	{
99
+		return 10080; // = 1 week = 7 * 24 * 60 min
100
+	}
101
+
102
+	/**
103
+	 * {@inheritDoc}
104
+	 * @see \MyArtJaub\Webtrees\Contracts\Tasks\TaskInterface::run()
105
+	 */
106
+	public function run(TaskSchedule $task_schedule): bool
107
+	{
108
+		if ($this->module === null) {
109
+			return false;
110
+		}
111
+
112
+		$res = true;
113
+
114
+		// Compute the number of days to compute
115
+		$interval_lastrun =
116
+			Registry::timestampFactory()->now()->timestamp() -  $task_schedule->lastRunTime()->timestamp();
117
+		$task_freq_seconds = 60 * $task_schedule->frequency();
118
+		$interval = $interval_lastrun > $task_freq_seconds ? $interval_lastrun : $task_freq_seconds;
119
+		$nb_days = (int) CarbonInterval::seconds($interval)->ceilDay()->totalDays;
120
+
121
+		$view_params_site = [
122
+			'nb_days'               =>  $nb_days,
123
+			'upgrade_available'     =>  $this->upgrade_service->isUpgradeAvailable(),
124
+			'latest_version'        =>  $this->upgrade_service->latestVersion(),
125
+			'download_url'          =>  $this->upgrade_service->downloadUrl(),
126
+			'all_users'             =>  $this->user_service->all(),
127
+			'unapproved'            =>  $this->user_service->unapproved(),
128
+			'unverified'            =>  $this->user_service->unverified(),
129
+		];
130
+
131
+		foreach ($this->tree_service->all() as $tree) {
132
+		/** @var Tree $tree */
133
+
134
+			if ($tree->getPreference(self::TREE_PREFERENCE_NAME) !== '1') {
135
+				continue;
136
+			}
137
+
138
+			$webmaster = $this->user_service->find((int) $tree->getPreference('WEBMASTER_USER_ID'));
139
+			if ($webmaster === null) {
140
+				continue;
141
+			}
142
+			I18N::init($webmaster->getPreference('language'));
143
+
144
+			$error_logs = $this->healthcheck_service->errorLogs($tree, $nb_days);
145
+			$nb_errors = $error_logs->sum('nblogs');
146
+
147
+			$view_params = $view_params_site + [
148
+				'tree'              =>  $tree,
149
+				'total_by_type'     =>  $this->healthcheck_service->countByRecordType($tree),
150
+				'change_by_type'    =>  $this->healthcheck_service->changesByRecordType($tree, $nb_days),
151
+				'error_logs'        =>  $error_logs,
152
+				'nb_errors'         =>  $nb_errors
153
+			];
154
+
155
+			$res = $res && $this->email_service->send(
156
+				new TreeUser($tree),
157
+				$webmaster,
158
+				new NoReplyUser(),
159
+				I18N::translate('Health Check Report') . ' - ' . I18N::translate('Tree %s', $tree->name()),
160
+				view($this->module->name() . '::tasks/healthcheck/email-healthcheck-text', $view_params),
161
+				view($this->module->name() . '::tasks/healthcheck/email-healthcheck-html', $view_params)
162
+			);
163
+		}
164
+
165
+		return $res;
166
+	}
167
+
168
+	/**
169
+	 * {@inheritDoc}
170
+	 * @see \MyArtJaub\Webtrees\Contracts\Tasks\ConfigurableTaskInterface::configView()
171
+	 */
172
+	public function configView(ServerRequestInterface $request): string
173
+	{
174
+		return $this->module === null ? '' : view($this->module->name() . '::tasks/healthcheck/config', [
175
+			'all_trees'     =>  $this->tree_service->all()
176
+		]);
177
+	}
178
+
179
+	/**
180
+	 * {@inheritDoc}
181
+	 * @see \MyArtJaub\Webtrees\Contracts\Tasks\ConfigurableTaskInterface::updateConfig()
182
+	 */
183
+	public function updateConfig(ServerRequestInterface $request, TaskSchedule $task_schedule): bool
184
+	{
185
+		try {
186
+			$validator = Validator::parsedBody($request);
187
+
188
+			foreach ($this->tree_service->all() as $tree) {
189
+				if (Auth::isManager($tree)) {
190
+					$tree_enabled = $validator->boolean('HEALTHCHECK_ENABLED_' . $tree->id(), false);
191
+					$tree->setPreference(self::TREE_PREFERENCE_NAME, $tree_enabled ? '1' : '0');
192
+				}
193
+			}
194
+			return true;
195
+		} catch (Throwable $ex) {
196
+			Log::addErrorLog(
197
+				sprintf(
198
+					'Error while updating the Task schedule "%s". Exception: %s',
199
+					$task_schedule->id(),
200
+					$ex->getMessage()
201
+				)
202
+			);
203
+		}
204
+		return false;
205
+	}
206 206
 }
Please login to merge, or discard this patch.
app/Module/AdminTasks/Services/TaskScheduleService.php 1 patch
Indentation   +271 added lines, -271 removed lines patch added patch discarded remove patch
@@ -35,275 +35,275 @@
 block discarded – undo
35 35
  */
36 36
 class TaskScheduleService
37 37
 {
38
-    /**
39
-     * Time-out after which the task will be considered not running any more.
40
-     * In seconds, default 5 mins.
41
-     * @var integer
42
-     */
43
-    public const TASK_TIME_OUT = 600;
44
-
45
-    private ModuleService $module_service;
46
-
47
-    /**
48
-     * Constructor for TaskScheduleService
49
-     *
50
-     * @param ModuleService $module_service
51
-     */
52
-    public function __construct(ModuleService $module_service)
53
-    {
54
-        $this->module_service = $module_service;
55
-    }
56
-
57
-    /**
58
-     * Returns all Tasks schedules in database.
59
-     * Stored records can be synchronised with the tasks actually available to the system.
60
-     *
61
-     * @param bool $sync_available Should tasks synchronised with available ones
62
-     * @param bool $include_disabled Should disabled tasks be returned
63
-     * @return Collection<TaskSchedule> Collection of TaskSchedule
64
-     */
65
-    public function all(bool $sync_available = false, bool $include_disabled = true): Collection
66
-    {
67
-        $tasks_schedules = DB::table('maj_admintasks')
68
-            ->select()
69
-            ->get()
70
-            ->map(self::rowMapper());
71
-
72
-        if ($sync_available) {
73
-            $available_tasks = clone $this->available();
74
-            foreach ($tasks_schedules as $task_schedule) {
75
-                /** @var TaskSchedule $task_schedule */
76
-                if ($available_tasks->has($task_schedule->taskId())) {
77
-                    $available_tasks->forget($task_schedule->taskId());
78
-                } else {
79
-                    $this->delete($task_schedule);
80
-                }
81
-            }
82
-
83
-            foreach ($available_tasks as $task_name => $task_class) {
84
-                if (null !== $task = app($task_class)) {
85
-                    $this->insertTask($task_name, $task->defaultFrequency());
86
-                }
87
-            }
88
-
89
-            return $this->all(false, $include_disabled);
90
-        }
91
-
92
-        return $tasks_schedules;
93
-    }
94
-
95
-    /**
96
-     * Returns tasks exposed through modules implementing ModuleTasksProviderInterface.
97
-     *
98
-     * @return Collection<string, string>
99
-     */
100
-    public function available(): Collection
101
-    {
102
-        return Registry::cache()->array()->remember(
103
-            'maj-available-admintasks',
104
-            function (): Collection {
105
-                /** @var Collection<string, string> $tasks */
106
-                $tasks = $this->module_service
107
-                    ->findByInterface(ModuleTasksProviderInterface::class)
108
-                    ->flatMap(fn(ModuleTasksProviderInterface $module) => $module->listTasks());
109
-                return $tasks;
110
-            }
111
-        );
112
-    }
113
-
114
-    /**
115
-     * Find a task schedule by its ID.
116
-     *
117
-     * @param int $task_schedule_id
118
-     * @return TaskSchedule|NULL
119
-     */
120
-    public function find(int $task_schedule_id): ?TaskSchedule
121
-    {
122
-        return DB::table('maj_admintasks')
123
-            ->select()
124
-            ->where('majat_id', '=', $task_schedule_id)
125
-            ->get()
126
-            ->map(self::rowMapper())
127
-            ->first();
128
-    }
129
-
130
-    /**
131
-     * Add a new task schedule with the specified task ID, and frequency if defined.
132
-     * Uses default for other settings.
133
-     *
134
-     * @param string $task_id
135
-     * @param int $frequency
136
-     * @return bool
137
-     */
138
-    public function insertTask(string $task_id, int $frequency = 0): bool
139
-    {
140
-        $values = ['majat_task_id' => $task_id];
141
-        if ($frequency > 0) {
142
-            $values['majat_frequency'] = $frequency;
143
-        }
144
-
145
-        return DB::table('maj_admintasks')
146
-            ->insert($values);
147
-    }
148
-
149
-    /**
150
-     * Update a task schedule.
151
-     * Returns the number of tasks schedules updated.
152
-     *
153
-     * @param TaskSchedule $task_schedule
154
-     * @return int
155
-     */
156
-    public function update(TaskSchedule $task_schedule): int
157
-    {
158
-        return DB::table('maj_admintasks')
159
-            ->where('majat_id', '=', $task_schedule->id())
160
-            ->update([
161
-                'majat_status'      =>  $task_schedule->isEnabled() ? 'enabled' : 'disabled',
162
-                'majat_last_run'    =>  $task_schedule->lastRunTime()->toDateTimeString(),
163
-                'majat_last_result' =>  $task_schedule->wasLastRunSuccess(),
164
-                'majat_frequency'   =>  $task_schedule->frequency(),
165
-                'majat_nb_occur'    =>  $task_schedule->remainingOccurrences(),
166
-                'majat_running'     =>  $task_schedule->isRunning()
167
-            ]);
168
-    }
169
-
170
-    /**
171
-     * Delete a task schedule.
172
-     *
173
-     * @param TaskSchedule $task_schedule
174
-     * @return int
175
-     */
176
-    public function delete(TaskSchedule $task_schedule): int
177
-    {
178
-        return DB::table('maj_admintasks')
179
-            ->where('majat_id', '=', $task_schedule->id())
180
-            ->delete();
181
-    }
182
-
183
-    /**
184
-     * Find a task by its name
185
-     *
186
-     * @param string $task_id
187
-     * @return TaskInterface|NULL
188
-     */
189
-    public function findTask(string $task_id): ?TaskInterface
190
-    {
191
-        if ($this->available()->has($task_id)) {
192
-            return app($this->available()->get($task_id));
193
-        }
194
-        return null;
195
-    }
196
-
197
-    /**
198
-     * Retrieve all tasks that are candidates to be run.
199
-     *
200
-     * @param bool $force Should the run be forced
201
-     * @param string $task_id Specific task ID to be run
202
-     * @return Collection<TaskSchedule>
203
-     */
204
-    public function findTasksToRun(bool $force, string $task_id = null): Collection
205
-    {
206
-        $query = DB::table('maj_admintasks')
207
-            ->select()
208
-            ->where('majat_status', '=', 'enabled')
209
-            ->where(function (Builder $query): void {
210
-
211
-                $query->where('majat_running', '=', 0)
212
-                ->orWhere(
213
-                    'majat_last_run',
214
-                    '<=',
215
-                    Registry::timestampFactory()->now()->addSeconds(-self::TASK_TIME_OUT)->toDateTimeString()
216
-                );
217
-            });
218
-
219
-        if (!$force) {
220
-            $query->where(function (Builder $query): void {
221
-
222
-                $query->where('majat_running', '=', 0)
223
-                    ->orWhereRaw('DATE_ADD(majat_last_run, INTERVAL majat_frequency MINUTE) <= NOW()');
224
-            });
225
-        }
226
-
227
-        if ($task_id !== null) {
228
-            $query->where('majat_task_id', '=', $task_id);
229
-        }
230
-
231
-        return $query->get()->map(self::rowMapper());
232
-    }
233
-
234
-    /**
235
-     * Run the task associated with the schedule.
236
-     * The task will run if either forced to, or its next scheduled run time has been exceeded.
237
-     * The last run time is recorded only if the task is successful.
238
-     *
239
-     * @param TaskSchedule $task_schedule
240
-     * @param boolean $force
241
-     */
242
-    public function run(TaskSchedule $task_schedule, $force = false): void
243
-    {
244
-        /** @var TaskSchedule $task_schedule */
245
-        $task_schedule = DB::table('maj_admintasks')
246
-            ->select()
247
-            ->where('majat_id', '=', $task_schedule->id())
248
-            ->lockForUpdate()
249
-            ->get()
250
-            ->map(self::rowMapper())
251
-            ->first();
252
-
253
-        if (
254
-            !$task_schedule->isRunning() &&
255
-            ($force ||
256
-                $task_schedule->lastRunTime()->addMinutes($task_schedule->frequency())
257
-                    ->compare(Registry::timestampFactory()->now()) < 0
258
-            )
259
-        ) {
260
-            $task_schedule->setLastResult(false);
261
-
262
-            $task = $this->findTask($task_schedule->taskId());
263
-            if ($task !== null) {
264
-                $task_schedule->startRunning();
265
-                $this->update($task_schedule);
266
-
267
-                $first_error = $task_schedule->wasLastRunSuccess();
268
-                try {
269
-                    $task_schedule->setLastResult($task->run($task_schedule));
270
-                } catch (Throwable $ex) {
271
-                    if ($first_error) { // Only record the first error, as this could fill the log.
272
-                        Log::addErrorLog(I18N::translate('Error while running task %s:', $task->name()) . ' ' .
273
-                            '[' . get_class($ex) . '] ' . $ex->getMessage() . ' ' . $ex->getFile() . ':'
274
-                            . $ex->getLine() . PHP_EOL . $ex->getTraceAsString());
275
-                    }
276
-                }
277
-
278
-                if ($task_schedule->wasLastRunSuccess()) {
279
-                    $task_schedule->setLastRunTime(Registry::timestampFactory()->now());
280
-                    $task_schedule->decrementRemainingOccurrences();
281
-                }
282
-                $task_schedule->stopRunning();
283
-            }
284
-            $this->update($task_schedule);
285
-        }
286
-    }
287
-
288
-    /**
289
-     * Mapper to return a TaskSchedule object from an object.
290
-     *
291
-     * @return Closure(stdClass $row): TaskSchedule
292
-     */
293
-    public static function rowMapper(): Closure
294
-    {
295
-        return static function (stdClass $row): TaskSchedule {
296
-
297
-            return new TaskSchedule(
298
-                (int) $row->majat_id,
299
-                $row->majat_task_id,
300
-                $row->majat_status === 'enabled',
301
-                Registry::timestampFactory()->fromString($row->majat_last_run),
302
-                (bool) $row->majat_last_result,
303
-                (int) $row->majat_frequency,
304
-                (int) $row->majat_nb_occur,
305
-                (bool) $row->majat_running
306
-            );
307
-        };
308
-    }
38
+	/**
39
+	 * Time-out after which the task will be considered not running any more.
40
+	 * In seconds, default 5 mins.
41
+	 * @var integer
42
+	 */
43
+	public const TASK_TIME_OUT = 600;
44
+
45
+	private ModuleService $module_service;
46
+
47
+	/**
48
+	 * Constructor for TaskScheduleService
49
+	 *
50
+	 * @param ModuleService $module_service
51
+	 */
52
+	public function __construct(ModuleService $module_service)
53
+	{
54
+		$this->module_service = $module_service;
55
+	}
56
+
57
+	/**
58
+	 * Returns all Tasks schedules in database.
59
+	 * Stored records can be synchronised with the tasks actually available to the system.
60
+	 *
61
+	 * @param bool $sync_available Should tasks synchronised with available ones
62
+	 * @param bool $include_disabled Should disabled tasks be returned
63
+	 * @return Collection<TaskSchedule> Collection of TaskSchedule
64
+	 */
65
+	public function all(bool $sync_available = false, bool $include_disabled = true): Collection
66
+	{
67
+		$tasks_schedules = DB::table('maj_admintasks')
68
+			->select()
69
+			->get()
70
+			->map(self::rowMapper());
71
+
72
+		if ($sync_available) {
73
+			$available_tasks = clone $this->available();
74
+			foreach ($tasks_schedules as $task_schedule) {
75
+				/** @var TaskSchedule $task_schedule */
76
+				if ($available_tasks->has($task_schedule->taskId())) {
77
+					$available_tasks->forget($task_schedule->taskId());
78
+				} else {
79
+					$this->delete($task_schedule);
80
+				}
81
+			}
82
+
83
+			foreach ($available_tasks as $task_name => $task_class) {
84
+				if (null !== $task = app($task_class)) {
85
+					$this->insertTask($task_name, $task->defaultFrequency());
86
+				}
87
+			}
88
+
89
+			return $this->all(false, $include_disabled);
90
+		}
91
+
92
+		return $tasks_schedules;
93
+	}
94
+
95
+	/**
96
+	 * Returns tasks exposed through modules implementing ModuleTasksProviderInterface.
97
+	 *
98
+	 * @return Collection<string, string>
99
+	 */
100
+	public function available(): Collection
101
+	{
102
+		return Registry::cache()->array()->remember(
103
+			'maj-available-admintasks',
104
+			function (): Collection {
105
+				/** @var Collection<string, string> $tasks */
106
+				$tasks = $this->module_service
107
+					->findByInterface(ModuleTasksProviderInterface::class)
108
+					->flatMap(fn(ModuleTasksProviderInterface $module) => $module->listTasks());
109
+				return $tasks;
110
+			}
111
+		);
112
+	}
113
+
114
+	/**
115
+	 * Find a task schedule by its ID.
116
+	 *
117
+	 * @param int $task_schedule_id
118
+	 * @return TaskSchedule|NULL
119
+	 */
120
+	public function find(int $task_schedule_id): ?TaskSchedule
121
+	{
122
+		return DB::table('maj_admintasks')
123
+			->select()
124
+			->where('majat_id', '=', $task_schedule_id)
125
+			->get()
126
+			->map(self::rowMapper())
127
+			->first();
128
+	}
129
+
130
+	/**
131
+	 * Add a new task schedule with the specified task ID, and frequency if defined.
132
+	 * Uses default for other settings.
133
+	 *
134
+	 * @param string $task_id
135
+	 * @param int $frequency
136
+	 * @return bool
137
+	 */
138
+	public function insertTask(string $task_id, int $frequency = 0): bool
139
+	{
140
+		$values = ['majat_task_id' => $task_id];
141
+		if ($frequency > 0) {
142
+			$values['majat_frequency'] = $frequency;
143
+		}
144
+
145
+		return DB::table('maj_admintasks')
146
+			->insert($values);
147
+	}
148
+
149
+	/**
150
+	 * Update a task schedule.
151
+	 * Returns the number of tasks schedules updated.
152
+	 *
153
+	 * @param TaskSchedule $task_schedule
154
+	 * @return int
155
+	 */
156
+	public function update(TaskSchedule $task_schedule): int
157
+	{
158
+		return DB::table('maj_admintasks')
159
+			->where('majat_id', '=', $task_schedule->id())
160
+			->update([
161
+				'majat_status'      =>  $task_schedule->isEnabled() ? 'enabled' : 'disabled',
162
+				'majat_last_run'    =>  $task_schedule->lastRunTime()->toDateTimeString(),
163
+				'majat_last_result' =>  $task_schedule->wasLastRunSuccess(),
164
+				'majat_frequency'   =>  $task_schedule->frequency(),
165
+				'majat_nb_occur'    =>  $task_schedule->remainingOccurrences(),
166
+				'majat_running'     =>  $task_schedule->isRunning()
167
+			]);
168
+	}
169
+
170
+	/**
171
+	 * Delete a task schedule.
172
+	 *
173
+	 * @param TaskSchedule $task_schedule
174
+	 * @return int
175
+	 */
176
+	public function delete(TaskSchedule $task_schedule): int
177
+	{
178
+		return DB::table('maj_admintasks')
179
+			->where('majat_id', '=', $task_schedule->id())
180
+			->delete();
181
+	}
182
+
183
+	/**
184
+	 * Find a task by its name
185
+	 *
186
+	 * @param string $task_id
187
+	 * @return TaskInterface|NULL
188
+	 */
189
+	public function findTask(string $task_id): ?TaskInterface
190
+	{
191
+		if ($this->available()->has($task_id)) {
192
+			return app($this->available()->get($task_id));
193
+		}
194
+		return null;
195
+	}
196
+
197
+	/**
198
+	 * Retrieve all tasks that are candidates to be run.
199
+	 *
200
+	 * @param bool $force Should the run be forced
201
+	 * @param string $task_id Specific task ID to be run
202
+	 * @return Collection<TaskSchedule>
203
+	 */
204
+	public function findTasksToRun(bool $force, string $task_id = null): Collection
205
+	{
206
+		$query = DB::table('maj_admintasks')
207
+			->select()
208
+			->where('majat_status', '=', 'enabled')
209
+			->where(function (Builder $query): void {
210
+
211
+				$query->where('majat_running', '=', 0)
212
+				->orWhere(
213
+					'majat_last_run',
214
+					'<=',
215
+					Registry::timestampFactory()->now()->addSeconds(-self::TASK_TIME_OUT)->toDateTimeString()
216
+				);
217
+			});
218
+
219
+		if (!$force) {
220
+			$query->where(function (Builder $query): void {
221
+
222
+				$query->where('majat_running', '=', 0)
223
+					->orWhereRaw('DATE_ADD(majat_last_run, INTERVAL majat_frequency MINUTE) <= NOW()');
224
+			});
225
+		}
226
+
227
+		if ($task_id !== null) {
228
+			$query->where('majat_task_id', '=', $task_id);
229
+		}
230
+
231
+		return $query->get()->map(self::rowMapper());
232
+	}
233
+
234
+	/**
235
+	 * Run the task associated with the schedule.
236
+	 * The task will run if either forced to, or its next scheduled run time has been exceeded.
237
+	 * The last run time is recorded only if the task is successful.
238
+	 *
239
+	 * @param TaskSchedule $task_schedule
240
+	 * @param boolean $force
241
+	 */
242
+	public function run(TaskSchedule $task_schedule, $force = false): void
243
+	{
244
+		/** @var TaskSchedule $task_schedule */
245
+		$task_schedule = DB::table('maj_admintasks')
246
+			->select()
247
+			->where('majat_id', '=', $task_schedule->id())
248
+			->lockForUpdate()
249
+			->get()
250
+			->map(self::rowMapper())
251
+			->first();
252
+
253
+		if (
254
+			!$task_schedule->isRunning() &&
255
+			($force ||
256
+				$task_schedule->lastRunTime()->addMinutes($task_schedule->frequency())
257
+					->compare(Registry::timestampFactory()->now()) < 0
258
+			)
259
+		) {
260
+			$task_schedule->setLastResult(false);
261
+
262
+			$task = $this->findTask($task_schedule->taskId());
263
+			if ($task !== null) {
264
+				$task_schedule->startRunning();
265
+				$this->update($task_schedule);
266
+
267
+				$first_error = $task_schedule->wasLastRunSuccess();
268
+				try {
269
+					$task_schedule->setLastResult($task->run($task_schedule));
270
+				} catch (Throwable $ex) {
271
+					if ($first_error) { // Only record the first error, as this could fill the log.
272
+						Log::addErrorLog(I18N::translate('Error while running task %s:', $task->name()) . ' ' .
273
+							'[' . get_class($ex) . '] ' . $ex->getMessage() . ' ' . $ex->getFile() . ':'
274
+							. $ex->getLine() . PHP_EOL . $ex->getTraceAsString());
275
+					}
276
+				}
277
+
278
+				if ($task_schedule->wasLastRunSuccess()) {
279
+					$task_schedule->setLastRunTime(Registry::timestampFactory()->now());
280
+					$task_schedule->decrementRemainingOccurrences();
281
+				}
282
+				$task_schedule->stopRunning();
283
+			}
284
+			$this->update($task_schedule);
285
+		}
286
+	}
287
+
288
+	/**
289
+	 * Mapper to return a TaskSchedule object from an object.
290
+	 *
291
+	 * @return Closure(stdClass $row): TaskSchedule
292
+	 */
293
+	public static function rowMapper(): Closure
294
+	{
295
+		return static function (stdClass $row): TaskSchedule {
296
+
297
+			return new TaskSchedule(
298
+				(int) $row->majat_id,
299
+				$row->majat_task_id,
300
+				$row->majat_status === 'enabled',
301
+				Registry::timestampFactory()->fromString($row->majat_last_run),
302
+				(bool) $row->majat_last_result,
303
+				(int) $row->majat_frequency,
304
+				(int) $row->majat_nb_occur,
305
+				(bool) $row->majat_running
306
+			);
307
+		};
308
+	}
309 309
 }
Please login to merge, or discard this patch.
app/Module/AdminTasks/Services/HealthCheckService.php 1 patch
Indentation   +93 added lines, -93 removed lines patch added patch discarded remove patch
@@ -27,101 +27,101 @@
 block discarded – undo
27 27
  */
28 28
 class HealthCheckService
29 29
 {
30
-    /**
31
-     * Returns a query collating all gedcom records, for use in other queries
32
-     *
33
-     * @param Tree $tree
34
-     * @return Builder
35
-     */
36
-    private function allGedcomRecords(Tree $tree): Builder
37
-    {
38
-        return DB::table('individuals')
39
-            ->select(DB::raw("'indi' AS ged_type"), 'i_id AS ged_id')->where('i_file', '=', $tree->id())
40
-            ->unionAll(DB::table('families')
41
-                ->select(DB::raw("'fam' AS ged_type"), 'f_id AS ged_id')->where('f_file', '=', $tree->id()))
42
-            ->unionAll(DB::table('sources')
43
-                ->select(DB::raw("'sour' AS ged_type"), 's_id AS ged_id')->where('s_file', '=', $tree->id()))
44
-            ->unionAll(DB::table('media')
45
-                ->select(DB::raw("'media' AS ged_type"), 'm_id AS ged_id')->where('m_file', '=', $tree->id()))
46
-            ->unionAll(DB::table('other')
47
-                ->select(DB::raw('LOWER(o_type) AS ged_type'), 'o_id AS ged_id')->where('o_file', '=', $tree->id()));
48
-    }
30
+	/**
31
+	 * Returns a query collating all gedcom records, for use in other queries
32
+	 *
33
+	 * @param Tree $tree
34
+	 * @return Builder
35
+	 */
36
+	private function allGedcomRecords(Tree $tree): Builder
37
+	{
38
+		return DB::table('individuals')
39
+			->select(DB::raw("'indi' AS ged_type"), 'i_id AS ged_id')->where('i_file', '=', $tree->id())
40
+			->unionAll(DB::table('families')
41
+				->select(DB::raw("'fam' AS ged_type"), 'f_id AS ged_id')->where('f_file', '=', $tree->id()))
42
+			->unionAll(DB::table('sources')
43
+				->select(DB::raw("'sour' AS ged_type"), 's_id AS ged_id')->where('s_file', '=', $tree->id()))
44
+			->unionAll(DB::table('media')
45
+				->select(DB::raw("'media' AS ged_type"), 'm_id AS ged_id')->where('m_file', '=', $tree->id()))
46
+			->unionAll(DB::table('other')
47
+				->select(DB::raw('LOWER(o_type) AS ged_type'), 'o_id AS ged_id')->where('o_file', '=', $tree->id()));
48
+	}
49 49
 
50
-    /**
51
-     * Returns the count of gedcom records by type in a Tree, as a keyed Collection.
52
-     *
53
-     * Collection output:
54
-     *      - Key : gedcom record type
55
-     *      - Value: count of records
56
-     *
57
-     * @param Tree $tree
58
-     * @return Collection<string, int>
59
-     */
60
-    public function countByRecordType(Tree $tree): Collection
61
-    {
62
-        return DB::query()
63
-            ->fromSub($this->allGedcomRecords($tree), 'gedrecords')
64
-            ->select('ged_type', new Expression('COUNT(ged_id) AS total'))
65
-            ->groupBy('ged_type')
66
-            ->pluck('total', 'ged_type');
67
-    }
50
+	/**
51
+	 * Returns the count of gedcom records by type in a Tree, as a keyed Collection.
52
+	 *
53
+	 * Collection output:
54
+	 *      - Key : gedcom record type
55
+	 *      - Value: count of records
56
+	 *
57
+	 * @param Tree $tree
58
+	 * @return Collection<string, int>
59
+	 */
60
+	public function countByRecordType(Tree $tree): Collection
61
+	{
62
+		return DB::query()
63
+			->fromSub($this->allGedcomRecords($tree), 'gedrecords')
64
+			->select('ged_type', new Expression('COUNT(ged_id) AS total'))
65
+			->groupBy('ged_type')
66
+			->pluck('total', 'ged_type');
67
+	}
68 68
 
69
-    /**
70
-     * Returns the count of gedcom records changes by type in a Tree across a number of days, as a keyed Collection.
71
-     *
72
-     * Collection output:
73
-     *      - Key : gedcom record type
74
-     *      - Value: count of changes
75
-     *
76
-     * @param Tree $tree
77
-     * @return Collection<string, int>
78
-     */
79
-    public function changesByRecordType(Tree $tree, int $nb_days): Collection
80
-    {
81
-        return DB::table('change')
82
-            ->joinSub($this->allGedcomRecords($tree), 'gedrecords', function (JoinClause $join) use ($tree): void {
69
+	/**
70
+	 * Returns the count of gedcom records changes by type in a Tree across a number of days, as a keyed Collection.
71
+	 *
72
+	 * Collection output:
73
+	 *      - Key : gedcom record type
74
+	 *      - Value: count of changes
75
+	 *
76
+	 * @param Tree $tree
77
+	 * @return Collection<string, int>
78
+	 */
79
+	public function changesByRecordType(Tree $tree, int $nb_days): Collection
80
+	{
81
+		return DB::table('change')
82
+			->joinSub($this->allGedcomRecords($tree), 'gedrecords', function (JoinClause $join) use ($tree): void {
83 83
 
84
-                $join->on('change.xref', '=', 'gedrecords.ged_id')
85
-                    ->where('change.gedcom_id', '=', $tree->id());
86
-            })
87
-            ->select('ged_type AS type', new Expression('COUNT(change_id) AS count'))
88
-            ->where('change.status', '', 'accepted')
89
-            ->where('change.change_time', '>=', Carbon::now()->subDays($nb_days))
90
-            ->groupBy('ged_type')
91
-            ->pluck('total', 'ged_type');
92
-    }
84
+				$join->on('change.xref', '=', 'gedrecords.ged_id')
85
+					->where('change.gedcom_id', '=', $tree->id());
86
+			})
87
+			->select('ged_type AS type', new Expression('COUNT(change_id) AS count'))
88
+			->where('change.status', '', 'accepted')
89
+			->where('change.change_time', '>=', Carbon::now()->subDays($nb_days))
90
+			->groupBy('ged_type')
91
+			->pluck('total', 'ged_type');
92
+	}
93 93
 
94
-    /**
95
-     * Return the error logs associated with a tree across a number of days, grouped by error message, as a Collection.
96
-     *
97
-     * Collection output:
98
-     *      - Value: stdClass object
99
-     *          - log message:  Error log message
100
-     *          - type:         'site' if no associated Tree, the Tree ID otherwise
101
-     *          - nblogs:       The number of occurrence of the same error message
102
-     *          - lastoccurred: Date/time of the last occurrence of the error message
103
-     *
104
-     * @param Tree $tree
105
-     * @param int $nb_days
106
-     * @return Collection<\stdClass>
107
-     */
108
-    public function errorLogs(Tree $tree, int $nb_days): Collection
109
-    {
110
-        return DB::table('log')
111
-            ->select(
112
-                'log_message',
113
-                new Expression("IFNULL(gedcom_id, 'site') as type"),
114
-                new Expression('COUNT(log_id) AS nblogs'),
115
-                new Expression('MAX(log_time) AS lastoccurred')
116
-            )
117
-            ->where('log_type', '=', 'error')
118
-            ->where(function (Builder $query) use ($tree): void {
119
-                $query->where('gedcom_id', '=', $tree->id())
120
-                    ->orWhereNull('gedcom_id');
121
-            })
122
-            ->where('log_time', '>=', Carbon::now()->subDays($nb_days))
123
-            ->groupBy('log_message', 'gedcom_id')
124
-            ->orderByDesc('lastoccurred')
125
-            ->get();
126
-    }
94
+	/**
95
+	 * Return the error logs associated with a tree across a number of days, grouped by error message, as a Collection.
96
+	 *
97
+	 * Collection output:
98
+	 *      - Value: stdClass object
99
+	 *          - log message:  Error log message
100
+	 *          - type:         'site' if no associated Tree, the Tree ID otherwise
101
+	 *          - nblogs:       The number of occurrence of the same error message
102
+	 *          - lastoccurred: Date/time of the last occurrence of the error message
103
+	 *
104
+	 * @param Tree $tree
105
+	 * @param int $nb_days
106
+	 * @return Collection<\stdClass>
107
+	 */
108
+	public function errorLogs(Tree $tree, int $nb_days): Collection
109
+	{
110
+		return DB::table('log')
111
+			->select(
112
+				'log_message',
113
+				new Expression("IFNULL(gedcom_id, 'site') as type"),
114
+				new Expression('COUNT(log_id) AS nblogs'),
115
+				new Expression('MAX(log_time) AS lastoccurred')
116
+			)
117
+			->where('log_type', '=', 'error')
118
+			->where(function (Builder $query) use ($tree): void {
119
+				$query->where('gedcom_id', '=', $tree->id())
120
+					->orWhereNull('gedcom_id');
121
+			})
122
+			->where('log_time', '>=', Carbon::now()->subDays($nb_days))
123
+			->groupBy('log_message', 'gedcom_id')
124
+			->orderByDesc('lastoccurred')
125
+			->get();
126
+	}
127 127
 }
Please login to merge, or discard this patch.
app/Module/GeoDispersion/Views/GeoAnalysisMap.php 1 patch
Indentation   +113 added lines, -113 removed lines patch added patch discarded remove patch
@@ -32,117 +32,117 @@
 block discarded – undo
32 32
  */
33 33
 class GeoAnalysisMap extends AbstractGeoAnalysisView
34 34
 {
35
-    private ?MapColorsConfig $colors_config = null;
36
-
37
-    public function type(): string
38
-    {
39
-        return I18N::translateContext('GEODISPERSION', 'Map');
40
-    }
41
-
42
-    /**
43
-     * {@inheritDoc}
44
-     * @see \MyArtJaub\Webtrees\Module\GeoDispersion\Views\AbstractGeoAnalysisView::icon()
45
-     */
46
-    public function icon(ModuleInterface $module): string
47
-    {
48
-        return view($module->name() . '::icons/view-map', ['type' => $this->type()]);
49
-    }
50
-
51
-    /**
52
-     * {@inheritDoc}
53
-     * @see \MyArtJaub\Webtrees\Module\GeoDispersion\Views\AbstractGeoAnalysisView::globalSettingsContent()
54
-     */
55
-    public function globalSettingsContent(ModuleInterface $module): string
56
-    {
57
-        return view($module->name() . '::admin/view-edit-map', [
58
-            'module_name'   =>  $module->name(),
59
-            'view'          =>  $this,
60
-            'colors'        =>  $this->colors(),
61
-            'map_adapters'  =>  app(MapAdapterDataService::class)->allForView($this, true)
62
-        ]);
63
-    }
64
-
65
-    /**
66
-     * {@inheritDoc}
67
-     * @see \MyArtJaub\Webtrees\Module\GeoDispersion\Views\AbstractGeoAnalysisView::withGlobalSettingsUpdate()
68
-     * @return static
69
-     */
70
-    public function withGlobalSettingsUpdate(ServerRequestInterface $request): self
71
-    {
72
-        $default_color  = Validator::parsedBody($request)->string('view_map_color_default', '');
73
-        $stroke_color   = Validator::parsedBody($request)->string('view_map_color_stroke', '');
74
-        $maxvalue_color  = Validator::parsedBody($request)->string('view_map_color_maxvalue', '');
75
-        $hover_color  = Validator::parsedBody($request)->string('view_map_color_hover', '');
76
-
77
-        try {
78
-            return $this->withColors(new MapColorsConfig(
79
-                Hex::fromString($default_color),
80
-                Hex::fromString($stroke_color),
81
-                Hex::fromString($maxvalue_color),
82
-                Hex::fromString($hover_color)
83
-            ));
84
-        } catch (InvalidColorValue $ex) {
85
-        }
86
-
87
-        return $this;
88
-    }
89
-
90
-    /**
91
-     * {@inheritDoc}
92
-     * @see \MyArtJaub\Webtrees\Module\GeoDispersion\Views\AbstractGeoAnalysisView::globalTabContent()
93
-     */
94
-    public function globalTabContent(GeoDispersionModule $module, GeoAnalysisResult $result, array $params): string
95
-    {
96
-        $map_adapters = app(MapAdapterDataService::class)->allForView($this);
97
-
98
-        $adapter_result = null;
99
-        foreach ($map_adapters as $map_adapter) {
100
-            $adapter_result_tmp = $map_adapter->convert($result);
101
-            $adapter_result = $adapter_result === null ?
102
-                $adapter_result_tmp :
103
-                $adapter_result->merge($adapter_result_tmp);
104
-        }
105
-
106
-        if ($adapter_result === null) {
107
-            return view($module->name() . '::errors/tab-error', [
108
-                'message'   =>  I18N::translate('The map could not be loaded.'),
109
-            ]);
110
-        }
111
-
112
-        return view($module->name() . '::geoanalysisview-tab-glb-map', $params + [
113
-            'result'            =>  $adapter_result->geoAnalysisResult(),
114
-            'features'          =>  $adapter_result->features(),
115
-            'colors'            =>  $this->colors(),
116
-            'leaflet_config'    =>  app(LeafletJsService::class)->config(),
117
-            'js_script_url'     =>  $module->assetUrl('js/geodispersion.min.js')
118
-        ]);
119
-    }
120
-
121
-    /**
122
-     * Get the color scheme configuration for the map view
123
-     *
124
-     * @return MapColorsConfig
125
-     */
126
-    public function colors(): MapColorsConfig
127
-    {
128
-        return $this->colors_config ?? new MapColorsConfig(
129
-            new Rgb(245, 245, 245),
130
-            new Rgb(213, 213, 213),
131
-            new Rgb(4, 147, 171),
132
-            new Rgb(255, 102, 0)
133
-        );
134
-    }
135
-
136
-    /**
137
-     * Returns a map view with a new color scheme configuration
138
-     *
139
-     * @param MapColorsConfig $config
140
-     * @return static
141
-     */
142
-    public function withColors(?MapColorsConfig $config): self
143
-    {
144
-        $new = clone $this;
145
-        $new->colors_config = $config;
146
-        return $new;
147
-    }
35
+	private ?MapColorsConfig $colors_config = null;
36
+
37
+	public function type(): string
38
+	{
39
+		return I18N::translateContext('GEODISPERSION', 'Map');
40
+	}
41
+
42
+	/**
43
+	 * {@inheritDoc}
44
+	 * @see \MyArtJaub\Webtrees\Module\GeoDispersion\Views\AbstractGeoAnalysisView::icon()
45
+	 */
46
+	public function icon(ModuleInterface $module): string
47
+	{
48
+		return view($module->name() . '::icons/view-map', ['type' => $this->type()]);
49
+	}
50
+
51
+	/**
52
+	 * {@inheritDoc}
53
+	 * @see \MyArtJaub\Webtrees\Module\GeoDispersion\Views\AbstractGeoAnalysisView::globalSettingsContent()
54
+	 */
55
+	public function globalSettingsContent(ModuleInterface $module): string
56
+	{
57
+		return view($module->name() . '::admin/view-edit-map', [
58
+			'module_name'   =>  $module->name(),
59
+			'view'          =>  $this,
60
+			'colors'        =>  $this->colors(),
61
+			'map_adapters'  =>  app(MapAdapterDataService::class)->allForView($this, true)
62
+		]);
63
+	}
64
+
65
+	/**
66
+	 * {@inheritDoc}
67
+	 * @see \MyArtJaub\Webtrees\Module\GeoDispersion\Views\AbstractGeoAnalysisView::withGlobalSettingsUpdate()
68
+	 * @return static
69
+	 */
70
+	public function withGlobalSettingsUpdate(ServerRequestInterface $request): self
71
+	{
72
+		$default_color  = Validator::parsedBody($request)->string('view_map_color_default', '');
73
+		$stroke_color   = Validator::parsedBody($request)->string('view_map_color_stroke', '');
74
+		$maxvalue_color  = Validator::parsedBody($request)->string('view_map_color_maxvalue', '');
75
+		$hover_color  = Validator::parsedBody($request)->string('view_map_color_hover', '');
76
+
77
+		try {
78
+			return $this->withColors(new MapColorsConfig(
79
+				Hex::fromString($default_color),
80
+				Hex::fromString($stroke_color),
81
+				Hex::fromString($maxvalue_color),
82
+				Hex::fromString($hover_color)
83
+			));
84
+		} catch (InvalidColorValue $ex) {
85
+		}
86
+
87
+		return $this;
88
+	}
89
+
90
+	/**
91
+	 * {@inheritDoc}
92
+	 * @see \MyArtJaub\Webtrees\Module\GeoDispersion\Views\AbstractGeoAnalysisView::globalTabContent()
93
+	 */
94
+	public function globalTabContent(GeoDispersionModule $module, GeoAnalysisResult $result, array $params): string
95
+	{
96
+		$map_adapters = app(MapAdapterDataService::class)->allForView($this);
97
+
98
+		$adapter_result = null;
99
+		foreach ($map_adapters as $map_adapter) {
100
+			$adapter_result_tmp = $map_adapter->convert($result);
101
+			$adapter_result = $adapter_result === null ?
102
+				$adapter_result_tmp :
103
+				$adapter_result->merge($adapter_result_tmp);
104
+		}
105
+
106
+		if ($adapter_result === null) {
107
+			return view($module->name() . '::errors/tab-error', [
108
+				'message'   =>  I18N::translate('The map could not be loaded.'),
109
+			]);
110
+		}
111
+
112
+		return view($module->name() . '::geoanalysisview-tab-glb-map', $params + [
113
+			'result'            =>  $adapter_result->geoAnalysisResult(),
114
+			'features'          =>  $adapter_result->features(),
115
+			'colors'            =>  $this->colors(),
116
+			'leaflet_config'    =>  app(LeafletJsService::class)->config(),
117
+			'js_script_url'     =>  $module->assetUrl('js/geodispersion.min.js')
118
+		]);
119
+	}
120
+
121
+	/**
122
+	 * Get the color scheme configuration for the map view
123
+	 *
124
+	 * @return MapColorsConfig
125
+	 */
126
+	public function colors(): MapColorsConfig
127
+	{
128
+		return $this->colors_config ?? new MapColorsConfig(
129
+			new Rgb(245, 245, 245),
130
+			new Rgb(213, 213, 213),
131
+			new Rgb(4, 147, 171),
132
+			new Rgb(255, 102, 0)
133
+		);
134
+	}
135
+
136
+	/**
137
+	 * Returns a map view with a new color scheme configuration
138
+	 *
139
+	 * @param MapColorsConfig $config
140
+	 * @return static
141
+	 */
142
+	public function withColors(?MapColorsConfig $config): self
143
+	{
144
+		$new = clone $this;
145
+		$new->colors_config = $config;
146
+		return $new;
147
+	}
148 148
 }
Please login to merge, or discard this patch.
app/Module/GeoDispersion/Schema/Migration0.php 1 patch
Indentation   +8 added lines, -8 removed lines patch added patch discarded remove patch
@@ -21,12 +21,12 @@
 block discarded – undo
21 21
  */
22 22
 class Migration0 implements MigrationInterface
23 23
 {
24
-    /**
25
-     * {@inheritDoc}
26
-     * @see \Fisharebest\Webtrees\Schema\MigrationInterface::upgrade()
27
-     */
28
-    public function upgrade(): void
29
-    {
30
-        // This migration has been superseded by migration 1.
31
-    }
24
+	/**
25
+	 * {@inheritDoc}
26
+	 * @see \Fisharebest\Webtrees\Schema\MigrationInterface::upgrade()
27
+	 */
28
+	public function upgrade(): void
29
+	{
30
+		// This migration has been superseded by migration 1.
31
+	}
32 32
 }
Please login to merge, or discard this patch.
app/Module/GeoDispersion/Schema/Migration2.php 1 patch
Indentation   +193 added lines, -193 removed lines patch added patch discarded remove patch
@@ -39,219 +39,219 @@
 block discarded – undo
39 39
  */
40 40
 class Migration2 implements MigrationInterface
41 41
 {
42
-    /**
43
-     * Mapping from old map definitions to new ones
44
-     * @var array<string,mixed> MAPS_XML_MAPPING
45
-     */
46
-    private const MAPS_XML_MAPPING = [
47
-        'aubracmargeridebycommunes.xml' =>  'fr-area-aubrac-lot-margeride-planeze-communes',
48
-        'calvadosbycommunes.xml'        =>  'fr-dpt-14-communes',
49
-        'cantalbycommunes.xml'          =>  'fr-dpt-15-communes',
50
-        'cotesdarmorbycommunes.xml'     =>  'fr-dpt-22-communes',
51
-        'essonnebycommunes.xml'         =>  'fr-dpt-91-communes',
52
-        'eurebycommunes.xml'            =>  'fr-dpt-27-communes',
53
-        'eureetloirbycommunes.xml'      =>  'fr-dpt-28-communes',
54
-        'francebydepartements.xml'      =>  'fr-metropole-departements',
55
-        'francebyregions1970.xml'       =>  'fr-metropole-regions-1970',
56
-        'francebyregions2016.xml'       =>  'fr-metropole-regions-2016',
57
-        'hauteloirebycommunes.xml'      =>  'fr-dpt-43-communes',
58
-        'illeetvilainebycommunes.xml'   =>  'fr-dpt-35-communes',
59
-        'loiretbycommunes.xml'          =>  'fr-dpt-45-communes',
60
-        'lozerebycodepostaux.xml'       =>  'fr-dpt-48-codespostaux',
61
-        'lozerebycommunes.xml'          =>  'fr-dpt-48-communes',
62
-        'mayennebycommunes.xml'         =>  'fr-dpt-53-communes',
63
-        'oisebycommunes.xml'            =>  'fr-dpt-60-communes',
64
-        'ornebycommunes.xml'            =>  'fr-dpt-61-communes',
65
-        'puydedomebycommunes.xml'       =>  'fr-dpt-63-communes',
66
-        'sarthebycommunes.xml'          =>  'fr-dpt-72-communes',
67
-        'seinemaritimebycommunes.xml'   =>  'fr-dpt-76-communes',
68
-        'seinesommeoisebycommunes.xml'  =>  ['fr-dpt-60-communes', 'fr-dpt-76-communes', 'fr-dpt-80-communes'],
69
-        'valdoisebycommunes.xml'        =>  'fr-dpt-95-communes',
70
-        'yvelinesbycommunes.xml'        =>  'fr-dpt-78-communes'
71
-    ];
42
+	/**
43
+	 * Mapping from old map definitions to new ones
44
+	 * @var array<string,mixed> MAPS_XML_MAPPING
45
+	 */
46
+	private const MAPS_XML_MAPPING = [
47
+		'aubracmargeridebycommunes.xml' =>  'fr-area-aubrac-lot-margeride-planeze-communes',
48
+		'calvadosbycommunes.xml'        =>  'fr-dpt-14-communes',
49
+		'cantalbycommunes.xml'          =>  'fr-dpt-15-communes',
50
+		'cotesdarmorbycommunes.xml'     =>  'fr-dpt-22-communes',
51
+		'essonnebycommunes.xml'         =>  'fr-dpt-91-communes',
52
+		'eurebycommunes.xml'            =>  'fr-dpt-27-communes',
53
+		'eureetloirbycommunes.xml'      =>  'fr-dpt-28-communes',
54
+		'francebydepartements.xml'      =>  'fr-metropole-departements',
55
+		'francebyregions1970.xml'       =>  'fr-metropole-regions-1970',
56
+		'francebyregions2016.xml'       =>  'fr-metropole-regions-2016',
57
+		'hauteloirebycommunes.xml'      =>  'fr-dpt-43-communes',
58
+		'illeetvilainebycommunes.xml'   =>  'fr-dpt-35-communes',
59
+		'loiretbycommunes.xml'          =>  'fr-dpt-45-communes',
60
+		'lozerebycodepostaux.xml'       =>  'fr-dpt-48-codespostaux',
61
+		'lozerebycommunes.xml'          =>  'fr-dpt-48-communes',
62
+		'mayennebycommunes.xml'         =>  'fr-dpt-53-communes',
63
+		'oisebycommunes.xml'            =>  'fr-dpt-60-communes',
64
+		'ornebycommunes.xml'            =>  'fr-dpt-61-communes',
65
+		'puydedomebycommunes.xml'       =>  'fr-dpt-63-communes',
66
+		'sarthebycommunes.xml'          =>  'fr-dpt-72-communes',
67
+		'seinemaritimebycommunes.xml'   =>  'fr-dpt-76-communes',
68
+		'seinesommeoisebycommunes.xml'  =>  ['fr-dpt-60-communes', 'fr-dpt-76-communes', 'fr-dpt-80-communes'],
69
+		'valdoisebycommunes.xml'        =>  'fr-dpt-95-communes',
70
+		'yvelinesbycommunes.xml'        =>  'fr-dpt-78-communes'
71
+	];
72 72
 
73
-    /**
74
-     * {@inheritDoc}
75
-     * @see \Fisharebest\Webtrees\Schema\MigrationInterface::upgrade()
76
-     */
77
-    public function upgrade(): void
78
-    {
79
-        if (!DB::schema()->hasTable('maj_geodispersion')) {
80
-            return;
81
-        }
73
+	/**
74
+	 * {@inheritDoc}
75
+	 * @see \Fisharebest\Webtrees\Schema\MigrationInterface::upgrade()
76
+	 */
77
+	public function upgrade(): void
78
+	{
79
+		if (!DB::schema()->hasTable('maj_geodispersion')) {
80
+			return;
81
+		}
82 82
 
83
-        /** @var TreeService $tree_service */
84
-        $tree_service = app(TreeService::class);
85
-        /** @var GeoAnalysisViewDataService $geoview_data_service */
86
-        $geoview_data_service = app(GeoAnalysisViewDataService::class);
83
+		/** @var TreeService $tree_service */
84
+		$tree_service = app(TreeService::class);
85
+		/** @var GeoAnalysisViewDataService $geoview_data_service */
86
+		$geoview_data_service = app(GeoAnalysisViewDataService::class);
87 87
 
88
-        $existing_views = DB::table('maj_geodispersion')
89
-            ->select()
90
-            ->get();
88
+		$existing_views = DB::table('maj_geodispersion')
89
+			->select()
90
+			->get();
91 91
 
92
-        foreach ($existing_views as $old_view) {
93
-            try {
94
-                $tree = $tree_service->find((int) $old_view->majgd_file);
95
-            } catch (RuntimeException $ex) {
96
-                continue;
97
-            }
92
+		foreach ($existing_views as $old_view) {
93
+			try {
94
+				$tree = $tree_service->find((int) $old_view->majgd_file);
95
+			} catch (RuntimeException $ex) {
96
+				continue;
97
+			}
98 98
 
99
-            if ($old_view->majgd_map === null) {
100
-                $this->migrateGeoAnalysisTable($old_view, $tree, $geoview_data_service);
101
-            } else {
102
-                DB::connection()->beginTransaction();
103
-                if ($this->migrateGeoAnalysisMap($old_view, $tree, $geoview_data_service)) {
104
-                    DB::connection()->commit();
105
-                } else {
106
-                    DB::connection()->rollBack();
107
-                }
108
-            }
109
-        }
99
+			if ($old_view->majgd_map === null) {
100
+				$this->migrateGeoAnalysisTable($old_view, $tree, $geoview_data_service);
101
+			} else {
102
+				DB::connection()->beginTransaction();
103
+				if ($this->migrateGeoAnalysisMap($old_view, $tree, $geoview_data_service)) {
104
+					DB::connection()->commit();
105
+				} else {
106
+					DB::connection()->rollBack();
107
+				}
108
+			}
109
+		}
110 110
 
111
-        $in_transaction = DB::connection()->getPdo()->inTransaction();
111
+		$in_transaction = DB::connection()->getPdo()->inTransaction();
112 112
 
113
-        DB::schema()->drop('maj_geodispersion');
113
+		DB::schema()->drop('maj_geodispersion');
114 114
 
115
-        if ($in_transaction && !DB::connection()->getPdo()->inTransaction()) {
116
-            DB::connection()->beginTransaction();
117
-        }
115
+		if ($in_transaction && !DB::connection()->getPdo()->inTransaction()) {
116
+			DB::connection()->beginTransaction();
117
+		}
118 118
 
119
-        FlashMessages::addMessage(I18N::translate(
120
-            'The geographical dispersion analyses have been migrated for webtrees 2. Please review their settings.'
121
-        ));
122
-    }
119
+		FlashMessages::addMessage(I18N::translate(
120
+			'The geographical dispersion analyses have been migrated for webtrees 2. Please review their settings.'
121
+		));
122
+	}
123 123
 
124
-    /**
125
-     * Create a Table geographical analysis view from a migrated item.
126
-     *
127
-     * @param stdClass $old_view
128
-     * @param Tree $tree
129
-     * @param GeoAnalysisViewDataService $geoview_data_service
130
-     * @return bool
131
-     */
132
-    private function migrateGeoAnalysisTable(
133
-        stdClass $old_view,
134
-        Tree $tree,
135
-        GeoAnalysisViewDataService $geoview_data_service
136
-    ): bool {
137
-        $new_view = new GeoAnalysisTable(
138
-            0,
139
-            $tree,
140
-            $old_view->majgd_status === 'enabled',
141
-            $old_view->majgd_descr,
142
-            app(SosaByGenerationGeoAnalysis::class),
143
-            (int) $old_view->majgd_sublevel,
144
-            (int) $old_view->majgd_detailsgen
145
-        );
124
+	/**
125
+	 * Create a Table geographical analysis view from a migrated item.
126
+	 *
127
+	 * @param stdClass $old_view
128
+	 * @param Tree $tree
129
+	 * @param GeoAnalysisViewDataService $geoview_data_service
130
+	 * @return bool
131
+	 */
132
+	private function migrateGeoAnalysisTable(
133
+		stdClass $old_view,
134
+		Tree $tree,
135
+		GeoAnalysisViewDataService $geoview_data_service
136
+	): bool {
137
+		$new_view = new GeoAnalysisTable(
138
+			0,
139
+			$tree,
140
+			$old_view->majgd_status === 'enabled',
141
+			$old_view->majgd_descr,
142
+			app(SosaByGenerationGeoAnalysis::class),
143
+			(int) $old_view->majgd_sublevel,
144
+			(int) $old_view->majgd_detailsgen
145
+		);
146 146
 
147
-        return $geoview_data_service->insertGetId($new_view) > 0;
148
-    }
147
+		return $geoview_data_service->insertGetId($new_view) > 0;
148
+	}
149 149
 
150
-    /**
151
-     * Create a Map geographical analysis view from a migrated item.
152
-     *
153
-     * @param stdClass $old_view
154
-     * @param Tree $tree
155
-     * @param GeoAnalysisViewDataService $geoview_data_service
156
-     * @return bool
157
-     */
158
-    private function migrateGeoAnalysisMap(
159
-        stdClass $old_view,
160
-        Tree $tree,
161
-        GeoAnalysisViewDataService $geoview_data_service
162
-    ): bool {
163
-        /** @var MapDefinitionsService $map_definition_service */
164
-        $map_definition_service = app(MapDefinitionsService::class);
165
-        /** @var MapAdapterDataService $mapadapter_data_service */
166
-        $mapadapter_data_service = app(MapAdapterDataService::class);
150
+	/**
151
+	 * Create a Map geographical analysis view from a migrated item.
152
+	 *
153
+	 * @param stdClass $old_view
154
+	 * @param Tree $tree
155
+	 * @param GeoAnalysisViewDataService $geoview_data_service
156
+	 * @return bool
157
+	 */
158
+	private function migrateGeoAnalysisMap(
159
+		stdClass $old_view,
160
+		Tree $tree,
161
+		GeoAnalysisViewDataService $geoview_data_service
162
+	): bool {
163
+		/** @var MapDefinitionsService $map_definition_service */
164
+		$map_definition_service = app(MapDefinitionsService::class);
165
+		/** @var MapAdapterDataService $mapadapter_data_service */
166
+		$mapadapter_data_service = app(MapAdapterDataService::class);
167 167
 
168
-        $new_view = new GeoAnalysisMap(
169
-            0,
170
-            $tree,
171
-            $old_view->majgd_status === 'enabled',
172
-            $old_view->majgd_descr,
173
-            app(SosaByGenerationGeoAnalysis::class),
174
-            (int) $old_view->majgd_sublevel,
175
-            (int) $old_view->majgd_detailsgen
176
-        );
168
+		$new_view = new GeoAnalysisMap(
169
+			0,
170
+			$tree,
171
+			$old_view->majgd_status === 'enabled',
172
+			$old_view->majgd_descr,
173
+			app(SosaByGenerationGeoAnalysis::class),
174
+			(int) $old_view->majgd_sublevel,
175
+			(int) $old_view->majgd_detailsgen
176
+		);
177 177
 
178
-        $view_id = $geoview_data_service->insertGetId($new_view);
179
-        if ($view_id === 0) {
180
-            return false;
181
-        }
182
-        $new_view = $new_view->withId($view_id);
178
+		$view_id = $geoview_data_service->insertGetId($new_view);
179
+		if ($view_id === 0) {
180
+			return false;
181
+		}
182
+		$new_view = $new_view->withId($view_id);
183 183
 
184
-        $colors = $new_view->colors();
185
-        foreach ($this->mapIdsFromOld($old_view->majgd_map) as $new_map_id) {
186
-            $map = $map_definition_service->find($new_map_id);
187
-            if ($map === null) {
188
-                return false;
189
-            }
190
-            $colors = $this->colorsFromMap($new_map_id);
184
+		$colors = $new_view->colors();
185
+		foreach ($this->mapIdsFromOld($old_view->majgd_map) as $new_map_id) {
186
+			$map = $map_definition_service->find($new_map_id);
187
+			if ($map === null) {
188
+				return false;
189
+			}
190
+			$colors = $this->colorsFromMap($new_map_id);
191 191
 
192
-            /** @var SimplePlaceMapper $mapper */
193
-            $mapper = app(SimplePlaceMapper::class);
194
-            $mapview_config = new MapViewConfig($this->mappingPropertyForMap($new_map_id), $mapper->config());
195
-            $map_adapter = new GeoAnalysisMapAdapter(0, $view_id, $map, $mapper, $mapview_config);
192
+			/** @var SimplePlaceMapper $mapper */
193
+			$mapper = app(SimplePlaceMapper::class);
194
+			$mapview_config = new MapViewConfig($this->mappingPropertyForMap($new_map_id), $mapper->config());
195
+			$map_adapter = new GeoAnalysisMapAdapter(0, $view_id, $map, $mapper, $mapview_config);
196 196
 
197
-            $mapadapter_data_service->insertGetId($map_adapter);
198
-        }
197
+			$mapadapter_data_service->insertGetId($map_adapter);
198
+		}
199 199
 
200
-        return $geoview_data_service->update($new_view->withColors($colors)) > 0;
201
-    }
200
+		return $geoview_data_service->update($new_view->withColors($colors)) > 0;
201
+	}
202 202
 
203
-    /**
204
-     * Get all new map definitions IDs representing an old map definition
205
-     *
206
-     * @param string $map_xml
207
-     * @return string[]
208
-     */
209
-    private function mapIdsFromOld(string $map_xml): array
210
-    {
211
-        $mapping = self::MAPS_XML_MAPPING[$map_xml] ?? [];
212
-        return is_array($mapping) ? $mapping : [ $mapping ];
213
-    }
203
+	/**
204
+	 * Get all new map definitions IDs representing an old map definition
205
+	 *
206
+	 * @param string $map_xml
207
+	 * @return string[]
208
+	 */
209
+	private function mapIdsFromOld(string $map_xml): array
210
+	{
211
+		$mapping = self::MAPS_XML_MAPPING[$map_xml] ?? [];
212
+		return is_array($mapping) ? $mapping : [ $mapping ];
213
+	}
214 214
 
215
-    /**
216
-     * Get the mapping property to be used for the migrated map adapter
217
-     *
218
-     * @param string $map_id
219
-     * @return string
220
-     */
221
-    private function mappingPropertyForMap(string $map_id): string
222
-    {
223
-        switch ($map_id) {
224
-            case 'fr-metropole-regions-1970':
225
-            case 'fr-metropole-regions-2016':
226
-                return 'region_insee_libelle';
227
-            case 'fr-metropole-departements':
228
-                return 'dpt_insee_libelle';
229
-            case 'fr-dpt-48-codespostaux':
230
-                return 'code_postal';
231
-            default:
232
-                return 'commune_insee_libelle';
233
-        }
234
-    }
215
+	/**
216
+	 * Get the mapping property to be used for the migrated map adapter
217
+	 *
218
+	 * @param string $map_id
219
+	 * @return string
220
+	 */
221
+	private function mappingPropertyForMap(string $map_id): string
222
+	{
223
+		switch ($map_id) {
224
+			case 'fr-metropole-regions-1970':
225
+			case 'fr-metropole-regions-2016':
226
+				return 'region_insee_libelle';
227
+			case 'fr-metropole-departements':
228
+				return 'dpt_insee_libelle';
229
+			case 'fr-dpt-48-codespostaux':
230
+				return 'code_postal';
231
+			default:
232
+				return 'commune_insee_libelle';
233
+		}
234
+	}
235 235
 
236
-    /**
237
-     * Get the color configuration to be used for the migrated map view
238
-     *
239
-     * @param string $map_id
240
-     * @return MapColorsConfig
241
-     */
242
-    private function colorsFromMap(string $map_id): MapColorsConfig
243
-    {
244
-        $default = Hex::fromString('#f5f5f5');
245
-        $stroke = Hex::fromString('#d5d5d5');
246
-        $hover = Hex::fromString('#ff6600');
236
+	/**
237
+	 * Get the color configuration to be used for the migrated map view
238
+	 *
239
+	 * @param string $map_id
240
+	 * @return MapColorsConfig
241
+	 */
242
+	private function colorsFromMap(string $map_id): MapColorsConfig
243
+	{
244
+		$default = Hex::fromString('#f5f5f5');
245
+		$stroke = Hex::fromString('#d5d5d5');
246
+		$hover = Hex::fromString('#ff6600');
247 247
 
248
-        switch ($map_id) {
249
-            case 'fr-metropole-departements':
250
-                return new MapColorsConfig($default, $stroke, Hex::fromString('#0493ab'), $hover);
251
-            case 'fr-dpt-48-codespostaux':
252
-                return new MapColorsConfig($default, $stroke, Hex::fromString('#44aa00'), $hover);
253
-            default:
254
-                return new MapColorsConfig($default, $stroke, Hex::fromString('#e2a61d'), $hover);
255
-        }
256
-    }
248
+		switch ($map_id) {
249
+			case 'fr-metropole-departements':
250
+				return new MapColorsConfig($default, $stroke, Hex::fromString('#0493ab'), $hover);
251
+			case 'fr-dpt-48-codespostaux':
252
+				return new MapColorsConfig($default, $stroke, Hex::fromString('#44aa00'), $hover);
253
+			default:
254
+				return new MapColorsConfig($default, $stroke, Hex::fromString('#e2a61d'), $hover);
255
+		}
256
+	}
257 257
 }
Please login to merge, or discard this patch.
app/Module/GeoDispersion/Schema/Migration1.php 1 patch
Indentation   +37 added lines, -37 removed lines patch added patch discarded remove patch
@@ -23,41 +23,41 @@
 block discarded – undo
23 23
  */
24 24
 class Migration1 implements MigrationInterface
25 25
 {
26
-    /**
27
-     * {@inheritDoc}
28
-     * @see \Fisharebest\Webtrees\Schema\MigrationInterface::upgrade()
29
-     */
30
-    public function upgrade(): void
31
-    {
32
-        $in_transaction = DB::connection()->getPdo()->inTransaction();
33
-
34
-        DB::schema()->create('maj_geodisp_views', static function (Blueprint $table): void {
35
-            $table->integer('majgv_id')->autoIncrement();
36
-            $table->integer('majgv_gedcom_id')->index();
37
-            $table->string('majgv_view_class', 255);
38
-            $table->enum('majgv_status', ['enabled', 'disabled'])->default('enabled');
39
-            $table->string('majgv_descr', 248);
40
-            $table->string('majgv_analysis', 255);
41
-            $table->tinyInteger('majgv_place_depth')->default(1);
42
-            $table->tinyInteger('majgv_top_places')->default(0);
43
-            $table->json('majgv_colors')->nullable();
44
-
45
-            $table->foreign('majgv_gedcom_id')->references('gedcom_id')->on('gedcom')->onDelete('cascade');
46
-        });
47
-
48
-        DB::schema()->create('maj_geodisp_mapviews', static function (Blueprint $table): void {
49
-            $table->integer('majgm_id')->autoIncrement();
50
-            $table->integer('majgm_majgv_id')->index();
51
-            $table->string('majgm_map_id', 127);
52
-            $table->string('majgm_mapper', 255);
53
-            $table->string('majgm_feature_prop', 31);
54
-            $table->json('majgm_config')->nullable();
55
-
56
-            $table->foreign('majgm_majgv_id')->references('majgv_id')->on('maj_geodisp_views')->onDelete('cascade');
57
-        });
58
-
59
-        if ($in_transaction && !DB::connection()->getPdo()->inTransaction()) {
60
-            DB::connection()->beginTransaction();
61
-        }
62
-    }
26
+	/**
27
+	 * {@inheritDoc}
28
+	 * @see \Fisharebest\Webtrees\Schema\MigrationInterface::upgrade()
29
+	 */
30
+	public function upgrade(): void
31
+	{
32
+		$in_transaction = DB::connection()->getPdo()->inTransaction();
33
+
34
+		DB::schema()->create('maj_geodisp_views', static function (Blueprint $table): void {
35
+			$table->integer('majgv_id')->autoIncrement();
36
+			$table->integer('majgv_gedcom_id')->index();
37
+			$table->string('majgv_view_class', 255);
38
+			$table->enum('majgv_status', ['enabled', 'disabled'])->default('enabled');
39
+			$table->string('majgv_descr', 248);
40
+			$table->string('majgv_analysis', 255);
41
+			$table->tinyInteger('majgv_place_depth')->default(1);
42
+			$table->tinyInteger('majgv_top_places')->default(0);
43
+			$table->json('majgv_colors')->nullable();
44
+
45
+			$table->foreign('majgv_gedcom_id')->references('gedcom_id')->on('gedcom')->onDelete('cascade');
46
+		});
47
+
48
+		DB::schema()->create('maj_geodisp_mapviews', static function (Blueprint $table): void {
49
+			$table->integer('majgm_id')->autoIncrement();
50
+			$table->integer('majgm_majgv_id')->index();
51
+			$table->string('majgm_map_id', 127);
52
+			$table->string('majgm_mapper', 255);
53
+			$table->string('majgm_feature_prop', 31);
54
+			$table->json('majgm_config')->nullable();
55
+
56
+			$table->foreign('majgm_majgv_id')->references('majgv_id')->on('maj_geodisp_views')->onDelete('cascade');
57
+		});
58
+
59
+		if ($in_transaction && !DB::connection()->getPdo()->inTransaction()) {
60
+			DB::connection()->beginTransaction();
61
+		}
62
+	}
63 63
 }
Please login to merge, or discard this patch.
app/Module/GeoDispersion/Http/RequestHandlers/GeoAnalysisViewEditAction.php 1 patch
Indentation   +82 added lines, -82 removed lines patch added patch discarded remove patch
@@ -34,86 +34,86 @@
 block discarded – undo
34 34
  */
35 35
 class GeoAnalysisViewEditAction implements RequestHandlerInterface
36 36
 {
37
-    private ?GeoDispersionModule $module;
38
-    private GeoAnalysisViewDataService $geoview_data_service;
39
-
40
-    /**
41
-     * Constructor for GeoAnalysisViewEditAction Request Handler
42
-     *
43
-     * @param ModuleService $module_service
44
-     * @param GeoAnalysisViewDataService $geoview_data_service
45
-     */
46
-    public function __construct(ModuleService $module_service, GeoAnalysisViewDataService $geoview_data_service)
47
-    {
48
-        $this->module = $module_service->findByInterface(GeoDispersionModule::class)->first();
49
-        $this->geoview_data_service = $geoview_data_service;
50
-    }
51
-
52
-    /**
53
-     * {@inheritDoc}
54
-     * @see \Psr\Http\Server\RequestHandlerInterface::handle()
55
-     */
56
-    public function handle(ServerRequestInterface $request): ResponseInterface
57
-    {
58
-        $tree = Validator::attributes($request)->tree();
59
-
60
-        $admin_config_route = route(AdminConfigPage::class, ['tree' => $tree->name()]);
61
-
62
-        if ($this->module === null) {
63
-            FlashMessages::addMessage(
64
-                I18N::translate('The attached module could not be found.'),
65
-                'danger'
66
-            );
67
-            return redirect($admin_config_route);
68
-        }
69
-
70
-
71
-        $view_id = Validator::attributes($request)->integer('view_id', -1);
72
-        $view = $this->geoview_data_service->find($tree, $view_id, true);
73
-
74
-        $description    = Validator::parsedBody($request)->string('view_description', '');
75
-        $place_depth    = Validator::parsedBody($request)->integer('view_depth', 1);
76
-        $top_places     = Validator::parsedBody($request)->integer('view_top_places', 0);
77
-
78
-        $analysis = null;
79
-        try {
80
-            $analysis = app(Validator::parsedBody($request)->string('view_analysis', ''));
81
-        } catch (BindingResolutionException $ex) {
82
-        }
83
-
84
-        if (
85
-            $view === null
86
-            || $analysis === null || !($analysis instanceof GeoAnalysisInterface)
87
-            || $place_depth <= 0 && $top_places < 0
88
-        ) {
89
-            FlashMessages::addMessage(
90
-                I18N::translate('The parameters for view with ID “%s” are not valid.', I18N::number($view_id)),
91
-                'danger'
92
-            );
93
-            return redirect($admin_config_route);
94
-        }
95
-
96
-        $new_view = $view
97
-            ->with($view->isEnabled(), $description, $analysis, $place_depth, $top_places)
98
-            ->withGlobalSettingsUpdate($request);
99
-
100
-        try {
101
-            $this->geoview_data_service->update($new_view);
102
-            FlashMessages::addMessage(
103
-                I18N::translate('The geographical dispersion analysis view has been successfully updated.'),
104
-                'success'
105
-            );
106
-            //phpcs:ignore Generic.Files.LineLength.TooLong
107
-            Log::addConfigurationLog('Module ' . $this->module->title() . ' : View “' . $view->id() . '” has been updated.');
108
-        } catch (Throwable $ex) {
109
-            FlashMessages::addMessage(
110
-                I18N::translate('An error occured while updating the geographical dispersion analysis view.'),
111
-                'danger'
112
-            );
113
-            //phpcs:ignore Generic.Files.LineLength.TooLong
114
-            Log::addErrorLog('Module ' . $this->module->title() . ' : Error when updating view “' . $view->id() . '”: ' . $ex->getMessage());
115
-        }
116
-
117
-        return redirect($admin_config_route);
118
-    }
37
+	private ?GeoDispersionModule $module;
38
+	private GeoAnalysisViewDataService $geoview_data_service;
39
+
40
+	/**
41
+	 * Constructor for GeoAnalysisViewEditAction Request Handler
42
+	 *
43
+	 * @param ModuleService $module_service
44
+	 * @param GeoAnalysisViewDataService $geoview_data_service
45
+	 */
46
+	public function __construct(ModuleService $module_service, GeoAnalysisViewDataService $geoview_data_service)
47
+	{
48
+		$this->module = $module_service->findByInterface(GeoDispersionModule::class)->first();
49
+		$this->geoview_data_service = $geoview_data_service;
50
+	}
51
+
52
+	/**
53
+	 * {@inheritDoc}
54
+	 * @see \Psr\Http\Server\RequestHandlerInterface::handle()
55
+	 */
56
+	public function handle(ServerRequestInterface $request): ResponseInterface
57
+	{
58
+		$tree = Validator::attributes($request)->tree();
59
+
60
+		$admin_config_route = route(AdminConfigPage::class, ['tree' => $tree->name()]);
61
+
62
+		if ($this->module === null) {
63
+			FlashMessages::addMessage(
64
+				I18N::translate('The attached module could not be found.'),
65
+				'danger'
66
+			);
67
+			return redirect($admin_config_route);
68
+		}
69
+
70
+
71
+		$view_id = Validator::attributes($request)->integer('view_id', -1);
72
+		$view = $this->geoview_data_service->find($tree, $view_id, true);
73
+
74
+		$description    = Validator::parsedBody($request)->string('view_description', '');
75
+		$place_depth    = Validator::parsedBody($request)->integer('view_depth', 1);
76
+		$top_places     = Validator::parsedBody($request)->integer('view_top_places', 0);
77
+
78
+		$analysis = null;
79
+		try {
80
+			$analysis = app(Validator::parsedBody($request)->string('view_analysis', ''));
81
+		} catch (BindingResolutionException $ex) {
82
+		}
83
+
84
+		if (
85
+			$view === null
86
+			|| $analysis === null || !($analysis instanceof GeoAnalysisInterface)
87
+			|| $place_depth <= 0 && $top_places < 0
88
+		) {
89
+			FlashMessages::addMessage(
90
+				I18N::translate('The parameters for view with ID “%s” are not valid.', I18N::number($view_id)),
91
+				'danger'
92
+			);
93
+			return redirect($admin_config_route);
94
+		}
95
+
96
+		$new_view = $view
97
+			->with($view->isEnabled(), $description, $analysis, $place_depth, $top_places)
98
+			->withGlobalSettingsUpdate($request);
99
+
100
+		try {
101
+			$this->geoview_data_service->update($new_view);
102
+			FlashMessages::addMessage(
103
+				I18N::translate('The geographical dispersion analysis view has been successfully updated.'),
104
+				'success'
105
+			);
106
+			//phpcs:ignore Generic.Files.LineLength.TooLong
107
+			Log::addConfigurationLog('Module ' . $this->module->title() . ' : View “' . $view->id() . '” has been updated.');
108
+		} catch (Throwable $ex) {
109
+			FlashMessages::addMessage(
110
+				I18N::translate('An error occured while updating the geographical dispersion analysis view.'),
111
+				'danger'
112
+			);
113
+			//phpcs:ignore Generic.Files.LineLength.TooLong
114
+			Log::addErrorLog('Module ' . $this->module->title() . ' : Error when updating view “' . $view->id() . '”: ' . $ex->getMessage());
115
+		}
116
+
117
+		return redirect($admin_config_route);
118
+	}
119 119
 }
Please login to merge, or discard this patch.
app/Module/GeoDispersion/Http/RequestHandlers/GeoAnalysisViewAddPage.php 1 patch
Indentation   +39 added lines, -39 removed lines patch added patch discarded remove patch
@@ -32,48 +32,48 @@
 block discarded – undo
32 32
  */
33 33
 class GeoAnalysisViewAddPage implements RequestHandlerInterface
34 34
 {
35
-    use ViewResponseTrait;
35
+	use ViewResponseTrait;
36 36
 
37
-    private ?GeoDispersionModule $module;
38
-    private GeoAnalysisService $geoanalysis_service;
39
-    private GeoAnalysisDataService $geoanalysis_data_service;
37
+	private ?GeoDispersionModule $module;
38
+	private GeoAnalysisService $geoanalysis_service;
39
+	private GeoAnalysisDataService $geoanalysis_data_service;
40 40
 
41
-    /**
42
-     * Constructor for GeoAnalysisViewAddPage Request Handler
43
-     *
44
-     * @param ModuleService $module_service
45
-     * @param GeoAnalysisService $geoanalysis_service
46
-     * @param GeoAnalysisDataService $geoanalysis_data_service
47
-     */
48
-    public function __construct(
49
-        ModuleService $module_service,
50
-        GeoAnalysisService $geoanalysis_service,
51
-        GeoAnalysisDataService $geoanalysis_data_service
52
-    ) {
53
-        $this->module = $module_service->findByInterface(GeoDispersionModule::class)->first();
54
-        $this->geoanalysis_service = $geoanalysis_service;
55
-        $this->geoanalysis_data_service = $geoanalysis_data_service;
56
-    }
41
+	/**
42
+	 * Constructor for GeoAnalysisViewAddPage Request Handler
43
+	 *
44
+	 * @param ModuleService $module_service
45
+	 * @param GeoAnalysisService $geoanalysis_service
46
+	 * @param GeoAnalysisDataService $geoanalysis_data_service
47
+	 */
48
+	public function __construct(
49
+		ModuleService $module_service,
50
+		GeoAnalysisService $geoanalysis_service,
51
+		GeoAnalysisDataService $geoanalysis_data_service
52
+	) {
53
+		$this->module = $module_service->findByInterface(GeoDispersionModule::class)->first();
54
+		$this->geoanalysis_service = $geoanalysis_service;
55
+		$this->geoanalysis_data_service = $geoanalysis_data_service;
56
+	}
57 57
 
58
-    /**
59
-     * {@inheritDoc}
60
-     * @see \Psr\Http\Server\RequestHandlerInterface::handle()
61
-     */
62
-    public function handle(ServerRequestInterface $request): ResponseInterface
63
-    {
64
-        $this->layout = 'layouts/administration';
58
+	/**
59
+	 * {@inheritDoc}
60
+	 * @see \Psr\Http\Server\RequestHandlerInterface::handle()
61
+	 */
62
+	public function handle(ServerRequestInterface $request): ResponseInterface
63
+	{
64
+		$this->layout = 'layouts/administration';
65 65
 
66
-        if ($this->module === null) {
67
-            throw new HttpNotFoundException(I18N::translate('The attached module could not be found.'));
68
-        }
69
-        $tree = Validator::attributes($request)->tree();
66
+		if ($this->module === null) {
67
+			throw new HttpNotFoundException(I18N::translate('The attached module could not be found.'));
68
+		}
69
+		$tree = Validator::attributes($request)->tree();
70 70
 
71
-        return $this->viewResponse($this->module->name() . '::admin/view-add', [
72
-            'module'        =>  $this->module,
73
-            'title'         =>  I18N::translate('Add a geographical dispersion analysis view'),
74
-            'tree'          =>  $tree,
75
-            'geoanalysis_list'  =>  $this->geoanalysis_service->all(),
76
-            'place_example'     =>  $this->geoanalysis_data_service->placeHierarchyExample($tree)
77
-        ]);
78
-    }
71
+		return $this->viewResponse($this->module->name() . '::admin/view-add', [
72
+			'module'        =>  $this->module,
73
+			'title'         =>  I18N::translate('Add a geographical dispersion analysis view'),
74
+			'tree'          =>  $tree,
75
+			'geoanalysis_list'  =>  $this->geoanalysis_service->all(),
76
+			'place_example'     =>  $this->geoanalysis_data_service->placeHierarchyExample($tree)
77
+		]);
78
+	}
79 79
 }
Please login to merge, or discard this patch.