PluginProbe
Double Opt-In for Contact Form 7 – Secure, GDPR-Compliant Email Verification / 5.8.1
Double Opt-In for Contact Form 7 – Secure, GDPR-Compliant Email Verification v5.8.1
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 +76 -1 5.1.5 → 5.8.1 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,14 @@
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(),
270 + // Installed form plugins, so the forms page offers only add-ons
271 + // for plugins this site actually runs.
272 + 'formPlugins' => $this->formPlugins(),
265 273 );
266 274
267 275 /**
268 276 * Filter the admin SPA configuration data.
@@ -276,8 +284,75 @@
276 284 */
277 285 $config = apply_filters( 'f12_doi_admin_localize_data', $config );
278 286
279 287 wp_localize_script( 'doi-admin-ui', 'doiAdmin', $config );
288 + }
289 +
290 + /**
291 + * Product-site link for the SPA.
292 + *
293 + * The URL builders live in core/feedback.php, a plain-function file in the
294 + * legacy namespace rather than an autoloaded class. The guard is not
295 + * ceremony: this controller is also exercised by tests that do not load that
296 + * file, and a fatal there would be a poor trade for a marketing link.
297 + *
298 + * @param string $from Entry point recorded on the link.
299 + * @param string $fallback Used when core/feedback.php is not loaded.
300 + */
301 + /**
302 + * @return array<int, array{id: string, name: string, addonLoaded: bool, productUrl: string}>
303 + */
304 + private function formPlugins(): array {
305 + $plugins = array();
306 + foreach ( ( new \Forge12\DoubleOptIn\Setup\FormPluginDetector() )->installed() as $plugin ) {
307 + $plugins[] = array(
308 + 'id' => $plugin['id'],
309 + 'name' => $plugin['name'],
310 + 'addonLoaded' => $plugin['addonLoaded'],
311 + 'productUrl' => $this->productUrl( 'forms-detected-' . $plugin['id'], '' ),
312 + );
313 + }
314 + return $plugins;
315 + }
316 +
317 + /**
318 + * @return array{status: string, step: int, steps: int}|null
319 + */
320 + private function setupState(): ?array {
321 + $state = new \Forge12\DoubleOptIn\Setup\SetupState();
322 + if ( ! $state->exists() ) {
323 + return null;
324 + }
325 + $current = $state->get();
326 + return array(
327 + 'status' => $current['status'],
328 + 'step' => $current['step'],
329 + 'steps' => \Forge12\DoubleOptIn\Setup\SetupState::STEPS,
330 + );
331 + }
332 +
333 + private function productUrl( string $from, string $fallback ): string {
334 + $fn = '\\forge12\\contactform7\\CF7DoubleOptIn\\get_product_url';
335 +
336 + return function_exists( $fn ) ? esc_url_raw( $fn( $from ) ) : $fallback;
337 + }
338 +
339 + /**
340 + * Support link for the SPA. Empty when unavailable, so the UI can hide it.
341 + */
342 + private function supportUrl(): string {
343 + $fn = '\\forge12\\contactform7\\CF7DoubleOptIn\\get_support_url';
344 +
345 + return function_exists( $fn ) ? esc_url_raw( $fn( 'admin-spa' ) ) : '';
346 + }
347 +
348 + /**
349 + * Feedback link for the SPA. Empty when unavailable, so the UI can hide it.
350 + */
351 + private function feedbackUrl(): string {
352 + $fn = '\\forge12\\contactform7\\CF7DoubleOptIn\\get_feedback_url';
353 +
354 + return function_exists( $fn ) ? esc_url_raw( $fn( 'admin-spa' ) ) : '';
280 355 }
281 356
282 357 /**
283 358 * Feed the active locale's "double-opt-in" translations to wp.i18n so the