← All changes
|
includes/widgets/Form_Builder/Form_Builder.php
+424
-25
51.1.64
→
51.1.83
View file →
| @@ -85,9 +85,11 @@ | ||
| 85 | 85 | 'email' => 'Email', |
| 86 | 86 | 'redirect' => 'Redirect', |
| 87 | 87 | 'pro-sb' => 'Submission (Pro)', |
| 88 | 88 | 'pro-mch' => 'Mailchimp (Pro)', |
| 89 | - 'pro-wh' => 'Webhook (Pro)' | |
| 89 | + 'pro-wh' => 'Webhook (Pro)', | |
| 90 | + 'integrations' => 'Integrations (Pro)', | |
| 91 | + 'payment' => 'Payment (Pro)' | |
| 90 | 92 | ]; |
| 91 | 93 | } |
| 92 | 94 | |
| 93 | 95 | public function register_settings_section_submissions($widget) |
| @@ -155,8 +157,235 @@ | ||
| 155 | 157 | |
| 156 | 158 | $widget->end_controls_section(); |
| 157 | 159 | } |
| 158 | 160 | |
| 161 | + /** | |
| 162 | + * Settings for the Integrations action. | |
| 163 | + * | |
| 164 | + * Everything here is stored per form and read back on the server when a | |
| 165 | + * submission arrives; nothing travels through the browser. | |
| 166 | + * | |
| 167 | + * @param \Elementor\Widget_Base $widget Widget the controls belong to. | |
| 168 | + * | |
| 169 | + * @return void | |
| 170 | + */ | |
| 171 | + public function register_settings_section_integrations($widget) | |
| 172 | + { | |
| 173 | + if (!class_exists('King_Addons\\Form_Integrations')) { | |
| 174 | + return; | |
| 175 | + } | |
| 176 | + | |
| 177 | + $widget->start_controls_section( | |
| 178 | + $this->get_control_id('section_integrations'), | |
| 179 | + [ | |
| 180 | + 'label' => KING_ADDONS_ELEMENTOR_ICON . esc_html__('Integrations', 'king-addons'), | |
| 181 | + 'tab' => Controls_Manager::TAB_CONTENT, | |
| 182 | + 'condition' => [ | |
| 183 | + 'submit_actions' => 'integrations', | |
| 184 | + ], | |
| 185 | + ] | |
| 186 | + ); | |
| 187 | + | |
| 188 | + $widget->add_control( | |
| 189 | + 'integration_services', | |
| 190 | + [ | |
| 191 | + 'label' => esc_html__('Send to', 'king-addons'), | |
| 192 | + 'type' => Controls_Manager::SELECT2, | |
| 193 | + 'multiple' => true, | |
| 194 | + 'label_block' => true, | |
| 195 | + 'options' => Form_Integrations::services(), | |
| 196 | + 'render_type' => 'none', | |
| 197 | + ] | |
| 198 | + ); | |
| 199 | + | |
| 200 | + $widget->add_control( | |
| 201 | + 'integration_email_field', | |
| 202 | + [ | |
| 203 | + 'label' => esc_html__('Email field ID', 'king-addons'), | |
| 204 | + 'type' => Controls_Manager::TEXT, | |
| 205 | + 'description' => esc_html__('Which field holds the address. Left empty, the first value that looks like an email is used.', 'king-addons'), | |
| 206 | + 'render_type' => 'none', | |
| 207 | + ] | |
| 208 | + ); | |
| 209 | + | |
| 210 | + $widget->add_control( | |
| 211 | + 'integration_name_field', | |
| 212 | + [ | |
| 213 | + 'label' => esc_html__('Name field ID', 'king-addons'), | |
| 214 | + 'type' => Controls_Manager::TEXT, | |
| 215 | + 'render_type' => 'none', | |
| 216 | + ] | |
| 217 | + ); | |
| 218 | + | |
| 219 | + $text_controls = [ | |
| 220 | + ['slack_webhook_url', esc_html__('Slack webhook URL', 'king-addons'), 'slack', 'https://hooks.slack.com/services/…'], | |
| 221 | + ['slack_title', esc_html__('Slack message title', 'king-addons'), 'slack', ''], | |
| 222 | + ['telegram_bot_token', esc_html__('Telegram bot token', 'king-addons'), 'telegram', ''], | |
| 223 | + ['telegram_chat_id', esc_html__('Telegram chat ID', 'king-addons'), 'telegram', ''], | |
| 224 | + ['brevo_api_key', esc_html__('Brevo API key', 'king-addons'), 'brevo', ''], | |
| 225 | + ['brevo_list_id', esc_html__('Brevo list ID', 'king-addons'), 'brevo', ''], | |
| 226 | + ['convertkit_api_key', esc_html__('ConvertKit API key', 'king-addons'), 'convertkit', ''], | |
| 227 | + ['convertkit_form_id', esc_html__('ConvertKit form ID', 'king-addons'), 'convertkit', ''], | |
| 228 | + ['activecampaign_url', esc_html__('ActiveCampaign API URL', 'king-addons'), 'activecampaign', 'https://your-account.api-us1.com'], | |
| 229 | + ['activecampaign_api_key', esc_html__('ActiveCampaign API key', 'king-addons'), 'activecampaign', ''], | |
| 230 | + ['activecampaign_list_id', esc_html__('ActiveCampaign list ID', 'king-addons'), 'activecampaign', ''], | |
| 231 | + ['hubspot_token', esc_html__('HubSpot private app token', 'king-addons'), 'hubspot', ''], | |
| 232 | + ['klaviyo_api_key', esc_html__('Klaviyo private API key', 'king-addons'), 'klaviyo', ''], | |
| 233 | + ['klaviyo_list_id', esc_html__('Klaviyo list ID', 'king-addons'), 'klaviyo', ''], | |
| 234 | + ['google_sheets_url', esc_html__('Apps Script web app URL', 'king-addons'), 'google_sheets', 'https://script.google.com/macros/s/…/exec'], | |
| 235 | + ['google_sheets_secret', esc_html__('Shared secret', 'king-addons'), 'google_sheets', ''], | |
| 236 | + ]; | |
| 237 | + | |
| 238 | + foreach ($text_controls as $control) { | |
| 239 | + list($name, $label, $service, $placeholder) = $control; | |
| 240 | + | |
| 241 | + $widget->add_control( | |
| 242 | + $name, | |
| 243 | + [ | |
| 244 | + 'label' => $label, | |
| 245 | + 'type' => Controls_Manager::TEXT, | |
| 246 | + 'label_block' => true, | |
| 247 | + 'placeholder' => $placeholder, | |
| 248 | + 'render_type' => 'none', | |
| 249 | + 'ai' => ['active' => false], | |
| 250 | + 'condition' => ['integration_services' => $service], | |
| 251 | + ] | |
| 252 | + ); | |
| 253 | + } | |
| 254 | + | |
| 255 | + $widget->add_control( | |
| 256 | + 'google_sheets_notice', | |
| 257 | + [ | |
| 258 | + 'type' => Controls_Manager::RAW_HTML, | |
| 259 | + 'raw' => esc_html__('Google Sheets is reached through a Google Apps Script web app bound to your sheet: Google\'s own API needs an OAuth flow a form widget cannot run.', 'king-addons'), | |
| 260 | + 'content_classes' => 'elementor-panel-alert elementor-panel-alert-info', | |
| 261 | + 'condition' => ['integration_services' => 'google_sheets'], | |
| 262 | + ] | |
| 263 | + ); | |
| 264 | + | |
| 265 | + $widget->end_controls_section(); | |
| 266 | + } | |
| 267 | + | |
| 268 | + /** | |
| 269 | + * Settings for the Payment action. | |
| 270 | + * | |
| 271 | + * @param \Elementor\Widget_Base $widget Widget the controls belong to. | |
| 272 | + * | |
| 273 | + * @return void | |
| 274 | + */ | |
| 275 | + public function register_settings_section_payment($widget) | |
| 276 | + { | |
| 277 | + if (!class_exists('King_Addons\\Form_Payments')) { | |
| 278 | + return; | |
| 279 | + } | |
| 280 | + | |
| 281 | + $widget->start_controls_section( | |
| 282 | + $this->get_control_id('section_payment'), | |
| 283 | + [ | |
| 284 | + 'label' => KING_ADDONS_ELEMENTOR_ICON . esc_html__('Payment', 'king-addons'), | |
| 285 | + 'tab' => Controls_Manager::TAB_CONTENT, | |
| 286 | + 'condition' => [ | |
| 287 | + 'submit_actions' => 'payment', | |
| 288 | + ], | |
| 289 | + ] | |
| 290 | + ); | |
| 291 | + | |
| 292 | + $widget->add_control( | |
| 293 | + 'payment_provider', | |
| 294 | + [ | |
| 295 | + 'label' => esc_html__('Provider', 'king-addons'), | |
| 296 | + 'type' => Controls_Manager::SELECT, | |
| 297 | + 'options' => Form_Payments::providers(), | |
| 298 | + 'default' => 'none', | |
| 299 | + 'render_type' => 'none', | |
| 300 | + 'description' => esc_html__('Keys live in King Addons settings. The visitor pays on the provider\'s own page, so no card details reach this site.', 'king-addons'), | |
| 301 | + ] | |
| 302 | + ); | |
| 303 | + | |
| 304 | + $widget->add_control( | |
| 305 | + 'payment_currency', | |
| 306 | + [ | |
| 307 | + 'label' => esc_html__('Currency', 'king-addons'), | |
| 308 | + 'type' => Controls_Manager::TEXT, | |
| 309 | + 'default' => 'USD', | |
| 310 | + 'render_type' => 'none', | |
| 311 | + 'condition' => ['payment_provider!' => 'none'], | |
| 312 | + ] | |
| 313 | + ); | |
| 314 | + | |
| 315 | + $widget->add_control( | |
| 316 | + 'payment_fixed_amount', | |
| 317 | + [ | |
| 318 | + 'label' => esc_html__('Fixed amount', 'king-addons'), | |
| 319 | + 'type' => Controls_Manager::NUMBER, | |
| 320 | + 'min' => 0, | |
| 321 | + 'step' => 0.01, | |
| 322 | + 'render_type' => 'none', | |
| 323 | + 'condition' => ['payment_provider!' => 'none'], | |
| 324 | + ] | |
| 325 | + ); | |
| 326 | + | |
| 327 | + $widget->add_control( | |
| 328 | + 'payment_amount_field', | |
| 329 | + [ | |
| 330 | + 'label' => esc_html__('Or a calculation field ID', 'king-addons'), | |
| 331 | + 'type' => Controls_Manager::TEXT, | |
| 332 | + 'render_type' => 'none', | |
| 333 | + 'description' => esc_html__('The server works the total out again from that field\'s formula; the number the browser shows is never trusted.', 'king-addons'), | |
| 334 | + 'condition' => ['payment_provider!' => 'none'], | |
| 335 | + ] | |
| 336 | + ); | |
| 337 | + | |
| 338 | + $widget->add_control( | |
| 339 | + 'payment_max_amount', | |
| 340 | + [ | |
| 341 | + 'label' => esc_html__('Refuse above', 'king-addons'), | |
| 342 | + 'type' => Controls_Manager::NUMBER, | |
| 343 | + 'min' => 0, | |
| 344 | + 'step' => 0.01, | |
| 345 | + 'render_type' => 'none', | |
| 346 | + 'description' => esc_html__('A ceiling for calculated totals. 0 means no ceiling.', 'king-addons'), | |
| 347 | + 'condition' => ['payment_provider!' => 'none'], | |
| 348 | + ] | |
| 349 | + ); | |
| 350 | + | |
| 351 | + $widget->add_control( | |
| 352 | + 'payment_description', | |
| 353 | + [ | |
| 354 | + 'label' => esc_html__('What is being paid for', 'king-addons'), | |
| 355 | + 'type' => Controls_Manager::TEXT, | |
| 356 | + 'label_block' => true, | |
| 357 | + 'render_type' => 'none', | |
| 358 | + 'condition' => ['payment_provider!' => 'none'], | |
| 359 | + ] | |
| 360 | + ); | |
| 361 | + | |
| 362 | + $widget->add_control( | |
| 363 | + 'payment_success_url', | |
| 364 | + [ | |
| 365 | + 'label' => esc_html__('After paying', 'king-addons'), | |
| 366 | + 'type' => Controls_Manager::TEXT, | |
| 367 | + 'label_block' => true, | |
| 368 | + 'render_type' => 'none', | |
| 369 | + 'description' => esc_html__('A page on this site. Anything else is ignored.', 'king-addons'), | |
| 370 | + 'condition' => ['payment_provider!' => 'none'], | |
| 371 | + ] | |
| 372 | + ); | |
| 373 | + | |
| 374 | + $widget->add_control( | |
| 375 | + 'payment_cancel_url', | |
| 376 | + [ | |
| 377 | + 'label' => esc_html__('If they cancel', 'king-addons'), | |
| 378 | + 'type' => Controls_Manager::TEXT, | |
| 379 | + 'label_block' => true, | |
| 380 | + 'render_type' => 'none', | |
| 381 | + 'condition' => ['payment_provider!' => 'none'], | |
| 382 | + ] | |
| 383 | + ); | |
| 384 | + | |
| 385 | + $widget->end_controls_section(); | |
| 386 | + } | |
| 387 | + | |
| 159 | 388 | public function register_settings_section_email($widget) |
| 160 | 389 | { |
| 161 | 390 | $widget->start_controls_section( |
| 162 | 391 | $this->get_control_id('section_email'), |
| @@ -515,8 +744,10 @@ | ||
| 515 | 744 | } |
| 516 | 745 | |
| 517 | 746 | public $last_prev_btn_text; |
| 518 | 747 | |
| 748 | + public $last_next_btn_text; | |
| 749 | + | |
| 519 | 750 | protected function register_controls() |
| 520 | 751 | { |
| 521 | 752 | |
| 522 | 753 | $this->start_controls_section( |
| @@ -548,8 +779,14 @@ | ||
| 548 | 779 | 'hidden' => esc_html__('Hidden Field', 'king-addons'), |
| 549 | 780 | 'king-addons-fb-step' => esc_html__('Step', 'king-addons'), |
| 550 | 781 | ]; |
| 551 | 782 | |
| 783 | + // Range, rating, signature and acceptance: controls and markup live in | |
| 784 | + // helpers/Extra_Fields.php. | |
| 785 | + if (class_exists('King_Addons\\Form_Extra_Fields')) { | |
| 786 | + $field_types = array_merge($field_types, Form_Extra_Fields::field_types()); | |
| 787 | + } | |
| 788 | + | |
| 552 | 789 | $repeater->add_control( |
| 553 | 790 | 'field_type', |
| 554 | 791 | [ |
| 555 | 792 | 'label' => esc_html__('Type', 'king-addons'), |
| @@ -570,8 +807,16 @@ | ||
| 570 | 807 | ] |
| 571 | 808 | ] |
| 572 | 809 | ); |
| 573 | 810 | |
| 811 | + if (class_exists('King_Addons\\Form_Extra_Fields')) { | |
| 812 | + Form_Extra_Fields::register_controls($repeater); | |
| 813 | + } | |
| 814 | + | |
| 815 | + if (class_exists('King_Addons\\Form_Conditional_Logic')) { | |
| 816 | + Form_Conditional_Logic::register_controls($repeater); | |
| 817 | + } | |
| 818 | + | |
| 574 | 819 | if ('' == get_option('king_addons_recaptcha_v3_site_key')) { |
| 575 | 820 | $repeater->add_control( |
| 576 | 821 | 'recaptcha_key_notice', |
| 577 | 822 | [ |
| @@ -1408,8 +1653,35 @@ | ||
| 1408 | 1653 | ] |
| 1409 | 1654 | ); |
| 1410 | 1655 | } |
| 1411 | 1656 | |
| 1657 | + if (class_exists('King_Addons\\Form_Partial_Entries')) { | |
| 1658 | + $this->add_control( | |
| 1659 | + 'partial_entries', | |
| 1660 | + [ | |
| 1661 | + 'label' => esc_html__('Save partial entries', 'king-addons'), | |
| 1662 | + 'type' => Controls_Manager::SWITCHER, | |
| 1663 | + 'return_value' => 'yes', | |
| 1664 | + 'separator' => 'before', | |
| 1665 | + 'description' => esc_html__('Keeps what a visitor typed even if they never press submit. Drafts appear alongside submissions and are removed after 30 days.', 'king-addons'), | |
| 1666 | + ] | |
| 1667 | + ); | |
| 1668 | + } | |
| 1669 | + | |
| 1670 | + if (class_exists('King_Addons\\Form_Spam_Protection')) { | |
| 1671 | + $this->add_control( | |
| 1672 | + 'spam_protection', | |
| 1673 | + [ | |
| 1674 | + 'label' => esc_html__('Spam protection', 'king-addons'), | |
| 1675 | + 'type' => Controls_Manager::SELECT, | |
| 1676 | + 'options' => Form_Spam_Protection::modes(), | |
| 1677 | + 'default' => 'none', | |
| 1678 | + 'separator' => 'before', | |
| 1679 | + 'description' => esc_html__('Honeypot needs no account. Turnstile and hCaptcha need their keys in King Addons settings.', 'king-addons'), | |
| 1680 | + ] | |
| 1681 | + ); | |
| 1682 | + } | |
| 1683 | + | |
| 1412 | 1684 | $this->end_controls_section(); |
| 1413 | 1685 | |
| 1414 | 1686 | $this->register_settings_section_submissions($this); |
| 1415 | 1687 | |
| @@ -1416,8 +1688,12 @@ | ||
| 1416 | 1688 | $this->register_settings_section_email($this); |
| 1417 | 1689 | |
| 1418 | 1690 | $this->register_settings_section_webhook($this); |
| 1419 | 1691 | |
| 1692 | + $this->register_settings_section_integrations($this); | |
| 1693 | + | |
| 1694 | + $this->register_settings_section_payment($this); | |
| 1695 | + | |
| 1420 | 1696 | $this->register_settings_section_redirect($this); |
| 1421 | 1697 | |
| 1422 | 1698 | $this->register_settings_section_mailchimp(); |
| 1423 | 1699 | |
| @@ -3277,16 +3553,39 @@ | ||
| 3277 | 3553 | } |
| 3278 | 3554 | |
| 3279 | 3555 | public function get_attribute_name($item) |
| 3280 | 3556 | { |
| 3281 | - return "form_fields[{$item['field_id']}]"; | |
| 3557 | + return 'form_fields[' . self::resolve_field_key($item) . ']'; | |
| 3282 | 3558 | } |
| 3283 | 3559 | |
| 3284 | 3560 | public function get_attribute_id($item) |
| 3285 | 3561 | { |
| 3562 | + return 'form-field-' . self::resolve_field_key($item); | |
| 3563 | + } | |
| 3286 | 3564 | |
| 3287 | - $id_suffix = !empty($item['field_id']) ? $item['field_id'] : $item['_id']; | |
| 3288 | - return 'form-field-' . $id_suffix; | |
| 3565 | + /** | |
| 3566 | + * Field ID used as the submitted name/key. | |
| 3567 | + * | |
| 3568 | + * An empty Field ID used to produce name="form_fields[]", which PHP cannot | |
| 3569 | + * map back onto the field. Fall back to Elementor's repeater _id so imported | |
| 3570 | + * and programmatic forms still keep their values. | |
| 3571 | + * | |
| 3572 | + * @param array<string,mixed> $item Repeater row. | |
| 3573 | + * | |
| 3574 | + * @return string | |
| 3575 | + */ | |
| 3576 | + public static function resolve_field_key($item): string | |
| 3577 | + { | |
| 3578 | + if (!is_array($item)) { | |
| 3579 | + return ''; | |
| 3580 | + } | |
| 3581 | + | |
| 3582 | + $id = isset($item['field_id']) ? trim((string) $item['field_id']) : ''; | |
| 3583 | + if ('' !== $id) { | |
| 3584 | + return $id; | |
| 3585 | + } | |
| 3586 | + | |
| 3587 | + return isset($item['_id']) ? trim((string) $item['_id']) : ''; | |
| 3289 | 3588 | } |
| 3290 | 3589 | |
| 3291 | 3590 | protected function make_textarea_field($item, $item_index) |
| 3292 | 3591 | { |
| @@ -3366,9 +3665,10 @@ | ||
| 3366 | 3665 | <!--suppress HtmlFormInputWithoutLabel --> |
| 3367 | 3666 | <select <?php $this->print_render_attribute_string('select' . $i); ?>> |
| 3368 | 3667 | <?php |
| 3369 | 3668 | foreach ($options as $key => $option) : |
| 3370 | - $option_id = $item['field_id'] . $key; | |
| 3669 | + $field_key = self::resolve_field_key($item); | |
| 3670 | + $option_id = $field_key . $key; | |
| 3371 | 3671 | $option_value = esc_attr($option); |
| 3372 | 3672 | $option_label = esc_html($option); |
| 3373 | 3673 | |
| 3374 | 3674 | if (false !== strpos($option, '|')) { |
| @@ -3404,9 +3704,10 @@ | ||
| 3404 | 3704 | $html = ''; |
| 3405 | 3705 | if ($options) { |
| 3406 | 3706 | $html .= '<div class="king-addons-field-sub-group ' . esc_attr($item['css_classes']) . ' ' . esc_attr($item['inline_list']) . '">'; |
| 3407 | 3707 | foreach ($options as $key => $option) { |
| 3408 | - $element_id = ($item['field_id'] ? esc_attr($item['field_id']) : $item['field_type']) . $key; | |
| 3708 | + $field_key = self::resolve_field_key($item); | |
| 3709 | + $element_id = ($field_key ? esc_attr($field_key) : $item['field_type']) . $key; | |
| 3409 | 3710 | $html_id = $this->get_attribute_id($item) . '-' . $key; |
| 3410 | 3711 | $option_label = $option; |
| 3411 | 3712 | $option_value = $option; |
| 3412 | 3713 | |
| @@ -3431,9 +3732,9 @@ | ||
| 3431 | 3732 | if ($item['required'] && ('radio' === $type || 'checkbox' === $type)) { |
| 3432 | 3733 | $this->add_required_attribute($element_id); |
| 3433 | 3734 | } |
| 3434 | 3735 | |
| 3435 | - $html .= '<span class="king-addons-form-field-option" data-key="form-field-' . esc_attr($item['field_id']) . '"><input ' . $this->get_render_attribute_string($element_id) . '> <label for="' . esc_attr($html_id) . '">' . esc_html($option_label) . '</label></span>'; | |
| 3736 | + $html .= '<span class="king-addons-form-field-option" data-key="form-field-' . esc_attr(self::resolve_field_key($item)) . '"><input ' . $this->get_render_attribute_string($element_id) . '> <label for="' . esc_attr($html_id) . '">' . esc_html($option_label) . '</label></span>'; | |
| 3436 | 3737 | } |
| 3437 | 3738 | $html .= '</div>'; |
| 3438 | 3739 | } |
| 3439 | 3740 | |
| @@ -3448,9 +3749,9 @@ | ||
| 3448 | 3749 | 'class' => [ |
| 3449 | 3750 | 'king-addons-form-field-type-' . $item['field_type'], |
| 3450 | 3751 | 'king-addons-field-group', |
| 3451 | 3752 | 'king-addons-column', |
| 3452 | - 'king-addons-field-group-' . esc_attr($item['field_id']), | |
| 3753 | + 'king-addons-field-group-' . esc_attr(self::resolve_field_key($item)), | |
| 3453 | 3754 | ], |
| 3454 | 3755 | ], |
| 3455 | 3756 | 'input' . $i => [ |
| 3456 | 3757 | 'type' => ('acceptance' === $item['field_type']) ? 'checkbox' : (('upload' === $item['field_type']) ? 'file' : $item['field_type']), |
| @@ -3479,9 +3780,18 @@ | ||
| 3479 | 3780 | |
| 3480 | 3781 | |
| 3481 | 3782 | $this->add_render_attribute('field-group' . $i, 'class', 'elementor-repeater-item-' . esc_attr($item['_id'])); |
| 3482 | 3783 | |
| 3784 | + // Conditional logic travels to the browser as one data attribute; the | |
| 3785 | + // script evaluates it on every change in the form. | |
| 3786 | + if (class_exists('King_Addons\\Form_Conditional_Logic')) { | |
| 3787 | + $condition = Form_Conditional_Logic::build($item); | |
| 3788 | + if (null !== $condition) { | |
| 3789 | + $this->add_render_attribute('field-group' . $i, 'data-ka-cond', wp_json_encode($condition)); | |
| 3790 | + } | |
| 3791 | + } | |
| 3483 | 3792 | |
| 3793 | + | |
| 3484 | 3794 | if ('true' == $instance['show_placeholders'] && !Utils::is_empty($item['placeholder'])) { |
| 3485 | 3795 | $this->add_render_attribute('input' . $i, 'placeholder', $item['placeholder']); |
| 3486 | 3796 | } |
| 3487 | 3797 | |
| @@ -3505,10 +3815,10 @@ | ||
| 3505 | 3815 | |
| 3506 | 3816 | private function render_form_icon($settings) |
| 3507 | 3817 | { ?> |
| 3508 | 3818 | <span <?php echo $this->get_render_attribute_string('icon-align'); ?>> |
| 3509 | - <?php Icons_Manager::render_icon($settings['selected_button_icon'], ['aria-hidden' => 'true']); ?> | |
| 3510 | - <?php if (empty($instance['button_text'])) : ?> | |
| 3819 | + <?php Icons_Manager::render_icon($settings['selected_button_icon'], ['aria-hidden' => 'true']); ?> | |
| 3820 | + <?php if (empty($settings['button_text'])) : ?> | |
| 3511 | 3821 | <span class="king-addons-hidden-element"><?php echo esc_html__('Submit', 'king-addons'); ?></span> |
| 3512 | 3822 | <?php endif; ?> |
| 3513 | 3823 | </span> |
| 3514 | 3824 | <?php } |
| @@ -3557,8 +3867,41 @@ | ||
| 3557 | 3867 | update_option('king_addons_referrer_' . $this->get_id(), home_url($_SERVER['REQUEST_URI'])); |
| 3558 | 3868 | update_option('king_addons_referrer_title_' . $this->get_id(), get_the_title($post->ID)); |
| 3559 | 3869 | update_option('king_addons_webhook_url_' . $this->get_id(), $instance['webhook_url']); |
| 3560 | 3870 | |
| 3871 | + if (class_exists('King_Addons\\Form_Integrations')) { | |
| 3872 | + Form_Integrations::save_settings($this->get_id(), $instance); | |
| 3873 | + } | |
| 3874 | + | |
| 3875 | + if (class_exists('King_Addons\\Form_Payments')) { | |
| 3876 | + Form_Payments::save_settings( | |
| 3877 | + $this->get_id(), | |
| 3878 | + $instance, | |
| 3879 | + isset($post->ID) ? (int) $post->ID : (int) get_the_ID() | |
| 3880 | + ); | |
| 3881 | + } | |
| 3882 | + | |
| 3883 | + $partial_entries = false; | |
| 3884 | + if (class_exists('King_Addons\\Form_Partial_Entries')) { | |
| 3885 | + $partial_entries = 'yes' === ($instance['partial_entries'] ?? '') | |
| 3886 | + && king_addons_freemius()->can_use_premium_code__premium_only(); | |
| 3887 | + Form_Partial_Entries::save_setting($this->get_id(), $partial_entries); | |
| 3888 | + } | |
| 3889 | + | |
| 3890 | + // The submit actions are separate AJAX endpoints, so the challenge has | |
| 3891 | + // to be checked there; they read it back by form id. | |
| 3892 | + $spam_mode = 'none'; | |
| 3893 | + if (class_exists('King_Addons\\Form_Spam_Protection')) { | |
| 3894 | + $spam_mode = (string) ($instance['spam_protection'] ?? 'none'); | |
| 3895 | + Form_Spam_Protection::save_mode($this->get_id(), $spam_mode); | |
| 3896 | + $spam_mode = Form_Spam_Protection::get_mode($this->get_id()); | |
| 3897 | + | |
| 3898 | + $spam_script = Form_Spam_Protection::script($spam_mode); | |
| 3899 | + if (null !== $spam_script) { | |
| 3900 | + wp_enqueue_script($spam_script['handle'], $spam_script['src'], [], null, true); | |
| 3901 | + } | |
| 3902 | + } | |
| 3903 | + | |
| 3561 | 3904 | $emailField = isset($instance['email_field']) ? $instance['email_field'] : ''; |
| 3562 | 3905 | $firstNameField = isset($instance['first_name_field']) ? $instance['first_name_field'] : ''; |
| 3563 | 3906 | $lastNameField = isset($instance['last_name_field']) ? $instance['last_name_field'] : ''; |
| 3564 | 3907 | $addressField = isset($instance['address_field']) ? $instance['address_field'] : ''; |
| @@ -3575,10 +3918,18 @@ | ||
| 3575 | 3918 | 'birthday_field' => $birthdayField, |
| 3576 | 3919 | 'group_id' => $groupId |
| 3577 | 3920 | ]; |
| 3578 | 3921 | |
| 3579 | - $submit_actions = array_filter($instance['submit_actions'], function ($value) { | |
| 3580 | - return $value !== 'pro-sb' && $value !== 'pro-mch' && $value !== 'pro-wh'; | |
| 3922 | + // The three original Pro actions are placeholders in the free build; | |
| 3923 | + // Integrations is a real action, so it is only dropped when Pro is off. | |
| 3924 | + $stripped_actions = ['pro-sb', 'pro-mch', 'pro-wh']; | |
| 3925 | + if (!king_addons_freemius()->can_use_premium_code__premium_only()) { | |
| 3926 | + $stripped_actions[] = 'integrations'; | |
| 3927 | + $stripped_actions[] = 'payment'; | |
| 3928 | + } | |
| 3929 | + | |
| 3930 | + $submit_actions = array_filter($instance['submit_actions'], function ($value) use ($stripped_actions) { | |
| 3931 | + return !in_array($value, $stripped_actions, true); | |
| 3581 | 3932 | }); |
| 3582 | 3933 | $submit_actions = array_values($submit_actions); |
| 3583 | 3934 | |
| 3584 | 3935 | $this->add_render_attribute( |
| @@ -3601,8 +3952,11 @@ | ||
| 3601 | 3952 | ], |
| 3602 | 3953 | 'data-redirect-url' => [ |
| 3603 | 3954 | in_array('redirect', $submit_actions) ? esc_url($instance['redirect_to']) : '' |
| 3604 | 3955 | ], |
| 3956 | + 'data-partial-entries' => [ | |
| 3957 | + $partial_entries ? '1' : '' | |
| 3958 | + ], | |
| 3605 | 3959 | 'data-mailchimp-fields' => [ |
| 3606 | 3960 | json_encode($fieldsArray) |
| 3607 | 3961 | ], |
| 3608 | 3962 | 'data-list-id' => [ |
| @@ -3738,22 +4092,24 @@ | ||
| 3738 | 4092 | |
| 3739 | 4093 | $print_label = !in_array($item['field_type'], ['hidden', 'html', 'king-addons-fb-step'], true); |
| 3740 | 4094 | |
| 3741 | 4095 | if ('king-addons-fb-step' === $item['field_type']) { |
| 3742 | - if (isset($item['previous_button_text'])) { | |
| 3743 | - $this->last_prev_btn_text = $item['previous_button_text']; | |
| 3744 | - } | |
| 3745 | - | |
| 3746 | 4096 | if (0 === $step_count) { |
| 3747 | 4097 | echo '<div class="king-addons-fb-step-tab king-addons-fb-step-tab-hidden">'; |
| 3748 | 4098 | } else { |
| 3749 | 4099 | echo '<div class="king-addons-step-buttons-wrap">'; |
| 3750 | - echo '<button type="button" class="king-addons-fb-step-prev">' . esc_html($item['previous_button_text']) . '</button>'; | |
| 3751 | - echo '<button type="button" class="king-addons-fb-step-next">' . esc_html($item['next_button_text']) . '</button>'; | |
| 4100 | + echo '<button type="button" class="king-addons-fb-step-prev">' . esc_html((string) $this->last_prev_btn_text) . '</button>'; | |
| 4101 | + echo '<button type="button" class="king-addons-fb-step-next">' . esc_html((string) $this->last_next_btn_text) . '</button>'; | |
| 3752 | 4102 | echo '</div>'; |
| 3753 | 4103 | echo '</div>'; |
| 3754 | 4104 | echo '<div class="king-addons-fb-step-tab king-addons-fb-step-tab-hidden">'; |
| 3755 | 4105 | } |
| 4106 | + $this->last_next_btn_text = (isset($item['next_button_text']) && '' !== $item['next_button_text']) | |
| 4107 | + ? $item['next_button_text'] | |
| 4108 | + : esc_html__('Next', 'king-addons'); | |
| 4109 | + $this->last_prev_btn_text = (isset($item['previous_button_text']) && '' !== $item['previous_button_text']) | |
| 4110 | + ? $item['previous_button_text'] | |
| 4111 | + : esc_html__('Previous', 'king-addons'); | |
| 3756 | 4112 | $step_count++; |
| 3757 | 4113 | } |
| 3758 | 4114 | |
| 3759 | 4115 | ?> |
| @@ -3787,9 +4143,23 @@ | ||
| 3787 | 4143 | |
| 3788 | 4144 | echo $this->make_radio_checkbox_field($item, $item_index, $item['field_type']); |
| 3789 | 4145 | break; |
| 3790 | 4146 | case 'recaptcha-v3': |
| 3791 | - echo '<input type="hidden" id="g-recaptcha-response" name="g-recaptcha-response" data-site-key="' . esc_attr(get_option('king_addons_recaptcha_v3_site_key')) . '" />'; | |
| 4147 | + $recaptcha_v3_site_key = (string) get_option('king_addons_recaptcha_v3_site_key', ''); | |
| 4148 | + if ('' === $recaptcha_v3_site_key) { | |
| 4149 | + if (current_user_can('manage_options')) { | |
| 4150 | + echo '<p class="king-addons-form-captcha-notice">' . sprintf( | |
| 4151 | + /* translators: 1: provider name, 2: settings URL. */ | |
| 4152 | + esc_html__('%1$s is selected but its keys are missing. Add them under %2$s.', 'king-addons'), | |
| 4153 | + esc_html__('reCAPTCHA v3', 'king-addons'), | |
| 4154 | + '<a href="' . esc_url(admin_url('admin.php?page=king-addons-settings')) . '">' | |
| 4155 | + . esc_html__('King Addons settings', 'king-addons') . '</a>' | |
| 4156 | + ) . '</p>'; | |
| 4157 | + } | |
| 4158 | + } else { | |
| 4159 | + echo '<input type="hidden" id="g-recaptcha-response" name="g-recaptcha-response" data-site-key="' . esc_attr($recaptcha_v3_site_key) . '" />'; | |
| 4160 | + } | |
| 4161 | + break; | |
| 3792 | 4162 | case 'text': |
| 3793 | 4163 | case 'email': |
| 3794 | 4164 | case 'url': |
| 3795 | 4165 | case 'tel': |
| @@ -3827,14 +4197,13 @@ | ||
| 3827 | 4197 | |
| 3828 | 4198 | if (!empty($non_whitelisted)) { |
| 3829 | 4199 | $item['file_types'] = 'jpg,jpeg,png,gif,pdf,doc,docx,ppt,pptx,odt,avi,ogg,m4a,mov,mp3,mp4,mpg,wav,wmv,txt'; |
| 3830 | 4200 | if (is_admin()) { |
| 3831 | - echo '<br>'; | |
| 3832 | - echo '<ul class="king-addons-file-type-error">'; | |
| 3833 | - echo esc_html__('Please remove unsupported file type(s):', 'king-addons'); | |
| 4201 | + echo '<p class="king-addons-file-type-error">' . esc_html__('Please remove unsupported file type(s):', 'king-addons') . '</p>'; | |
| 4202 | + echo '<ul class="king-addons-file-type-error-list">'; | |
| 3834 | 4203 | foreach ($non_whitelisted as $type) { |
| 3835 | 4204 | if (!empty($type)) { |
| 3836 | - echo '<li>' . esc_html($type) . ' <li/>'; | |
| 4205 | + echo '<li>' . esc_html($type) . '</li>'; | |
| 3837 | 4206 | } |
| 3838 | 4207 | } |
| 3839 | 4208 | echo '</ul>'; |
| 3840 | 4209 | } |
| @@ -3849,10 +4218,36 @@ | ||
| 3849 | 4218 | } |
| 3850 | 4219 | |
| 3851 | 4220 | echo '<input size="1 "' . $this->get_render_attribute_string('input' . $item_index) . '>'; |
| 3852 | 4221 | break; |
| 4222 | + case 'range': | |
| 4223 | + $this->add_render_attribute('input' . $item_index, [ | |
| 4224 | + 'class' => 'king-addons-form-field-range', | |
| 4225 | + 'min' => isset($item['range_min']) && '' !== $item['range_min'] ? (string) (float) $item['range_min'] : '0', | |
| 4226 | + 'max' => isset($item['range_max']) && '' !== $item['range_max'] ? (string) (float) $item['range_max'] : '100', | |
| 4227 | + 'step' => isset($item['range_step']) && '' !== $item['range_step'] && (float) $item['range_step'] > 0 ? (string) (float) $item['range_step'] : '1', | |
| 4228 | + ]); | |
| 4229 | + echo '<div class="king-addons-form-range">'; | |
| 4230 | + echo '<input ' . $this->get_render_attribute_string('input' . $item_index) . '>'; | |
| 4231 | + if ('yes' === ($item['range_show_value'] ?? 'yes')) { | |
| 4232 | + echo '<output class="king-addons-form-range__value"></output>'; | |
| 4233 | + } | |
| 4234 | + echo '</div>'; | |
| 4235 | + break; | |
| 4236 | + case 'rating': | |
| 4237 | + case 'signature': | |
| 4238 | + case 'acceptance': | |
| 4239 | + case 'calculation': | |
| 4240 | + if (class_exists('King_Addons\\Form_Extra_Fields')) { | |
| 4241 | + echo Form_Extra_Fields::render( | |
| 4242 | + $item, | |
| 4243 | + $this->get_attribute_name($item), | |
| 4244 | + $this->get_attribute_id($item) | |
| 4245 | + ); | |
| 4246 | + } | |
| 4247 | + break; | |
| 3853 | 4248 | case 'king-addons-fb-step': |
| 3854 | - echo '<input type="hidden" class="king-addons-fb-step-input" id="form-field-' . esc_attr($item['field_id']) . '" value="' . esc_attr($item['field_label']) . '">'; | |
| 4249 | + echo '<input type="hidden" class="king-addons-fb-step-input" id="form-field-' . esc_attr(self::resolve_field_key($item)) . '" value="' . esc_attr($item['field_label']) . '">'; | |
| 3855 | 4250 | break; |
| 3856 | 4251 | default: |
| 3857 | 4252 | $field_type = $item['field_type']; |
| 3858 | 4253 | endswitch; |
| @@ -3860,12 +4255,16 @@ | ||
| 3860 | 4255 | </div> |
| 3861 | 4256 | <?php |
| 3862 | 4257 | endforeach; |
| 3863 | 4258 | |
| 4259 | + if ('none' !== $spam_mode && class_exists('King_Addons\\Form_Spam_Protection')) { | |
| 4260 | + echo Form_Spam_Protection::render($spam_mode); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped | |
| 4261 | + } | |
| 4262 | + | |
| 3864 | 4263 | echo '<div ' . $this->get_render_attribute_string('submit-group') . '>'; |
| 3865 | 4264 | if ('exists' === $step_exists) { |
| 3866 | 4265 | if (2 <= $step_count) { |
| 3867 | - echo '<button type="button" class="king-addons-fb-step-prev">' . $this->last_prev_btn_text . '</button>'; | |
| 4266 | + echo '<button type="button" class="king-addons-fb-step-prev">' . esc_html((string) $this->last_prev_btn_text) . '</button>'; | |
| 3868 | 4267 | } |
| 3869 | 4268 | |
| 3870 | 4269 | $this->render_submit_button($instance); |
| 3871 | 4270 | |