PluginProbe
Double Opt-In for Contact Form 7 – Secure, GDPR-Compliant Email Verification / 5.5.0
Double Opt-In for Contact Form 7 – Secure, GDPR-Compliant Email Verification v5.5.0
5.5.0 5.4.0 5.3.2 5.3.1 5.1.6 5.1.5 trunk 2.1.5 2.11 2.12 2.13 2.15 3.0.0 3.0.1 3.0.2 3.0.3 3.0.5 3.0.51 3.0.60 3.0.61 3.0.62 3.0.70 3.0.71 3.0.72 3.1.0 All 34 releases
double-opt-in / core / telemetry.php

telemetry.php in Double Opt-In for Contact Form 7 – Secure, GDPR-Compliant Email Verification 5.5.0, at core/telemetry.php

212 lines 5.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace forge12\contactform7\CF7DoubleOptIn;
4
5 use Exception;
6 use Forge12\Shared\Logger;
7 use Forge12\Shared\LoggerInterface;
8
9 if (!class_exists(__NAMESPACE__ . '\\Telemetry')) {
10 class Telemetry {
11
12 /** @var string */
13 private $option_key = 'f12_cf7_doubleoptin_telemetry_counters';
14
15 /** @var \Forge12\Shared\LoggerInterface */
16 private $logger;
17
18 public function __construct(LoggerInterface $logger) {
19 $this->logger = $logger;
20 }
21
22 public function get_features(): array {
23 include_once(ABSPATH . 'wp-admin/includes/plugin.php');
24 return [
25 'double-opt-in-pro' => is_plugin_active('double-opt-in-pro/CF7DoubleOptIn.class.php'),
26 ];
27 }
28
29 /**
30 * Increment a counter by name
31 */
32 public function increment(string $counter): void {
33 $counters = get_option($this->option_key, []);
34
35 if (!is_array($counters)) {
36 $counters = maybe_unserialize($counters);
37 }
38 if (empty($counters) || !is_array($counters)) {
39 $counters = [];
40 }
41
42 if (!isset($counters[$counter])) {
43 $counters[$counter] = 0;
44 }
45
46 $counters[$counter]++;
47
48 update_option($this->option_key, $counters, false);
49
50 $this->logger->debug("Telemetry counter incremented", [
51 'plugin' => FORGE12_OPTIN_SLUG,
52 'counter' => $counter,
53 'value' => $counters[$counter],
54 ]);
55 }
56
57 /**
58 * Get all counters
59 */
60 public function get_counters(): array {
61 $counters = get_option($this->option_key, []);
62 return is_array($counters) ? $counters : [];
63 }
64
65 /**
66 * Reset counters (optional)
67 */
68 public function reset(): void {
69 update_option($this->option_key, [], false);
70 $this->logger->info("Telemetry counters reset", [
71 'plugin' => FORGE12_OPTIN_SLUG,
72 ]);
73 }
74
75 /**
76 * Static: build payload
77 */
78 public static function build_payload(): array {
79 $logger = Logger::getInstance();
80 $telemetry = new self($logger);
81
82 $logger->info("Telemetry payload creation started", [
83 'plugin' => FORGE12_OPTIN_SLUG,
84 ]);
85
86 try {
87 $counters = $telemetry->get_counters();
88
89 if (empty($counters)) {
90 $logger->info("Telemetry: Counters are empty, initializing as stdClass.", [
91 'plugin' => FORGE12_OPTIN_SLUG,
92 ]);
93 $counters = new \stdClass();
94 }
95
96 $payload = [
97 'installation_uuid' => f12_cf7_doubleoptin_get_installation_uuid(),
98 'plugin_slug' => FORGE12_OPTIN_SLUG,
99 'plugin_version' => FORGE12_OPTIN_VERSION,
100 'snapshot_date' => gmdate('Y-m-d'),
101 'settings' => get_option('f12-doi-settings', []),
102 'features' => $telemetry->get_features(),
103 'counters' => $counters,
104 'wp_version' => get_bloginfo('version'),
105 'php_version' => PHP_VERSION,
106 'locale' => get_locale(),
107 ];
108
109 $logger->info("Telemetry payload created successfully", [
110 'plugin' => FORGE12_OPTIN_SLUG,
111 'payload' => $payload,
112 ]);
113
114 return $payload;
115
116 } catch (Exception $e) {
117 $logger->error("Error creating telemetry payload", [
118 'plugin' => FORGE12_OPTIN_SLUG,
119 'message' => $e->getMessage(),
120 'trace' => $e->getTraceAsString(),
121 ]);
122
123 return [];
124 }
125 }
126
127 /**
128 * Whether the site owner has left telemetry switched on.
129 *
130 * Defaults to enabled, matching the setting's own default — but an
131 * absent option must not be read as consent to something the user
132 * turned off, so the value is taken from the same key the settings
133 * screen writes.
134 */
135 public static function is_enabled(): bool {
136 $settings = get_option('f12-doi-settings', []);
137
138 if (!is_array($settings) || !array_key_exists('telemetry', $settings)) {
139 return true;
140 }
141
142 return (int) $settings['telemetry'] === 1;
143 }
144
145 /**
146 * Static: send snapshot
147 *
148 * Currently unwired: no cron schedules this any more (see core/cron.php).
149 * The transport is kept — including the payload — so that switching it
150 * back on is a matter of scheduling the job again once a reachable
151 * endpoint exists, rather than rewriting it from memory.
152 */
153 public static function send_snapshot(): void {
154 $logger = Logger::getInstance();
155
156 // The consent check belongs here rather than at the call site: this
157 // method is public and was previously reachable from a cron job that
158 // never asked. Anything that calls it in future inherits the check.
159 if (!self::is_enabled()) {
160 $logger->debug("Telemetry skipped — disabled in settings", [
161 'plugin' => FORGE12_OPTIN_SLUG,
162 ]);
163
164 return;
165 }
166
167 $payload = self::build_payload();
168
169 $logger->debug("Telemetry Payload prepared", [
170 'plugin' => FORGE12_OPTIN_SLUG,
171 'payload' => $payload,
172 ]);
173
174 $response = wp_remote_post('https://silentshield.forge12.com/api/telemetry/snapshot', [
175 'headers' => [
176 'Content-Type' => 'application/json; charset=utf-8',
177 ],
178 'body' => wp_json_encode($payload),
179 'timeout' => 15,
180 ]);
181
182 if (is_wp_error($response)) {
183 $logger->error("Telemetry failed", [
184 'plugin' => FORGE12_OPTIN_SLUG,
185 'error' => $response->get_error_message(),
186 ]);
187 return;
188 }
189
190 $code = wp_remote_retrieve_response_code($response);
191
192 if ($code === 201) {
193 $logger->info("Telemetry sent successfully", [
194 'plugin' => FORGE12_OPTIN_SLUG,
195 'code' => $code,
196 ]);
197 } else {
198 $logger->warning("Telemetry unexpected response", [
199 'plugin' => FORGE12_OPTIN_SLUG,
200 'code' => $code,
201 'response' => wp_remote_retrieve_body($response),
202 ]);
203 }
204 }
205 }
206 }
207
208 // Kein Cron-Hook mehr: der Tagesjob ist abgeschafft (siehe core/cron.php, das
209 // bestehende Planungen auch wieder austrägt). Die Bindung bleibt bewusst weg,
210 // damit ein Event, das auf einer alten Installation noch im WP-Cron steht, bis
211 // zum Aufräumen ins Leere läuft statt an einen toten Endpoint zu posten.
212