logs = $logs ?: new DeliveryLogRepository(); $this->jobs = $jobs ?: new ReminderJobRepository(); } public function init(): void { add_action('admin_post_' . self::ACTION, [$this, 'handle']); } /** * @return array{ok: bool, message: string, results?: mixed[]} */ public function retry(int $log_id): array { $log = $this->logs->find($log_id); if (!$log) return ['ok' => false, 'message' => esc_html__('Notification log not found.', 'daytidy')]; if ((string) ($log->status ?? '') !== DeliveryLog::STATUS_FAILED) return ['ok' => false, 'message' => esc_html__('Only failed notification logs can be retried.', 'daytidy')]; if (!$this->canRetry($log)) return ['ok' => false, 'message' => esc_html__('Retry is not allowed for this notification log.', 'daytidy')]; $allowed = (bool) apply_filters( 'DayTidy/F__DayTidy/Notifications/Retry__can_retry/allowed', true, $log ); if (!$allowed) return ['ok' => false, 'message' => esc_html__('Retry is not allowed for this notification log.', 'daytidy')]; $notification_id = absint($log->notification_id ?? 0); $booking_id = absint($log->booking_id ?? 0); $notification_post = $notification_id ? get_post($notification_id) : null; $booking_post = $booking_id ? get_post($booking_id) : null; if (!$notification_post || $notification_post->post_type !== NotificationPost::PT) return ['ok' => false, 'message' => esc_html__('Notification template no longer exists.', 'daytidy')]; if (!$booking_post || $booking_post->post_type !== BookingPost::PT) return ['ok' => false, 'message' => esc_html__('Booking no longer exists.', 'daytidy')]; $notification = new Notification($notification_id); if ($notification_post->post_status !== 'publish' || !$notification->enabled() || $notification->type() !== Registry::TYPE_EMAIL || $notification->trigger() !== sanitize_key((string) ($log->trigger_key ?? '')) ) { return ['ok' => false, 'message' => esc_html__('Notification template is no longer eligible for retry.', 'daytidy')]; } $recipient = $this->recipient($log); if (empty($recipient['email']) || !is_email((string) $recipient['email'])) return ['ok' => false, 'message' => esc_html__('Notification log does not include a retryable recipient.', 'daytidy')]; do_action('DayTidy/A__DayTidy/Notifications/Retry__before', $log); $results = (new EmailSender())->send( $notification, new Booking($booking_id), sanitize_key((string) ($log->recipient_context ?? '')), [ 'recipient' => $recipient, 'suppress_extra_recipients' => true, 'source' => DeliveryLog::SOURCE_RETRY, 'retry_of_log_id' => absint($log->id ?? 0), ] ); if (!empty($results['sent']) && sanitize_key((string) ($log->trigger_key ?? '')) === Registry::TRIGGER_BOOKING_REMINDER_BEFORE_START) { $retry_log_ids = isset($results['log_ids']) && is_array($results['log_ids']) ? array_map('absint', $results['log_ids']) : []; $settled = $this->jobs->settleByIdentity( $notification_id, $booking_id, sanitize_key((string) ($log->recipient_context ?? '')), $retry_log_ids[0] ?? 0 ); if (!$settled) { $this->jobs->settleByDeliveryLogId(absint($log->id ?? 0), $retry_log_ids[0] ?? 0); } } do_action('DayTidy/A__DayTidy/Notifications/Retry__after', $log, $results); return [ 'ok' => !empty($results['sent']), 'message' => !empty($results['sent']) ? esc_html__('Notification retry sent.', 'daytidy') : esc_html__('Notification retry failed.', 'daytidy'), 'results' => $results, ]; } /** * @param object $log */ public function canRetry($log): bool { return sanitize_key((string) ($log->trigger_key ?? '')) !== Registry::TRIGGER_CUSTOMER_PORTAL_VERIFICATION_CODE; } public function handle(): void { if (!current_user_can(Capabilities::MANAGE_SETTINGS)) wp_die(esc_html__('You are not allowed to retry notifications.', 'daytidy')); $log_id = isset($_GET['log_id']) ? absint(wp_unslash($_GET['log_id'])) : 0; check_admin_referer(self::NONCE . '_' . $log_id); $result = $this->retry($log_id); $redirect = wp_get_referer() ?: admin_url('admin.php?page=daytidy-notification-center'); wp_safe_redirect(add_query_arg([ 'daytidy_notice' => $result['ok'] ? 'retry-sent' : 'retry-failed', ], $redirect)); exit; } /** * @param object $log * @return mixed[] */ private function recipient($log): array { $snapshot = json_decode((string) ($log->recipient_snapshot ?? ''), true); $recipient = is_array($snapshot) ? $snapshot : []; $recipient['email'] = sanitize_email((string) ($recipient['email'] ?? $log->recipient_email ?? '')); $recipient['name'] = sanitize_text_field((string) ($recipient['name'] ?? $log->recipient_name ?? '')); $recipient['context'] = sanitize_key((string) ($recipient['context'] ?? $log->recipient_context ?? '')); if (!isset($recipient['data']) || !is_array($recipient['data'])) $recipient['data'] = []; return $recipient; } }