{inapp, email, userMayOverride} * @property null|int $digestHour * @property null|int $retentionDays * @property null|string $defaultFrequency */ final class UpdateNotificationSettingsRequest extends Request { public function authorize() { return WpCapabilities::check(Capabilities::MANAGE->value); } public function failedAuthorizationMessage(): string { if (!is_user_logged_in()) { return 'You must be logged in to change notification settings.'; } return 'You do not have permission to change notification settings.'; } public function rules() { // No sender or wording fields: this endpoint does not accept them, // because this plugin does not store them. // // digestHour takes rule objects because hour 0 is midnight, and // wp-validator's own min/max reject any zero. return [ 'enabled' => ['nullable', 'boolean'], 'types' => ['nullable', 'array'], 'digestHour' => ['nullable', 'integer', new AtLeastRule(0), new AtMostRule(23)], 'retentionDays' => ['nullable', 'integer', 'min:7', 'max:3650'], 'defaultFrequency' => [ 'nullable', 'string', new InRule(NotificationSettings::frequencies()), ], ]; } public function messages() { return [ 'digestHour.min' => __('Pick an hour between 0 and 23.', 'bit-connect'), 'digestHour.max' => __('Pick an hour between 0 and 23.', 'bit-connect'), 'defaultFrequency.in' => __('That is not a summary email frequency this forum offers.', 'bit-connect'), ]; } /** * The stored shape, built only from values this build recognises. * * Written here rather than in the controller so the option can never hold a * type that no longer exists or an hour that is not on the clock — the * clamps live in NotificationSettings, and every read already goes through * them, but storing rubbish and normalising it forever afterwards is how an * option becomes impossible to reason about. * * @return array */ public function toSettingsData(): array { $validated = $this->validated(); $submitted = \is_array($validated['types'] ?? null) ? $validated['types'] : []; $types = []; // Driven by cases(), not by the payload: a type the screen omitted keeps // its stored value, and one it invented is dropped. foreach (NotificationTypes::cases() as $type) { $row = \is_array($submitted[$type->value] ?? null) ? $submitted[$type->value] : null; if ($row === null) { continue; } $types[$type->value] = [ 'inapp' => filter_var($row['inapp'] ?? true, FILTER_VALIDATE_BOOLEAN), 'email' => filter_var($row['email'] ?? false, FILTER_VALIDATE_BOOLEAN), 'userMayOverride' => filter_var($row['userMayOverride'] ?? true, FILTER_VALIDATE_BOOLEAN), ]; } $frequency = \is_string($validated['defaultFrequency'] ?? null) ? $validated['defaultFrequency'] : NotificationSettings::FREQUENCY_INSTANT; // Sender identity and email wording are not written here: this plugin // has no setting for either. A plugin that sends as something other // than the site, or rewords the lines around the list, supplies them // through the `bit_connect_mail_from_name`, `bit_connect_mail_from_email` // and `bit_connect_mail_template` filters. Nothing to carry forward, // and nothing this endpoint can overwrite. return [ 'enabled' => filter_var($validated['enabled'] ?? true, FILTER_VALIDATE_BOOLEAN), 'types' => $types, // Clamped on the way in as well as on the way out. The cron reads // these unsupervised, and an hour of 25 is a digest that never goes. 'digestHour' => NotificationSettings::digestHour($validated), 'retentionDays' => NotificationSettings::retentionDays($validated), 'defaultFrequency' => NotificationSettings::isValidFrequency($frequency) ? $frequency : NotificationSettings::FREQUENCY_INSTANT, ]; } }