| @@ -10,9 +10,9 @@ | ||
| 10 | 10 | */ |
| 11 | 11 | public static $action_post_type = 'frm_form_actions'; |
| 12 | 12 | |
| 13 | 13 | /** |
| 14 | - * @var array|null | |
| 14 | + * @var Frm_Form_Action_Factory|null | |
| 15 | 15 | */ |
| 16 | 16 | public static $registered_actions; |
| 17 | 17 | |
| 18 | 18 | /** |
| @@ -37,8 +37,9 @@ | ||
| 37 | 37 | 'has_archive' => false, |
| 38 | 38 | ) |
| 39 | 39 | ); |
| 40 | 40 | |
| 41 | + self::maybe_setup_unlicensed_action_gate(); | |
| 41 | 42 | self::actions_init(); |
| 42 | 43 | } |
| 43 | 44 | |
| 44 | 45 | public static function actions_init() { |
| @@ -55,27 +56,30 @@ | ||
| 55 | 56 | 'on_submit' => 'FrmOnSubmitAction', |
| 56 | 57 | 'email' => 'FrmEmailAction', |
| 57 | 58 | 'wppost' => 'FrmDefPostAction', |
| 58 | 59 | 'register' => 'FrmDefRegAction', |
| 59 | - 'paypal' => 'FrmDefPayPalAction', | |
| 60 | + 'stripe' => 'FrmStripeLiteAction', | |
| 61 | + 'square' => 'FrmSquareAction', | |
| 62 | + 'paypal' => 'FrmPayPalLiteAction', | |
| 63 | + 'paypal-legacy' => 'FrmDefPayPalLegacyAction', | |
| 60 | 64 | 'payment' => 'FrmTransLiteAction', |
| 61 | 65 | 'quiz' => 'FrmDefQuizAction', |
| 62 | 66 | 'quiz_outcome' => 'FrmDefQuizOutcomeAction', |
| 67 | + 'api' => 'FrmDefApiAction', | |
| 63 | 68 | 'mailchimp' => 'FrmDefMlcmpAction', |
| 64 | - 'api' => 'FrmDefApiAction', | |
| 65 | - 'salesforce' => 'FrmDefSalesforceAction', | |
| 66 | 69 | 'activecampaign' => 'FrmDefActiveCampaignAction', |
| 67 | 70 | 'constantcontact' => 'FrmDefConstContactAction', |
| 68 | 71 | 'getresponse' => 'FrmDefGetResponseAction', |
| 72 | + 'mailpoet' => 'FrmDefMailpoetAction', | |
| 73 | + 'convertkit' => 'FrmDefConvertKitAction', | |
| 74 | + 'aweber' => 'FrmDefAweberAction', | |
| 75 | + 'twilio' => 'FrmDefTwilioAction', | |
| 76 | + 'salesforce' => 'FrmDefSalesforceAction', | |
| 69 | 77 | 'hubspot' => 'FrmDefHubspotAction', |
| 78 | + 'highrise' => 'FrmDefHighriseAction', | |
| 70 | 79 | 'zapier' => 'FrmDefZapierAction', |
| 80 | + 'googlespreadsheet' => 'FrmDefGoogleSpreadsheetAction', | |
| 71 | 81 | 'n8n' => 'FrmDefN8NAction', |
| 72 | - 'twilio' => 'FrmDefTwilioAction', | |
| 73 | - 'highrise' => 'FrmDefHighriseAction', | |
| 74 | - 'mailpoet' => 'FrmDefMailpoetAction', | |
| 75 | - 'aweber' => 'FrmDefAweberAction', | |
| 76 | - 'convertkit' => 'FrmDefConvertKitAction', | |
| 77 | - 'googlespreadsheet' => 'FrmDefGoogleSpreadsheetAction', | |
| 78 | 82 | ); |
| 79 | 83 | |
| 80 | 84 | $action_classes = apply_filters( 'frm_registered_form_actions', $action_classes ); |
| 81 | 85 | $action_classes = self::maybe_unset_highrise( $action_classes ); |
| @@ -82,14 +86,65 @@ | ||
| 82 | 86 | |
| 83 | 87 | include_once FrmAppHelper::plugin_path() . '/classes/views/frm-form-actions/email_action.php'; |
| 84 | 88 | include_once FrmAppHelper::plugin_path() . '/classes/views/frm-form-actions/default_actions.php'; |
| 85 | 89 | |
| 90 | + // This needs to be called after we include default_actions.php or FrmDefPayPalLegacyAction will never exist. | |
| 91 | + if ( 'FrmPayPalLiteAction' === $action_classes['paypal'] || ! class_exists( 'FrmPaymentAction' ) || ! class_exists( 'FrmDefPayPalLegacyAction' ) ) { | |
| 92 | + unset( $action_classes['paypal-legacy'] ); | |
| 93 | + } | |
| 94 | + | |
| 86 | 95 | foreach ( $action_classes as $action_class ) { |
| 87 | 96 | self::$registered_actions->register( $action_class ); |
| 88 | 97 | } |
| 98 | + | |
| 99 | + self::apply_default_action_descriptions(); | |
| 89 | 100 | } |
| 90 | 101 | |
| 91 | 102 | /** |
| 103 | + * Sets default descriptions on registered actions from a central list. | |
| 104 | + * | |
| 105 | + * Keeps the description when an add-on replaces a base action class without its own. | |
| 106 | + * | |
| 107 | + * @since 6.31 | |
| 108 | + * | |
| 109 | + * @return void | |
| 110 | + */ | |
| 111 | + private static function apply_default_action_descriptions() { | |
| 112 | + $descriptions = array( | |
| 113 | + 'on_submit' => __( 'Success messages', 'formidable' ), | |
| 114 | + 'email' => __( 'Autoresponder alerts', 'formidable' ), | |
| 115 | + 'wppost' => __( 'Content publishing', 'formidable' ), | |
| 116 | + 'register' => __( 'Account creation', 'formidable' ), | |
| 117 | + 'payment' => __( 'Transaction alerts', 'formidable' ), | |
| 118 | + 'stripe' => __( 'Payment gateway', 'formidable' ), | |
| 119 | + 'square' => __( 'Payment gateway', 'formidable' ), | |
| 120 | + 'paypal' => __( 'Payment gateway', 'formidable' ), | |
| 121 | + 'quiz' => __( 'Automated grading', 'formidable' ), | |
| 122 | + 'quiz_outcome' => __( 'Result logic', 'formidable' ), | |
| 123 | + 'aweber' => __( 'List triggers', 'formidable' ), | |
| 124 | + 'mailchimp' => __( 'Subscription confirmation', 'formidable' ), | |
| 125 | + 'zapier' => __( 'App automation', 'formidable' ), | |
| 126 | + 'n8n' => __( 'Workflow automation', 'formidable' ), | |
| 127 | + 'twilio' => __( 'Text notifications', 'formidable' ), | |
| 128 | + 'activecampaign' => __( 'Contact automation', 'formidable' ), | |
| 129 | + 'salesforce' => __( 'Lead automation', 'formidable' ), | |
| 130 | + 'constantcontact' => __( 'Content distribution', 'formidable' ), | |
| 131 | + 'getresponse' => __( 'Success notifications', 'formidable' ), | |
| 132 | + 'hubspot' => __( 'CRM alerts', 'formidable' ), | |
| 133 | + 'mailpoet' => __( 'Plugin automation', 'formidable' ), | |
| 134 | + 'api' => __( 'System integration', 'formidable' ), | |
| 135 | + 'googlespreadsheet' => __( 'Spreadsheet sync', 'formidable' ), | |
| 136 | + 'convertkit' => __( 'Broadcast publishing', 'formidable' ), | |
| 137 | + ); | |
| 138 | + | |
| 139 | + foreach ( self::$registered_actions->actions as $action ) { | |
| 140 | + if ( $action->action_options['description'] === '' && isset( $descriptions[ $action->id_base ] ) ) { | |
| 141 | + $action->action_options['description'] = $descriptions[ $action->id_base ]; | |
| 142 | + } | |
| 143 | + } | |
| 144 | + } | |
| 145 | + | |
| 146 | + /** | |
| 92 | 147 | * Remove the Highrise action if it is not registered. |
| 93 | 148 | * |
| 94 | 149 | * @since 6.23 |
| 95 | 150 | * |
| @@ -136,8 +191,19 @@ | ||
| 136 | 191 | foreach ( $groups as $group ) { |
| 137 | 192 | if ( isset( $group['actions'] ) ) { |
| 138 | 193 | $grouped = array_merge( $grouped, $group['actions'] ); |
| 139 | 194 | } |
| 195 | + | |
| 196 | + // Also collect actions from sections | |
| 197 | + if ( ! isset( $group['sections'] ) ) { | |
| 198 | + continue; | |
| 199 | + } | |
| 200 | + | |
| 201 | + foreach ( $group['sections'] as $section ) { | |
| 202 | + if ( isset( $section['actions'] ) ) { | |
| 203 | + $grouped = array_merge( $grouped, $section['actions'] ); | |
| 204 | + } | |
| 205 | + } | |
| 140 | 206 | } |
| 141 | 207 | |
| 142 | 208 | foreach ( $action_controls as $action ) { |
| 143 | 209 | if ( isset( $groups[ $action->id_base ] ) || in_array( $action->id_base, $grouped, true ) ) { |
| @@ -164,31 +230,47 @@ | ||
| 164 | 230 | * |
| 165 | 231 | * @return array |
| 166 | 232 | */ |
| 167 | 233 | public static function form_action_groups() { |
| 234 | + // Get all action controls to check which are active | |
| 235 | + $action_controls = self::get_form_actions(); | |
| 236 | + | |
| 237 | + // Determine which actions are currently available | |
| 238 | + $available_actions = self::get_available_my_actions( $action_controls ); | |
| 239 | + | |
| 240 | + // Featured actions are add-ons that are NOT currently available | |
| 241 | + $all_addon_actions = array( | |
| 242 | + 'api', | |
| 243 | + 'register', | |
| 244 | + 'n8n', | |
| 245 | + 'quiz', | |
| 246 | + 'quiz_outcome', | |
| 247 | + 'googlespreadsheet', | |
| 248 | + ); | |
| 249 | + $featured_actions = array_diff( $all_addon_actions, $available_actions ); | |
| 250 | + | |
| 168 | 251 | $groups = array( |
| 169 | - 'misc' => array( | |
| 170 | - 'name' => '', | |
| 171 | - 'icon' => 'frmfont frm_shuffle_icon', | |
| 172 | - 'actions' => array( | |
| 173 | - 'email', | |
| 174 | - 'wppost', | |
| 175 | - 'register', | |
| 176 | - 'quiz', | |
| 177 | - 'quiz_outcome', | |
| 178 | - 'twilio', | |
| 252 | + 'my_actions' => array( | |
| 253 | + 'name' => __( 'My Actions', 'formidable' ), | |
| 254 | + 'icon' => 'frmfont frm_shuffle_icon', | |
| 255 | + 'sections' => array( | |
| 256 | + 'active' => array( | |
| 257 | + 'name' => '', | |
| 258 | + 'actions' => $available_actions, | |
| 259 | + ), | |
| 260 | + 'featured' => array( | |
| 261 | + 'name' => __( 'Featured', 'formidable' ), | |
| 262 | + 'actions' => array_values( $featured_actions ), | |
| 263 | + ), | |
| 179 | 264 | ), |
| 180 | 265 | ), |
| 181 | - 'payment' => array( | |
| 182 | - 'name' => __( 'eCommerce', 'formidable' ), | |
| 266 | + 'payment' => array( | |
| 267 | + 'name' => __( 'E-Commerce', 'formidable' ), | |
| 183 | 268 | 'icon' => 'frmfont frm_credit_card_alt_icon', |
| 184 | - 'actions' => array( | |
| 185 | - 'paypal', | |
| 186 | - 'payment', | |
| 187 | - ), | |
| 269 | + 'actions' => self::get_payment_actions( $action_controls ), | |
| 188 | 270 | ), |
| 189 | - 'marketing' => array( | |
| 190 | - 'name' => __( 'Email Marketing', 'formidable' ), | |
| 271 | + 'marketing' => array( | |
| 272 | + 'name' => __( 'Marketing', 'formidable' ), | |
| 191 | 273 | 'icon' => 'frmfont frm_mail_bulk_icon', |
| 192 | 274 | 'actions' => array( |
| 193 | 275 | 'mailchimp', |
| 194 | 276 | 'activecampaign', |
| @@ -196,15 +278,21 @@ | ||
| 196 | 278 | 'getresponse', |
| 197 | 279 | 'aweber', |
| 198 | 280 | 'mailpoet', |
| 199 | 281 | 'convertkit', |
| 282 | + 'twilio', | |
| 200 | 283 | ), |
| 201 | 284 | ), |
| 202 | - 'crm' => array( | |
| 285 | + 'crm' => array( | |
| 203 | 286 | 'name' => __( 'CRM', 'formidable' ), |
| 204 | 287 | 'icon' => 'frmfont frm_address_card_icon', |
| 205 | 288 | 'actions' => self::get_crm_actions(), |
| 206 | 289 | ), |
| 290 | + 'misc' => array( | |
| 291 | + 'name' => __( 'Misc', 'formidable' ), | |
| 292 | + 'icon' => 'frmfont frm_shuffle_icon', | |
| 293 | + 'actions' => self::get_misc_actions( $action_controls ), | |
| 294 | + ), | |
| 207 | 295 | ); |
| 208 | 296 | |
| 209 | 297 | return apply_filters( 'frm_action_groups', $groups ); |
| 210 | 298 | } |
| @@ -209,8 +297,102 @@ | ||
| 209 | 297 | return apply_filters( 'frm_action_groups', $groups ); |
| 210 | 298 | } |
| 211 | 299 | |
| 212 | 300 | /** |
| 301 | + * Get the actions that are currently available (active) for My Actions section. | |
| 302 | + * | |
| 303 | + * @since 6.31 | |
| 304 | + * | |
| 305 | + * @param array $action_controls The registered action controls. | |
| 306 | + * | |
| 307 | + * @return array | |
| 308 | + */ | |
| 309 | + private static function get_available_my_actions( $action_controls ) { | |
| 310 | + $available = array( | |
| 311 | + 'on_submit', | |
| 312 | + 'email', | |
| 313 | + 'stripe', | |
| 314 | + 'square', | |
| 315 | + 'paypal', | |
| 316 | + ); | |
| 317 | + | |
| 318 | + // Include all actions that are marked as active, including custom actions. | |
| 319 | + // This ensures custom actions appear in "My Actions" when enabled. | |
| 320 | + foreach ( $action_controls as $action_id => $action_control ) { | |
| 321 | + if ( ! in_array( $action_id, $available, true ) && ! empty( $action_control->action_options['active'] ) ) { | |
| 322 | + $available[] = $action_id; | |
| 323 | + } | |
| 324 | + } | |
| 325 | + | |
| 326 | + return $available; | |
| 327 | + } | |
| 328 | + | |
| 329 | + /** | |
| 330 | + * Get the actions to include in the Misc section. | |
| 331 | + * | |
| 332 | + * @since 6.31 | |
| 333 | + * | |
| 334 | + * @param array $action_controls The registered action controls. | |
| 335 | + * | |
| 336 | + * @return array | |
| 337 | + */ | |
| 338 | + private static function get_misc_actions( $action_controls ) { | |
| 339 | + $misc_actions = array( | |
| 340 | + 'on_submit', | |
| 341 | + 'email', | |
| 342 | + 'wppost', | |
| 343 | + 'register', | |
| 344 | + 'api', | |
| 345 | + 'n8n', | |
| 346 | + 'quiz', | |
| 347 | + 'quiz_outcome', | |
| 348 | + 'googlespreadsheet', | |
| 349 | + ); | |
| 350 | + | |
| 351 | + // Include all active actions that aren't in specific groups (payment, marketing, crm). | |
| 352 | + // This ensures custom actions appear in "Misc" when enabled. | |
| 353 | + $payment_actions = self::get_payment_actions( $action_controls ); | |
| 354 | + $marketing_actions = array( 'mailchimp', 'activecampaign', 'constantcontact', 'getresponse', 'aweber', 'mailpoet', 'convertkit', 'twilio' ); | |
| 355 | + | |
| 356 | + $excluded_actions = array_merge( $payment_actions, $marketing_actions, self::get_crm_actions() ); | |
| 357 | + | |
| 358 | + foreach ( $action_controls as $action_id => $action_control ) { | |
| 359 | + if ( ! in_array( $action_id, $misc_actions, true ) && ! in_array( $action_id, $excluded_actions, true ) && ! empty( $action_control->action_options['active'] ) ) { | |
| 360 | + $misc_actions[] = $action_id; | |
| 361 | + } | |
| 362 | + } | |
| 363 | + | |
| 364 | + return $misc_actions; | |
| 365 | + } | |
| 366 | + | |
| 367 | + /** | |
| 368 | + * Get the actions to include in the E-Commerce section. | |
| 369 | + * | |
| 370 | + * @since 6.33 | |
| 371 | + * | |
| 372 | + * @param array $action_controls | |
| 373 | + * | |
| 374 | + * @return array | |
| 375 | + */ | |
| 376 | + private static function get_payment_actions( $action_controls ) { | |
| 377 | + $payment_actions = array( | |
| 378 | + 'paypal', | |
| 379 | + 'stripe', | |
| 380 | + 'square', | |
| 381 | + ); | |
| 382 | + | |
| 383 | + if ( isset( $action_controls['paypal-legacy'] ) ) { | |
| 384 | + $payment_actions[] = 'paypal-legacy'; | |
| 385 | + } | |
| 386 | + | |
| 387 | + if ( isset( $action_controls['payment'] ) ) { | |
| 388 | + $payment_actions[] = 'payment'; | |
| 389 | + } | |
| 390 | + | |
| 391 | + return $payment_actions; | |
| 392 | + } | |
| 393 | + | |
| 394 | + /** | |
| 213 | 395 | * Get the actions to include in the CRM section. |
| 214 | 396 | * |
| 215 | 397 | * @since 6.23 |
| 216 | 398 | * |
| @@ -219,8 +401,9 @@ | ||
| 219 | 401 | private static function get_crm_actions() { |
| 220 | 402 | $crm_actions = array( |
| 221 | 403 | 'salesforce', |
| 222 | 404 | 'hubspot', |
| 405 | + 'zapier', | |
| 223 | 406 | ); |
| 224 | 407 | |
| 225 | 408 | // Only include Highrise when the add-on is active. |
| 226 | 409 | // This is because Highrise is deprecated. We don't want to show it in Lite. |
| @@ -277,8 +460,12 @@ | ||
| 277 | 460 | $classes .= ' frm_active_action'; |
| 278 | 461 | } else { |
| 279 | 462 | $classes .= ' frm_inactive_action'; |
| 280 | 463 | |
| 464 | + if ( str_contains( $action_control->action_options['classes'], 'frm_show_expired_modal' ) ) { | |
| 465 | + $classes .= ' frm_show_expired_modal'; | |
| 466 | + } | |
| 467 | + | |
| 281 | 468 | if ( $default_position !== false && ( $allowed_count + $default_position ) < 6 ) { |
| 282 | 469 | $group_class .= ' frm-default-show'; |
| 283 | 470 | } |
| 284 | 471 | |
| @@ -299,20 +486,42 @@ | ||
| 299 | 486 | |
| 300 | 487 | if ( $requires && 'free' !== $requires ) { |
| 301 | 488 | $data['data-requires'] = $requires; |
| 302 | 489 | } |
| 490 | + | |
| 491 | + $learn_more_slug = ! empty( $action_control->action_options['learn-more'] ) | |
| 492 | + ? $action_control->action_options['learn-more'] | |
| 493 | + : self::get_learn_more_slug( $action_control->id_base ); | |
| 494 | + | |
| 495 | + if ( $learn_more_slug ) { | |
| 496 | + $data['data-learn-more'] = FrmAppHelper::get_doc_url( | |
| 497 | + $learn_more_slug, | |
| 498 | + 'settings-' . $action_control->id_base, | |
| 499 | + ! str_contains( $learn_more_slug, '/' ) | |
| 500 | + ); | |
| 501 | + } | |
| 303 | 502 | }//end if |
| 304 | 503 | |
| 305 | - // HTML to include on the icon. | |
| 306 | - $icon_atts = array(); | |
| 504 | + include FrmAppHelper::plugin_path() . '/classes/views/frm-form-actions/_action_icon.php'; | |
| 505 | + } | |
| 307 | 506 | |
| 308 | - if ( $action_control->action_options['color'] !== 'var(--primary-700)' ) { | |
| 309 | - $icon_atts = array( | |
| 507 | + /** | |
| 508 | + * Get the HTML attributes for the action icon. | |
| 509 | + * | |
| 510 | + * @since 6.31 | |
| 511 | + * | |
| 512 | + * @param object $action_control | |
| 513 | + * | |
| 514 | + * @return array | |
| 515 | + */ | |
| 516 | + public static function get_action_icon_atts( $action_control ) { | |
| 517 | + if ( 'var(--primary-700)' !== $action_control->action_options['color'] ) { | |
| 518 | + return array( | |
| 310 | 519 | 'style' => '--primary-700:' . $action_control->action_options['color'], |
| 311 | 520 | ); |
| 312 | 521 | } |
| 313 | 522 | |
| 314 | - include FrmAppHelper::plugin_path() . '/classes/views/frm-form-actions/_action_icon.php'; | |
| 523 | + return array(); | |
| 315 | 524 | } |
| 316 | 525 | |
| 317 | 526 | /** |
| 318 | 527 | * @param string $action |
| @@ -379,8 +588,12 @@ | ||
| 379 | 588 | } |
| 380 | 589 | |
| 381 | 590 | self::maybe_show_limit_warning( $form->id, $form_actions ); |
| 382 | 591 | |
| 592 | + echo '<p class="frm-mb-lg frm-no-actions-message' . ( $form_actions ? ' frm_hidden' : '' ) . '"> ' | |
| 593 | + . esc_html__( 'No actions have been added yet. Select an action above to get started.', 'formidable' ) | |
| 594 | + . '</p>'; | |
| 595 | + | |
| 383 | 596 | foreach ( $form_actions as $action ) { |
| 384 | 597 | if ( ! isset( $action_map[ $action->post_excerpt ] ) ) { |
| 385 | 598 | // Don't try and show settings if action no longer exists |
| 386 | 599 | continue; |
| @@ -447,13 +660,18 @@ | ||
| 447 | 660 | public static function add_form_action() { |
| 448 | 661 | FrmAppHelper::permission_check( 'frm_edit_forms' ); |
| 449 | 662 | check_ajax_referer( 'frm_ajax', 'nonce' ); |
| 450 | 663 | |
| 664 | + $action_key = FrmAppHelper::get_param( 'list_id', '', 'post', 'absint' ); | |
| 665 | + $action_type = FrmAppHelper::get_param( 'type', '', 'post', 'sanitize_text_field' ); | |
| 666 | + $lite_actions = array_fill_keys( self::get_lite_actions(), true ); | |
| 667 | + | |
| 668 | + if ( ! FrmAppHelper::pro_is_connected() && ! isset( $lite_actions[ $action_type ] ) ) { | |
| 669 | + wp_die(); | |
| 670 | + } | |
| 671 | + | |
| 451 | 672 | global $frm_vars; |
| 452 | 673 | |
| 453 | - $action_key = FrmAppHelper::get_param( 'list_id', '', 'post', 'absint' ); | |
| 454 | - $action_type = FrmAppHelper::get_param( 'type', '', 'post', 'sanitize_text_field' ); | |
| 455 | - | |
| 456 | 674 | /** |
| 457 | 675 | * @var FrmFormAction |
| 458 | 676 | */ |
| 459 | 677 | $action_control = self::get_form_actions( $action_type ); |
| @@ -458,10 +676,16 @@ | ||
| 458 | 676 | */ |
| 459 | 677 | $action_control = self::get_form_actions( $action_type ); |
| 460 | 678 | $action_control->_set( $action_key ); |
| 461 | 679 | |
| 462 | - $form_id = FrmAppHelper::get_param( 'form_id', '', 'post', 'absint' ); | |
| 463 | - $form_action = $action_control->prepare_new( $form_id ); | |
| 680 | + $form_id = FrmAppHelper::get_param( 'form_id', '', 'post', 'absint' ); | |
| 681 | + $form_action = $action_control->prepare_new( $form_id ); | |
| 682 | + $existing_titles = (array) FrmAppHelper::get_post_param( 'existing_titles', array(), 'sanitize_text_field' ); | |
| 683 | + | |
| 684 | + if ( $existing_titles ) { | |
| 685 | + $form_action->post_title = self::get_unique_action_title( $form_action->post_title, $existing_titles ); | |
| 686 | + } | |
| 687 | + | |
| 464 | 688 | $use_logging = self::should_show_log_message( $action_type ); |
| 465 | 689 | $values = array(); |
| 466 | 690 | $form = self::fields_to_values( $form_id, $values ); |
| 467 | 691 | |
| @@ -468,8 +692,29 @@ | ||
| 468 | 692 | include FrmAppHelper::plugin_path() . '/classes/views/frm-form-actions/form_action.php'; |
| 469 | 693 | wp_die(); |
| 470 | 694 | } |
| 471 | 695 | |
| 696 | + /** | |
| 697 | + * Returns the first available title not in $existing_titles, appending " (2)", " (3)", etc. if needed. | |
| 698 | + * | |
| 699 | + * @since 6.31 | |
| 700 | + * | |
| 701 | + * @param string $base_title Default action title from the action type. | |
| 702 | + * @param string[] $existing_titles Titles currently visible in the form editor. | |
| 703 | + * | |
| 704 | + * @return string | |
| 705 | + */ | |
| 706 | + private static function get_unique_action_title( $base_title, array $existing_titles ) { | |
| 707 | + $taken = array_flip( $existing_titles ); | |
| 708 | + $title = $base_title; | |
| 709 | + | |
| 710 | + for ( $n = 2; isset( $taken[ $title ] ); $n++ ) { | |
| 711 | + $title = $base_title . ' (' . $n . ')'; | |
| 712 | + } | |
| 713 | + | |
| 714 | + return $title; | |
| 715 | + } | |
| 716 | + | |
| 472 | 717 | public static function fill_action() { |
| 473 | 718 | FrmAppHelper::permission_check( 'frm_edit_forms' ); |
| 474 | 719 | check_ajax_referer( 'frm_ajax', 'nonce' ); |
| 475 | 720 | |
| @@ -497,9 +742,25 @@ | ||
| 497 | 742 | * |
| 498 | 743 | * @return bool |
| 499 | 744 | */ |
| 500 | 745 | private static function should_show_log_message( $action_type ) { |
| 501 | - $logging = array( 'api', 'salesforce', 'constantcontact', 'activecampaign' ); | |
| 746 | + $logging = array( | |
| 747 | + 'activecampaign', | |
| 748 | + 'api', | |
| 749 | + 'aweber', | |
| 750 | + 'campaignmonitor', | |
| 751 | + 'constantcontact', | |
| 752 | + 'convertkit', | |
| 753 | + 'getresponse', | |
| 754 | + 'googlespreadsheet', | |
| 755 | + 'hubspot', | |
| 756 | + 'mailchimp', | |
| 757 | + 'mailpoet', | |
| 758 | + 'n8n', | |
| 759 | + 'salesforce', | |
| 760 | + 'twilio', | |
| 761 | + 'zapier', | |
| 762 | + ); | |
| 502 | 763 | return in_array( $action_type, $logging, true ) && ! function_exists( 'frm_log_autoloader' ); |
| 503 | 764 | } |
| 504 | 765 | |
| 505 | 766 | /** |
| @@ -799,8 +1060,123 @@ | ||
| 799 | 1060 | * @return bool|null |
| 800 | 1061 | */ |
| 801 | 1062 | public static function prevent_wpml_translations( $null, $post_type ) { |
| 802 | 1063 | return self::$action_post_type === $post_type ? false : $null; |
| 1064 | + } | |
| 1065 | + | |
| 1066 | + /** | |
| 1067 | + * If Pro is not connected, hook a filter that will force all non-Lite | |
| 1068 | + * actions to inactive so the upgrade popup is shown instead. | |
| 1069 | + * | |
| 1070 | + * @since 6.31 | |
| 1071 | + * | |
| 1072 | + * @return void | |
| 1073 | + */ | |
| 1074 | + private static function maybe_setup_unlicensed_action_gate() { | |
| 1075 | + if ( FrmAppHelper::pro_is_connected() ) { | |
| 1076 | + return; | |
| 1077 | + } | |
| 1078 | + | |
| 1079 | + add_filter( 'frm_registered_form_actions', array( self::class, 'disable_unlicensed_actions' ), 100 ); | |
| 1080 | + } | |
| 1081 | + | |
| 1082 | + /** | |
| 1083 | + * For every registered action that is not a Lite action, add a per-action | |
| 1084 | + * options filter that forces it to inactive with the upgrade class. | |
| 1085 | + * | |
| 1086 | + * Runs inside apply_filters('frm_registered_form_actions') at priority 100, | |
| 1087 | + * so the per-key option filters are in place before the class constructors | |
| 1088 | + * run in the foreach loop that follows. | |
| 1089 | + * | |
| 1090 | + * @since 6.31 | |
| 1091 | + * | |
| 1092 | + * @param array $actions Map of action_key => class_name. | |
| 1093 | + * | |
| 1094 | + * @return array | |
| 1095 | + */ | |
| 1096 | + public static function disable_unlicensed_actions( $actions ) { | |
| 1097 | + $lite_actions = array_fill_keys( self::get_lite_actions(), true ); | |
| 1098 | + | |
| 1099 | + foreach ( array_keys( $actions ) as $key ) { | |
| 1100 | + if ( isset( $lite_actions[ $key ] ) ) { | |
| 1101 | + continue; | |
| 1102 | + } | |
| 1103 | + | |
| 1104 | + add_filter( | |
| 1105 | + 'frm_' . $key . '_action_options', | |
| 1106 | + function ( $options ) { | |
| 1107 | + $options['active'] = false; | |
| 1108 | + | |
| 1109 | + if ( ! str_contains( $options['classes'], 'frm_show_upgrade' ) ) { | |
| 1110 | + $options['classes'] .= ' frm_show_upgrade'; | |
| 1111 | + } | |
| 1112 | + | |
| 1113 | + return $options; | |
| 1114 | + } | |
| 1115 | + ); | |
| 1116 | + }//end foreach | |
| 1117 | + | |
| 1118 | + return $actions; | |
| 1119 | + } | |
| 1120 | + | |
| 1121 | + /** | |
| 1122 | + * Get action keys that are available in Lite without a Pro license. | |
| 1123 | + * | |
| 1124 | + * @since 6.31 | |
| 1125 | + * | |
| 1126 | + * @return string[] | |
| 1127 | + */ | |
| 1128 | + public static function get_lite_actions() { | |
| 1129 | + return apply_filters( 'frm_lite_form_actions', array( 'on_submit', 'email', 'payment', 'stripe', 'square', 'paypal' ) ); | |
| 1130 | + } | |
| 1131 | + | |
| 1132 | + /** | |
| 1133 | + * Single source of truth for learn-more URL slugs used in | |
| 1134 | + * upgrade modals for non-Lite form actions. | |
| 1135 | + * | |
| 1136 | + * Slugs without '/' are KB doc slugs (knowledgebase/ prefix is added). | |
| 1137 | + * Slugs with '/' are direct paths (e.g. features/) used as-is. | |
| 1138 | + * | |
| 1139 | + * @since 6.31 | |
| 1140 | + * | |
| 1141 | + * @return array<string,string> Map of action_key => URL slug. | |
| 1142 | + */ | |
| 1143 | + public static function get_action_learn_more_links() { | |
| 1144 | + return array( | |
| 1145 | + 'wppost' => 'features/user-submitted-posts-wordpress-forms', | |
| 1146 | + 'register' => 'user-registration', | |
| 1147 | + 'paypal' => 'features/paypal-wordpress-payments', | |
| 1148 | + 'quiz' => 'quiz-maker-forms', | |
| 1149 | + 'quiz_outcome' => 'quiz-maker-forms', | |
| 1150 | + 'aweber' => 'features/aweber-addon', | |
| 1151 | + 'mailchimp' => 'features/mailchimp-addon', | |
| 1152 | + 'zapier' => 'features/form-entry-routing-with-zapier', | |
| 1153 | + 'twilio' => 'features/twilio-sms-form-notifications', | |
| 1154 | + 'activecampaign' => 'features/entries-to-activecampaign', | |
| 1155 | + 'salesforce' => 'features/form-entries-to-salesforce', | |
| 1156 | + 'constantcontact' => 'features/entries-to-constant-contact', | |
| 1157 | + 'getresponse' => 'features/form-entries-to-getresponse', | |
| 1158 | + 'hubspot' => 'features/form-entries-to-hubspot', | |
| 1159 | + 'mailpoet' => 'features/mailpoet-newsletters-addon', | |
| 1160 | + 'api' => 'features/wordpress-form-api', | |
| 1161 | + 'googlespreadsheet' => 'features/google-sheets', | |
| 1162 | + 'n8n' => 'features/connect-your-forms-to-any-app-with-n8n', | |
| 1163 | + 'convertkit' => 'features/convertkit', | |
| 1164 | + ); | |
| 1165 | + } | |
| 1166 | + | |
| 1167 | + /** | |
| 1168 | + * Look up the learn-more doc slug for a given action key. | |
| 1169 | + * | |
| 1170 | + * @since 6.31 | |
| 1171 | + * | |
| 1172 | + * @param string $action_key Action identifier (e.g. 'register'). | |
| 1173 | + * | |
| 1174 | + * @return string Doc slug or empty string. | |
| 1175 | + */ | |
| 1176 | + private static function get_learn_more_slug( $action_key ) { | |
| 1177 | + $links = self::get_action_learn_more_links(); | |
| 1178 | + return $links[ $action_key ] ?? ''; | |
| 803 | 1179 | } |
| 804 | 1180 | } |
| 805 | 1181 | |
| 806 | 1182 | class Frm_Form_Action_Factory { |