PluginProbe
ThinkRank AI SEO – AI SEO Plugin for WordPress: Schema, XML Sitemaps, Meta Tags, Search Console & Local SEO / 2.0.2
ThinkRank AI SEO – AI SEO Plugin for WordPress: Schema, XML Sitemaps, Meta Tags, Search Console & Local SEO v2.0.2
2.7.0 2.6.0 2.5.0 2.4.0 2.3.0 2.2.0 2.1.1 2.1.0 2.0.2 2.0.1 2.0.0 1.32.0 1.31.0 1.30.0 1.29.0 1.28.0 1.27.0 1.26.0 1.25.0 trunk 1.0.0 1.0.1 1.0.2 1.1.0 1.10.0 All 48 releases
thinkrank / includes / seo / class-email-report-scheduler.php

class-email-report-scheduler.php in ThinkRank AI SEO – AI SEO Plugin for WordPress: Schema, XML Sitemaps, Meta Tags, Search Console & Local SEO 2.0.2, at includes/seo/class-email-report-scheduler.php

116 lines 3.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Email Report Scheduler
4 *
5 * Owns the cron lifecycle for Email Reporting:
6 * - registers the recurring `thinkrank_email_report_tick` event
7 * - on each tick, asks the config whether the site is due
8 * - delegates to Email_Report_Generator::generate_for_due()
9 *
10 * The cron event is cleared on plugin deactivation by ThinkRank\Core\Deactivator
11 * (see its clear_scheduled_hooks()).
12 *
13 * Cron cadence: hourly. Per-site frequency (7/15/30 days) is enforced
14 * by checking next_scheduled_at against now — not by scheduling N
15 * different cron events. One event for all sites, simple to reason about.
16 *
17 * @package ThinkRank
18 * @subpackage SEO
19 * @since 1.9.0
20 */
21
22 declare(strict_types=1);
23
24 namespace ThinkRank\SEO;
25
26 if (!defined('ABSPATH')) {
27 exit;
28 }
29
30 /**
31 * Email_Report_Scheduler
32 *
33 * @since 1.9.0
34 */
35 final class Email_Report_Scheduler {
36
37 public const CRON_HOOK = 'thinkrank_email_report_tick';
38
39 private Email_Report_Config $config;
40 private Email_Report_Generator $generator;
41
42 public function __construct(Email_Report_Config $config, Email_Report_Generator $generator) {
43 $this->config = $config;
44 $this->generator = $generator;
45 }
46
47 /**
48 * Register cron + handler. Called once at plugin boot.
49 */
50 public function register(): void {
51 add_action(self::CRON_HOOK, [$this, 'on_tick']);
52
53 if (!wp_next_scheduled(self::CRON_HOOK)) {
54 wp_schedule_event(time() + 60, 'hourly', self::CRON_HOOK);
55 }
56 }
57
58 /**
59 * Cron tick handler. Runs every hour.
60 *
61 * Decision tree:
62 * 1. Feature disabled → skip
63 * 2. No next_scheduled_at on file → set one and skip this tick
64 * (gives the user a fresh cycle starting from "now" rather than
65 * firing immediately on first enable)
66 * 3. next_scheduled_at is in the future → skip
67 * 4. Otherwise → generate.
68 */
69 public function on_tick(): void {
70 $config = $this->config->get();
71 if (empty($config['enabled'])) {
72 return;
73 }
74
75 if (empty($config['next_scheduled_at'])) {
76 $next = wp_date('Y-m-d H:i:s', strtotime('+' . max(1, (int) $config['frequency_days']) . ' days'));
77 $this->config->update_schedule(null, $next);
78 return;
79 }
80
81 $next_ts = strtotime((string) $config['next_scheduled_at']);
82 if ($next_ts && $next_ts > time()) {
83 return;
84 }
85
86 $this->generator->generate_for_due();
87 }
88
89 /**
90 * Convenience used by REST + admin UI to surface the next-send timestamp
91 * to the user. Defends against an empty or stale next_scheduled_at.
92 *
93 * next_scheduled_at is stored as a site-local `Y-m-d H:i:s` string with
94 * no zone information. Handing that to the browser meant `new Date()`
95 * read it in the *viewer's* timezone, so any admin whose browser zone
96 * differed from the site's saw the wrong time. Emit a real ISO 8601
97 * string carrying the site's UTC offset instead, which parses to one
98 * unambiguous instant everywhere.
99 */
100 public function next_run_iso(): ?string {
101 $config = $this->config->get();
102 if (empty($config['enabled']) || empty($config['next_scheduled_at'])) {
103 return null;
104 }
105
106 // Interpret the stored wall clock in the site timezone, then render
107 // that same instant with its offset attached.
108 $timestamp = (int) get_gmt_from_date((string) $config['next_scheduled_at'], 'U');
109 if ($timestamp <= 0) {
110 return null;
111 }
112
113 return wp_date('c', $timestamp);
114 }
115 }
116