PluginProbe
Double Opt-In for Contact Form 7 – Secure, GDPR-Compliant Email Verification / 5.7.0
Double Opt-In for Contact Form 7 – Secure, GDPR-Compliant Email Verification v5.7.0
5.8.0 5.8.1 5.7.0 5.6.2 5.6.3 5.6.1 5.6.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 All 41 releases
← All changes | src/Admin/AdminPageController.php +57 -1 5.1.6 → 5.7.0 View file →
@@ -250,9 +250,11 @@
250 250 'loadedAddons' => $this->getLoadedAddonIds(),
251 251 'registeredAddons' => $this->getRegisteredAddonIds(),
252 252 'version' => defined( 'FORGE12_OPTIN_VERSION' ) ? FORGE12_OPTIN_VERSION : '0.0.0',
253 253 'emailEditorUrl' => admin_url( 'admin.php?page=f12-doi-admin#/email-templates' ),
254 - 'upgradeUrl' => 'https://www.forge12.com',
254 + 'upgradeUrl' => $this->productUrl( 'admin-upgrade', 'https://www.forge12.com' ),
255 + 'supportUrl' => $this->supportUrl(),
256 + 'feedbackUrl' => $this->feedbackUrl(),
255 257 'adminUrl' => admin_url(),
256 258 // Nonce for the legacy admin-ajax `doi_export_consent`
257 259 // handler. The handler hard-requires `_wpnonce` in
258 260 // `$_REQUEST` keyed on action `doi_consent_export`; without
@@ -261,8 +263,11 @@
261 263 // 2026-05-13). REST routes use the separate `nonce` field
262 264 // above with action `wp_rest` — they live in different
263 265 // namespaces, so a single shared nonce won't work.
264 266 'consentExportNonce' => wp_create_nonce( 'doi_consent_export' ),
267 + // Setup wizard state for the dashboard card. Null on sites that
268 + // were installed before the wizard existed.
269 + 'setup' => $this->setupState(),
265 270 );
266 271
267 272 /**
268 273 * Filter the admin SPA configuration data.
@@ -276,8 +281,59 @@
276 281 */
277 282 $config = apply_filters( 'f12_doi_admin_localize_data', $config );
278 283
279 284 wp_localize_script( 'doi-admin-ui', 'doiAdmin', $config );
285 + }
286 +
287 + /**
288 + * Product-site link for the SPA.
289 + *
290 + * The URL builders live in core/feedback.php, a plain-function file in the
291 + * legacy namespace rather than an autoloaded class. The guard is not
292 + * ceremony: this controller is also exercised by tests that do not load that
293 + * file, and a fatal there would be a poor trade for a marketing link.
294 + *
295 + * @param string $from Entry point recorded on the link.
296 + * @param string $fallback Used when core/feedback.php is not loaded.
297 + */
298 + /**
299 + * @return array{status: string, step: int, steps: int}|null
300 + */
301 + private function setupState(): ?array {
302 + $state = new \Forge12\DoubleOptIn\Setup\SetupState();
303 + if ( ! $state->exists() ) {
304 + return null;
305 + }
306 + $current = $state->get();
307 + return array(
308 + 'status' => $current['status'],
309 + 'step' => $current['step'],
310 + 'steps' => \Forge12\DoubleOptIn\Setup\SetupState::STEPS,
311 + );
312 + }
313 +
314 + private function productUrl( string $from, string $fallback ): string {
315 + $fn = '\\forge12\\contactform7\\CF7DoubleOptIn\\get_product_url';
316 +
317 + return function_exists( $fn ) ? esc_url_raw( $fn( $from ) ) : $fallback;
318 + }
319 +
320 + /**
321 + * Support link for the SPA. Empty when unavailable, so the UI can hide it.
322 + */
323 + private function supportUrl(): string {
324 + $fn = '\\forge12\\contactform7\\CF7DoubleOptIn\\get_support_url';
325 +
326 + return function_exists( $fn ) ? esc_url_raw( $fn( 'admin-spa' ) ) : '';
327 + }
328 +
329 + /**
330 + * Feedback link for the SPA. Empty when unavailable, so the UI can hide it.
331 + */
332 + private function feedbackUrl(): string {
333 + $fn = '\\forge12\\contactform7\\CF7DoubleOptIn\\get_feedback_url';
334 +
335 + return function_exists( $fn ) ? esc_url_raw( $fn( 'admin-spa' ) ) : '';
280 336 }
281 337
282 338 /**
283 339 * Feed the active locale's "double-opt-in" translations to wp.i18n so the