File "Promos.php"
Full Path: /home/buyiwexj/public_html/wp-content/plugins/wpforms-lite/src/SetupChecklist/Promos.php
File size: 21.84 KB
MIME-type: text/x-php
Charset: utf-8
<?php
namespace WPForms\SetupChecklist;
use WPForms\SetupWizard\Service\PluginCatalog;
use WPForms\SetupWizard\Service\PluginDetector;
use WPForms\SetupWizard\SetupWizard;
/**
* Promo sections model for the Setup Checklist.
*
* Owns the static promo catalog (features, per-tier integrations, recommended growth
* tools) and the logic that turns it into render-ready tiles and cards: license-tier
* selection, wizard personalization, and the one-click install/activate CTA that the
* checklist's item buttons share. {@see Page} renders what this returns.
*
* @since 2.0.0
*/
class Promos {
/**
* Plugin detector (installed/active status for recommended plugins).
*
* @since 2.0.0
*
* @var PluginDetector
*/
private $plugin_detector;
/**
* Plugin catalog (resolves slugs to addon plugin files and display names).
*
* @since 2.0.0
*
* @var PluginCatalog
*/
private $plugin_catalog;
/**
* Constructor.
*
* @since 2.0.0
*
* @param PluginDetector $plugin_detector Plugin detector.
* @param PluginCatalog $plugin_catalog Plugin catalog.
*/
public function __construct( PluginDetector $plugin_detector, PluginCatalog $plugin_catalog ) {
$this->plugin_detector = $plugin_detector;
$this->plugin_catalog = $plugin_catalog;
}
/**
* Render-ready tiles for the "Take Your Forms to the Next Level" feature grid.
*
* @since 2.0.0
*
* @return array<int, array>
*/
public function feature_tiles(): array {
$features = [
[
'icon' => 'fa-arrow-right-from-bracket',
'title' => __( 'Form Abandonment Recovery', 'wpforms-lite' ),
'description' => __( 'Partial form submission & user journey to boost sales.', 'wpforms-lite' ),
'slugs' => [ 'form-abandonment', 'user-journey', 'geolocation' ],
],
[
'icon' => 'fa-chart-bar',
'title' => __( 'Surveys & Polls', 'wpforms-lite' ),
'description' => __( 'Create interactive surveys & gain valuable insights.', 'wpforms-lite' ),
'slugs' => [ 'surveys-polls', 'save-resume' ],
],
[
'icon' => 'fa-calculator',
'title' => __( 'Quizzes & Calculators', 'wpforms-lite' ),
'description' => __( 'Create lead generation quizzes & calculators.', 'wpforms-lite' ),
'slugs' => [ 'quiz', 'calculations' ],
],
[
'icon' => 'fa-comments',
'title' => __( 'Conversational Forms', 'wpforms-lite' ),
'description' => __( 'Improve form completion rate & conversions.', 'wpforms-lite' ),
'slug' => 'conversational-forms',
],
[
'icon' => 'fa-signature',
'title' => __( 'Collect Signatures', 'wpforms-lite' ),
'description' => __( 'Collect secure digital signatures on your forms.', 'wpforms-lite' ),
'slugs' => [ 'signatures', 'pdf' ],
],
[
'icon' => 'fa-sliders',
'title' => __( 'Advanced Form Tools', 'wpforms-lite' ),
'description' => __( 'Advanced fields, form permission control, and more.', 'wpforms-lite' ),
'slug' => 'form-locker',
],
];
$is_pro_plus = $this->is_pro_plus();
$upgrade_url = wpforms_admin_upgrade_link( 'Setup Checklist', 'Take Your Forms to the Next Level' );
$tiles = [];
foreach ( $features as $feature ) {
$tiles[] = array_merge(
[
'icon' => $feature['icon'],
'title' => $feature['title'],
'description' => $feature['description'],
],
$this->feature_link( $feature, $is_pro_plus, $upgrade_url )
);
}
return $tiles;
}
/**
* Render-ready cards for the integrations grid: tier-selected, personalized, and with
* each CTA link resolved.
*
* @since 2.0.0
*
* @return array<int, array>
*/
public function integration_cards(): array {
$upgrade_url = wpforms_admin_upgrade_link( 'Setup Checklist', 'Streamline Your Workflow with Seamless Integrations' );
$cards = [];
foreach ( $this->get_integrations() as $integration ) {
$integration['link'] = $this->integration_link( $integration, $upgrade_url );
$cards[] = $integration;
}
return $cards;
}
/**
* Render-ready tiles for the "Set Up Recommended Growth Tools" grid.
*
* @since 2.0.0
*
* @return array<int, array>
*/
public function growth_tool_tiles(): array {
$tools = [
[
'icon' => 'setup-checklist/brand-aioseo.svg',
'name' => 'AIOSEO',
'description' => __( 'Improve SEO rankings with AI tools and get valuable insights.', 'wpforms-lite' ),
'basename' => 'all-in-one-seo-pack/all_in_one_seo_pack.php',
],
[
'icon' => 'setup-checklist/brand-universally.svg',
'name' => 'Universally',
'description' => __( 'Easily translate your website into 110+ languages within minutes using AI.', 'wpforms-lite' ),
'basename' => 'universally-language-translation-multilingual-tool/universally.php',
],
[
'icon' => 'setup-checklist/brand-duplicator.svg',
'name' => 'Duplicator',
'description' => __( 'Easy, fast, and secure WordPress backups and website migrations.', 'wpforms-lite' ),
'basename' => 'duplicator/duplicator.php',
],
[
'icon' => 'setup-checklist/brand-smashballoon.svg',
'name' => 'Reviews Feed',
'description' => __( 'Show customer reviews from Google, Yelp, TripAdvisor, and more to boost sales.', 'wpforms-lite' ),
'basename' => 'reviews-feed/sb-reviews.php',
],
[
'icon' => 'setup-checklist/brand-optinmonster.svg',
'name' => 'OptinMonster',
'description' => __( 'Get more email subscribers & sales with the #1 CRO toolkit for WordPress.', 'wpforms-lite' ),
'basename' => 'optinmonster/optin-monster-wp-api.php',
],
[
'icon' => 'setup-checklist/brand-monsterinsights.svg',
'name' => 'MonsterInsights',
'description' => __( 'Website analytics made easy for WordPress. Form tracking, reports, and more.', 'wpforms-lite' ),
'basename' => 'google-analytics-for-wordpress/googleanalytics.php',
],
];
$tiles = [];
foreach ( $tools as $tool ) {
$link = $this->install_link( [ $tool['basename'] ] );
$tiles[] = [
'image' => $tool['icon'],
'title' => $tool['name'],
'description' => $tool['description'],
'link_text' => $link['text'],
'link_external' => $link['external'],
'link_action' => $link['action'],
'link_plugin' => $link['plugin'],
];
}
return $tiles;
}
/**
* Resolve the one-click install CTA for one or more plugins by their aggregate state.
*
* The install endpoint installs-or-activates and resolves addon-vs-plugin itself, so this
* builds a single CTA for any WordPress.org plugin or WPForms addon, addressed by basename
* ( comma-joined for a bundle ). An already-active set is a no-op "Installed" state. Shared
* with the checklist's item buttons (WP Mail SMTP, WPConsent).
*
* @since 2.0.0
*
* @param array $files Plugin main-file paths (folder/file.php).
*
* @return array Link parts: `text`, `action`, `plugin`, `external`, `installed`.
*/
public function install_link( array $files ): array {
$all_active = true;
$all_installed = true;
foreach ( $files as $file ) {
$status = $this->plugin_detector->status( $file );
$all_active = $all_active && $status['active'];
$all_installed = $all_installed && $status['installed'];
}
if ( $all_active ) {
$text = __( 'Installed', 'wpforms-lite' );
} elseif ( $all_installed ) {
$text = __( 'Activate', 'wpforms-lite' );
} else {
$text = __( 'Install', 'wpforms-lite' );
}
return [
'text' => $text,
'action' => $all_active ? 'active' : ( $all_installed ? 'activate-plugin' : 'install-plugin' ),
'plugin' => $all_active ? '' : implode( ',', $files ),
'external' => false,
'installed' => $all_installed,
];
}
/**
* Whether the current license is Pro or Elite — the tiers that install addons in place.
*
* @since 2.0.0
*
* @return bool
*/
public function is_pro_plus(): bool {
return in_array( $this->get_license_tier(), [ 'pro', 'elite' ], true );
}
/**
* Build the CTA link parts for a "Take Your Forms to the Next Level" feature tile.
*
* @since 2.0.0
*
* @param array $feature Feature tile (icon, title, description, optional slug).
* @param bool $is_pro_plus Whether the license is Pro or Elite.
* @param string $upgrade_url Upgrade link target used on Lite.
*
* @return array Tile link parts (`link_text`, `link_url`, `link_action`, `link_plugin`, `link_external`).
*/
private function feature_link( array $feature, bool $is_pro_plus, string $upgrade_url ): array {
if ( ! $is_pro_plus ) {
return [
'link_text' => __( 'Upgrade', 'wpforms-lite' ),
'link_url' => $upgrade_url,
'link_external' => true,
];
}
if ( ! empty( $feature['slugs'] ) ) {
$link = $this->addon_install_link( $feature['slugs'] );
} elseif ( ! empty( $feature['slug'] ) ) {
$link = $this->addon_install_link( [ $feature['slug'] ] );
} else {
// No addon slug mapped to this tile — fall back to browsing the Addons page.
$link = $this->addons_page_link();
}
return [
'link_text' => $link['text'],
'link_url' => $link['url'] ?? '#',
'link_action' => $link['action'],
'link_plugin' => $link['plugin'],
'link_external' => $link['external'],
];
}
/**
* Resolve a single integration entry to its CTA link: upgrade, in-place plugin install,
* or in-place addon install.
*
* @since 2.0.0
*
* @param array $integration Integration entry.
* @param string $upgrade_url Upgrade link target (paid tiers).
*
* @return array
*/
private function integration_link( array $integration, string $upgrade_url ): array {
$action = $integration['action'] ?? 'install';
if ( $action === 'upgrade' ) {
return [
'text' => __( 'Upgrade', 'wpforms-lite' ),
'url' => $upgrade_url,
'action' => '',
'plugin' => '',
'external' => true,
];
}
// Free third-party plugin (e.g. Uncanny Automator) — one-click install in place.
if ( ! empty( $integration['plugin_slug'] ) ) {
return $this->install_link( [ $integration['basename'] ] );
}
// A WPForms addon included in this license tier — install it in place, reusing the
// Setup Wizard's gateway (it resolves the licensed download server-side).
return $this->addon_install_link( [ $integration['slug'] ] );
}
/**
* Resolve the install CTA for one or more WPForms addons by slug.
*
* Resolves each slug to its addon plugin file ( an unknown slug bails to the Addons page
* rather than rendering a partial install ), then defers to {@see Promos::install_link()}.
* Installing in place needs the install capability; a user who lacks it is sent to the
* Addons page, while an already-active addon still shows its "Installed" state.
*
* @since 2.0.0
*
* @param array $slugs Addon slugs ( one, or several for a bundled feature tile ).
*
* @return array
*/
private function addon_install_link( array $slugs ): array {
$files = [];
foreach ( $slugs as $slug ) {
$file = $this->addon_plugin_file( $slug );
if ( $file === '' ) {
return $this->addons_page_link();
}
$files[] = $file;
}
if ( $files === [] ) {
return $this->addons_page_link();
}
$link = $this->install_link( $files );
if ( $link['action'] !== 'active' && ! current_user_can( 'install_plugins' ) ) {
return $this->addons_page_link();
}
return $link;
}
/**
* The "Install" CTA that opens the Addons page — the fallback used when an in-place
* install is unavailable (an unknown slug, or a user who cannot install plugins).
*
* @since 2.0.0
*
* @return array
*/
private function addons_page_link(): array {
return [
'text' => __( 'Install', 'wpforms-lite' ),
'url' => admin_url( 'admin.php?page=wpforms-addons' ),
'action' => '',
'plugin' => '',
'external' => false,
];
}
/**
* Resolve an integration slug to its WPForms addon plugin file.
*
* @since 2.0.0
*
* @param string $slug Integration slug.
*
* @return string Plugin file, or empty string when the slug is not a known addon.
*/
private function addon_plugin_file( string $slug ): string {
$renamed = [
'brevo' => 'sendinblue',
];
$folder = 'wpforms-' . ( $renamed[ $slug ] ?? $slug );
$plugin_file = $folder . '/' . $folder . '.php';
return $this->plugin_catalog->is_addon( $plugin_file ) ? $plugin_file : '';
}
/**
* Build an integration grid entry.
*
* @since 2.0.0
*
* @param string $slug Integration slug (also the icon stem: addon-icon-{slug}.png).
* @param string $name Display name.
* @param string $tier Badge to show: '' (none), 'free', 'plus', 'pro', or 'elite'.
* @param string $action The 'install' (included in the tier) or 'upgrade' (needs a higher tier).
*
* @return array
*/
private function integration( string $slug, string $name, string $tier = '', string $action = 'install' ): array {
return [
'slug' => $slug,
'name' => $name,
'tier' => $tier,
'action' => $action,
];
}
/**
* The Uncanny Automator entry — a free wordpress.org plugin installed in place.
*
* @since 2.0.0
*
* @return array
*/
private function uncanny_automator_item(): array {
return [
'slug' => 'uncanny-automator',
'name' => 'Uncanny Automator',
'tier' => 'free',
'action' => 'install',
'icon' => 'icon-provider-uncanny-automator.png',
'plugin_slug' => 'uncanny-automator',
'basename' => 'uncanny-automator/uncanny-automator.php',
];
}
/**
* The integrations shown in the promo grid for the current license tier.
*
* Lite and Basic share one list; Plus, Pro, and Elite each get their own,
* reflecting which integrations the tier already includes (Install) versus
* which require an upgrade.
*
* @since 2.0.0
*
* @return array<int, array>
*/
private function get_integrations(): array {
$tier = $this->get_license_tier();
if ( $tier === 'plus' ) {
$list = $this->get_plus_integrations();
} elseif ( $tier === 'pro' ) {
$list = $this->get_pro_integrations();
} elseif ( $tier === 'elite' ) {
$list = $this->get_elite_integrations();
} else {
$list = $this->get_default_integrations();
}
return $this->personalize_integrations( $list );
}
/**
* Personalize the integrations grid from what the user set up in the Setup Wizard.
*
* The Setup Wizard and this checklist render in separate requests and cannot share
* runtime state, so the wizard durably records every addon it installs in the
* SetupWizard::OPTION_INSTALLED_ADDONS option (written by
* StateManager::record_installed_addons()). This method is the consumer of that record:
* it reads the installed addons and reorders the grid so the integrations the user
* already set up lead, followed by the curated tier list, capped at 10 total.
* Wizard-installed integrations missing from the curated list are rebuilt from the addon
* catalog; feature-only addons and unknown plugins are skipped so they never leak into
* the promo grid.
*
* @since 2.0.0
*
* @param array $curated Curated integrations for the current license tier.
*
* @return array<int, array>
*/
private function personalize_integrations( array $curated ): array {
$max = 10;
$installed = $this->wizard_installed_addons();
if ( $installed === [] ) {
return array_slice( $curated, 0, $max );
}
$lead = [];
$rest = [];
$seen_files = [];
// Curated integrations the user set up in the wizard lead; the rest follow.
foreach ( $curated as $integration ) {
$file = $this->integration_plugin_file( $integration );
$seen_files[] = $file;
if ( $file !== '' && in_array( $file, $installed, true ) ) {
$lead[] = $integration;
} else {
$rest[] = $integration;
}
}
// Wizard-installed integrations missing from the curated list lead too; feature addons
// and unknown plugins are skipped. Each is badged from its addon data (see wizard_integration()).
$known = $this->known_integration_files();
foreach ( $installed as $file ) {
if ( in_array( $file, $seen_files, true ) || ! in_array( $file, $known, true ) ) {
continue;
}
$seen_files[] = $file;
$lead[] = $this->wizard_integration( $file );
}
return array_slice( array_merge( $lead, $rest ), 0, $max );
}
/**
* Build a grid entry for a wizard-installed integration the current tier does not curate.
*
* @since 2.0.0
*
* @param string $file Plugin file.
*
* @return array
*/
private function wizard_integration( string $file ): array {
$entry = [
'slug' => str_replace( 'wpforms-', '', dirname( $file ) ),
'name' => $this->plugin_catalog->name( $file ),
'tier' => '',
'action' => 'install',
];
$addons = wpforms()->obj( 'addons' );
$addon = $addons ? (array) $addons->get_addon( dirname( $file ) ) : [];
if ( empty( $addon['path'] ) || $addon['path'] !== $file ) {
return $entry;
}
if ( empty( $addon['plugin_allow'] ) ) {
$entry['tier'] = $addon['license_level'] ?? '';
$entry['action'] = 'upgrade';
}
return $entry;
}
/**
* Plugin files for every integration any tier lists — the set the grid recognises,
* so wizard-installed feature addons do not leak into the integrations promo.
*
* @since 2.0.0
*
* @return array<int, string>
*/
private function known_integration_files(): array {
$all = array_merge(
$this->get_default_integrations(),
$this->get_plus_integrations(),
$this->get_pro_integrations(),
$this->get_elite_integrations()
);
return array_values( array_filter( array_map( [ $this, 'integration_plugin_file' ], $all ) ) );
}
/**
* Resolve an integration entry to its plugin file: an explicit basename (cross-product
* plugins like Uncanny Automator) or the WPForms addon file derived from the slug.
*
* @since 2.0.0
*
* @param array $integration Integration entry.
*
* @return string Plugin file, or empty string when it cannot be resolved.
*/
private function integration_plugin_file( array $integration ): string {
if ( ! empty( $integration['basename'] ) ) {
return $integration['basename'];
}
return $this->addon_plugin_file( $integration['slug'] ?? '' );
}
/**
* Addon plugin files the user installed during the Setup Wizard (durable record).
*
* @since 2.0.0
*
* @return array<int, string>
*/
private function wizard_installed_addons(): array {
return array_values( array_filter( (array) get_option( SetupWizard::OPTION_INSTALLED_ADDONS, [] ) ) );
}
/**
* Resolve the license to a promo bucket: plus, pro, elite, or default (Lite + Basic).
*
* @since 2.0.0
*
* @return string
*/
private function get_license_tier(): string {
$type = wpforms_get_license_type();
if ( $type === 'plus' || $type === 'pro' ) {
return $type;
}
// Elite and its legacy license names share the Elite list.
if ( in_array( $type, [ 'elite', 'agency', 'ultimate' ], true ) ) {
return 'elite';
}
// Lite (no license) and Basic share the default list.
return 'default';
}
/**
* Integrations for Lite and Basic (every paid integration is an upgrade).
*
* @since 2.0.0
*
* @return array<int, array>
*/
private function get_default_integrations(): array {
return [
$this->integration( 'google-sheets', 'Google Sheets', 'pro', 'upgrade' ),
$this->integration( 'mailchimp', 'Mailchimp', 'pro', 'upgrade' ),
$this->uncanny_automator_item(),
$this->integration( 'brevo', 'Brevo', 'plus', 'upgrade' ),
$this->integration( 'zapier', 'Zapier', 'pro', 'upgrade' ),
$this->integration( 'google-drive', 'Google Drive', 'pro', 'upgrade' ),
$this->integration( 'slack', 'Slack', 'pro', 'upgrade' ),
$this->integration( 'webhooks', 'Webhooks', 'elite', 'upgrade' ),
$this->integration( 'hubspot', 'HubSpot', 'elite', 'upgrade' ),
$this->integration( 'dropbox', 'Dropbox', 'pro', 'upgrade' ),
];
}
/**
* Integrations for the Plus tier.
*
* @since 2.0.0
*
* @return array<int, array>
*/
private function get_plus_integrations(): array {
return [
$this->integration( 'google-sheets', 'Google Sheets', 'pro', 'upgrade' ),
$this->integration( 'slack', 'Slack' ),
$this->integration( 'mailchimp', 'Mailchimp' ),
$this->integration( 'notion', 'Notion' ),
$this->uncanny_automator_item(),
$this->integration( 'mailerlite', 'MailerLite' ),
$this->integration( 'twilio', 'Twilio' ),
$this->integration( 'google-drive', 'Google Drive', 'pro', 'upgrade' ),
$this->integration( 'brevo', 'Brevo' ),
$this->integration( 'dropbox', 'Dropbox', 'pro', 'upgrade' ),
];
}
/**
* Integrations for the Pro tier.
*
* @since 2.0.0
*
* @return array<int, array>
*/
private function get_pro_integrations(): array {
return [
$this->integration( 'google-sheets', 'Google Sheets' ),
$this->integration( 'slack', 'Slack' ),
$this->integration( 'mailchimp', 'Mailchimp' ),
$this->integration( 'zapier', 'Zapier' ),
$this->uncanny_automator_item(),
$this->integration( 'dropbox', 'Dropbox' ),
$this->integration( 'mailerlite', 'MailerLite' ),
$this->integration( 'google-drive', 'Google Drive' ),
$this->integration( 'hubspot', 'HubSpot', 'elite', 'upgrade' ),
$this->integration( 'webhooks', 'Webhooks', 'elite', 'upgrade' ),
];
}
/**
* Integrations for the Elite tier (everything is included — all Install).
*
* @since 2.0.0
*
* @return array<int, array>
*/
private function get_elite_integrations(): array {
return [
$this->integration( 'google-sheets', 'Google Sheets' ),
$this->integration( 'salesforce', 'Salesforce' ),
$this->integration( 'mailchimp', 'Mailchimp' ),
$this->integration( 'hubspot', 'HubSpot' ),
$this->integration( 'zapier', 'Zapier' ),
$this->uncanny_automator_item(),
$this->integration( 'google-drive', 'Google Drive' ),
$this->integration( 'mailerlite', 'MailerLite' ),
$this->integration( 'dropbox', 'Dropbox' ),
$this->integration( 'webhooks', 'Webhooks' ),
];
}
}