settings = $settings; $this->remote = $remote; } public function backup_finished(array $job, ?array $entry): void { $this->send_email($job, $entry); $this->send_webhook($job, $entry); do_action('swpb_backup_finished', $job, $entry); } private function send_email(array $job, ?array $entry): void { $when = (string)$this->settings->get('notify_on'); $failed = $job['status'] === 'failed'; $has_warnings = !empty($job['warnings']); if ($when === 'never' || ($when === 'failure' && !$failed && !$has_warnings)) { return; } $site = wp_specialchars_decode(get_bloginfo('name'), ENT_QUOTES); if ($failed) { /* translators: %s: site name */ $subject = sprintf(__('[%s] Backup FAILED', 'seljebackup'), $site); } elseif ($has_warnings) { /* translators: %s: site name */ $subject = sprintf(__('[%s] Backup finished with warnings', 'seljebackup'), $site); } else { /* translators: %s: site name */ $subject = sprintf(__('[%s] Backup completed', 'seljebackup'), $site); } $lines = []; /* translators: %s: site address */ $lines[] = sprintf(__('Site: %s', 'seljebackup'), home_url()); /* translators: %s: backup type */ $lines[] = sprintf(__('Backup type: %s', 'seljebackup'), self::type_label((string)$job['type'])); /* translators: %s: date and time */ $lines[] = sprintf(__('Started: %s', 'seljebackup'), wp_date('Y-m-d H:i', (int)$job['started'])); if ($failed) { $lines[] = ''; $lines[] = __('What went wrong:', 'seljebackup'); $lines[] = (string)$job['error']; } if ($entry) { $lines[] = ''; $lines[] = __('Files:', 'seljebackup'); foreach ((array)$entry['files'] as $file) { $lines[] = ' - ' . $file['name'] . ' (' . size_format((int)$file['size']) . ')'; } foreach ((array)($entry['remote'] ?? []) as $remote) { $status = !empty($remote['error']) ? __('failed', 'seljebackup') : __('uploaded', 'seljebackup'); $lines[] = sprintf(' - %s: %s', (string)($remote['name'] ?? ''), $status); } } if ($has_warnings) { $lines[] = ''; $lines[] = __('Warnings:', 'seljebackup'); foreach ((array)$job['warnings'] as $warning) { $lines[] = ' - ' . $warning; } } $lines[] = ''; $lines[] = admin_url('admin.php?page=' . SWPB_ADMIN_SLUG); $lines[] = ''; $lines[] = '— seljeBackup'; wp_mail($this->settings->notify_recipients(), $subject, implode("\n", $lines)); } private function send_webhook(array $job, ?array $entry): void { $hook = $this->remote->webhook(); if ($hook['url'] === '') { return; } $payload = [ 'event' => $job['status'] === 'failed' ? 'backup.failed' : 'backup.completed', 'site' => home_url(), 'site_name' => get_bloginfo('name'), 'backup_id' => $job['id'], 'type' => $job['type'], 'trigger' => $job['trigger'], 'status' => $job['status'], 'started' => gmdate('c', (int)$job['started']), 'finished' => gmdate('c', (int)$job['finished']), 'error' => (string)$job['error'], 'warnings' => array_values((array)$job['warnings']), 'files' => $entry ? array_values((array)$entry['files']) : [], 'destinations' => $entry ? array_values(array_map(static function ($r) { return ['name' => $r['name'] ?? '', 'type' => $r['type'] ?? '', 'ok' => empty($r['error'])]; }, (array)($entry['remote'] ?? []))) : [], ]; $body = (string)wp_json_encode($payload); wp_remote_post($hook['url'], [ 'timeout' => 10, 'blocking' => false, 'headers' => [ 'Content-Type' => 'application/json', 'User-Agent' => 'seljeBackup/' . SWPB_PLUGIN_VERSION, 'X-SWPB-Event' => $payload['event'], 'X-SWPB-Signature' => 'sha256=' . hash_hmac('sha256', $body, $hook['secret']), ], 'body' => $body, ]); } public static function type_label(string $type): string { switch ($type) { case 'database': return __('Database only', 'seljebackup'); case 'files': return __('Files only', 'seljebackup'); default: return __('Database + files', 'seljebackup'); } } }