dvadf
File manager - Edit - /home/centroca/public_html/Forms.tar
Back
IconChoices.php 0000644 00000036545 15252506741 0007467 0 ustar 00 <?php namespace WPForms\Forms; use WPForms\Helpers\PluginSilentUpgrader; use WPForms_Builder; use WP_Ajax_Upgrader_Skin; /** * Icon Choices functionality. * * @since 1.7.9 */ class IconChoices { /** * Remote URL to download the icon library from. * * @since 1.7.9 * * @var string */ const FONT_AWESOME_URL = 'https://wpforms.com/wp-content/icon-choices.zip'; /** * Font Awesome version. * * @since 1.7.9 * * @var string */ const FONT_AWESOME_VERSION = '6.4.0'; /** * Default icon. * * @since 1.7.9 * * @var string */ const DEFAULT_ICON = 'face-smile'; /** * Default icon style. * * @since 1.7.9 * * @var string */ const DEFAULT_ICON_STYLE = 'regular'; /** * Default accent color. * * @since 1.7.9 * * @var string */ const DEFAULT_COLOR = [ 'classic' => '#0399ed', 'modern' => '#066aab', ]; /** * How many icons to display initially and paginate in the Icon Picker. * * @since 1.7.9 * * @var int */ const DEFAULT_ICONS_PER_PAGE = 50; /** * Absolute path to the cache directory. * * @since 1.7.9 * * @var string */ private $cache_base_path; /** * Cache directory URL. * * @since 1.7.9 * * @var string */ private $cache_base_url; /** * Absolute path to the icons data file. * * @since 1.7.9 * * @var string */ private $icons_data_file; /** * Whether icon library is already installed. * * @since 1.7.9 * * @var bool */ private $is_installed; /** * Default list of icon sizes. * * @since 1.7.9 * * @var array */ private $default_icon_sizes; /** * Initialize class. * * @since 1.7.9 */ public function init() { $upload_dir = wpforms_upload_dir(); $this->cache_base_url = $upload_dir['url'] . '/icon-choices'; $this->cache_base_path = $upload_dir['path'] . '/icon-choices'; $this->icons_data_file = $this->cache_base_path . '/icons.json'; $this->default_icon_sizes = [ 'large' => [ 'label' => __( 'Large', 'wpforms-lite' ), 'size' => 64, ], 'medium' => [ 'label' => __( 'Medium', 'wpforms-lite' ), 'size' => 48, ], 'small' => [ 'label' => __( 'Small', 'wpforms-lite' ), 'size' => 32, ], ]; $this->hooks(); } /** * Hook into WordPress lifecycle. * * @since 1.7.9 */ private function hooks() { // Add inline CSS with custom properties on the frontend. add_action( 'wpforms_frontend_css', [ $this, 'css_custom_properties' ] ); // Add inline CSS with custom properties in the form builder. if ( wpforms_is_admin_page( 'builder' ) ) { add_action( 'admin_head', [ $this, 'css_custom_properties' ] ); } // Load Font Awesome assets. add_action( 'wpforms_builder_enqueues', [ $this, 'enqueues' ] ); // Send data to the frontend. add_filter( 'wpforms_builder_strings', [ $this, 'get_strings' ], 10, 2 ); // Download and extract Font Awesome package. add_action( 'wp_ajax_wpforms_icon_choices_install', [ $this, 'install' ] ); } /** * Get Font Awesome library data file. * * @since 1.8.3 * * @return string */ public function get_icons_data_file() { return $this->icons_data_file; } /** * Whether Font Awesome library is already installed or not. * * @since 1.7.9 * * @return bool */ private function is_installed() { if ( $this->is_installed !== null ) { return $this->is_installed; } $this->is_installed = file_exists( $this->icons_data_file ); return $this->is_installed; } /** * Whether Icon Choices mode is active on any of the fields in current form. * * @since 1.7.9 * * @return bool */ private function is_active() { $form_data = WPForms_Builder::instance()->form_data; return wpforms_has_field_setting( 'choices_icons', $form_data, false ); } /** * Install Font Awesome library via Ajax. * * @since 1.7.9 */ public function install() { // Run a security check. check_ajax_referer( 'wpforms-builder', 'nonce' ); // Check for permissions. if ( ! wpforms_current_user_can( 'edit_forms' ) ) { wp_send_json_error(); } $this->run_install( $this->cache_base_path ); $this->is_installed = true; wp_send_json_success(); } /** * Run Install Font Awesome library from our server. * * @since 1.8.3 * * @param string $destination Destination path. */ public function run_install( $destination ) { // phpcs:ignore WPForms.PHP.HooksMethod.InvalidPlaceForAddingHooks // WordPress assumes it's a plugin/theme and tries to get translations. We don't need that, and it breaks JS output. remove_action( 'upgrader_process_complete', [ 'Language_Pack_Upgrader', 'async_upgrade' ], 20 ); if ( ! function_exists( 'request_filesystem_credentials' ) ) { require_once ABSPATH . 'wp-admin/includes/file.php'; } // Create the Upgrader with our custom skin that reports errors as WP JSON. $installer = new PluginSilentUpgrader( new WP_Ajax_Upgrader_Skin() ); // The installer skin reports any errors via wp_send_json_error() with generic error messages. $installer->init(); $installer->run( [ 'package' => self::FONT_AWESOME_URL, 'destination' => $destination, ] ); } /** * Load all necessary Font Awesome assets. * * @since 1.7.9 * * @param string $view Current Form Builder view (panel). */ public function enqueues( $view ) { if ( ! $this->is_installed() ) { return; } wp_enqueue_style( 'wpforms-icon-choices-font-awesome', $this->cache_base_url . '/css/fontawesome.min.css', [], self::FONT_AWESOME_VERSION ); wp_enqueue_style( 'wpforms-icon-choices-font-awesome-brands', $this->cache_base_url . '/css/brands.min.css', [], self::FONT_AWESOME_VERSION ); wp_enqueue_style( 'wpforms-icon-choices-font-awesome-regular', $this->cache_base_url . '/css/regular.min.css', [], self::FONT_AWESOME_VERSION ); wp_enqueue_style( 'wpforms-icon-choices-font-awesome-solid', $this->cache_base_url . '/css/solid.min.css', [], self::FONT_AWESOME_VERSION ); } /** * Define additional field properties specific to Icon Choices feature. * * @since 1.7.9 * * @see WPForms_Field_Checkbox::field_properties() * @see WPForms_Field_Radio::field_properties() * @see WPForms_Field_Payment_Checkbox::field_properties() * @see WPForms_Field_Payment_Multiple::field_properties() * * @param array $properties Field properties. * @param array $field Field settings. * * @return array */ public function field_properties( $properties, $field ) { $properties['input_container']['class'][] = 'wpforms-icon-choices'; $properties['input_container']['class'][] = sanitize_html_class( 'wpforms-icon-choices-' . $field['choices_icons_style'] ); $properties['input_container']['class'][] = sanitize_html_class( 'wpforms-icon-choices-' . $field['choices_icons_size'] ); $icon_color = isset( $field['choices_icons_color'] ) ? wpforms_sanitize_hex_color( $field['choices_icons_color'] ) : ''; $icon_color = empty( $icon_color ) ? self::get_default_color() : $icon_color; $properties['input_container']['attr']['style'] = "--wpforms-icon-choices-color: {$icon_color};"; foreach ( $properties['inputs'] as $key => $inputs ) { $properties['inputs'][ $key ]['container']['class'][] = 'wpforms-icon-choices-item'; if ( in_array( $field['choices_icons_style'], [ 'default', 'modern', 'classic' ], true ) ) { $properties['inputs'][ $key ]['class'][] = 'wpforms-screen-reader-element'; } } return $properties; } /** * Display a single choice on the form front-end. * * @since 1.7.9 * * @see WPForms_Field_Checkbox::field_display() * @see WPForms_Field_Radio::field_display() * @see WPForms_Field_Payment_Checkbox::field_display() * @see WPForms_Field_Payment_Multiple::field_display() * * @param array $field Field settings. * @param array $choice Single choice item settings. * @param string $type Field input type. * @param string|null $label Custom label, used by Payment fields. */ public function field_display( $field, $choice, $type, $label = null ) { // Only Payment fields supply a custom label. if ( ! $label ) { $label = $choice['label']['text']; } if ( is_array( $choice['label']['class'] ) && wpforms_is_empty_string( $label ) ) { $choice['label']['class'][] = 'wpforms-field-label-inline-empty'; } printf( '<label %1$s> <span class="wpforms-icon-choices-icon"> %2$s <span class="wpforms-icon-choices-icon-bg"></span> </span> <input type="%3$s" %4$s %5$s %6$s> <span class="wpforms-icon-choices-label">%7$s</span> </label>', wpforms_html_attributes( $choice['label']['id'], $choice['label']['class'], $choice['label']['data'], $choice['label']['attr'] ), // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped $this->get_icon( $choice['icon'], $choice['icon_style'], $field['choices_icons_size'] ), esc_attr( $type ), wpforms_html_attributes( $choice['id'], $choice['class'], $choice['data'], $choice['attr'] ), esc_attr( $choice['required'] ), checked( '1', $choice['default'], false ), wp_kses_post( $label ) ); } /** * Output inline CSS custom properties (vars). * * @since 1.7.9 * * @param null|array $forms Frontend forms, if available. * * @return void */ public function css_custom_properties( $forms = null ) { $hook = current_action(); // On the frontend, we need these properties only if Icon Choices is in use. if ( $hook === 'wpforms_frontend_css' && ! wpforms_has_field_setting( 'choices_icons', $forms, true ) ) { return; } $selectors = [ 'wpforms_frontend_css' => '.wpforms-container', 'admin_head' => '#wpforms-builder, .wpforms-icon-picker-container', ]; /** * Add CSS custom properties. * * @since 1.7.9 * * @param array $properties CSS custom properties using CSS syntax. */ $custom_properties = (array) apply_filters( 'wpforms_forms_icon_choices_css_custom_properties', [] ); $icon_sizes = $this->get_icon_sizes(); foreach ( $icon_sizes as $slug => $data ) { $custom_properties[ "wpforms-icon-choices-size-{$slug}" ] = $data['size'] . 'px'; } $custom_properties_css = ''; foreach ( $custom_properties as $property => $value ) { $custom_properties_css .= "--{$property}: {$value};"; } printf( '<style id="wpforms-icon-choices-custom-properties">%s { %s }</style>', esc_attr( $selectors[ $hook ] ), esc_html( $custom_properties_css ) ); } /** * Get available icon sizes. * * @since 1.7.9 * * @return array A list of all icon sizes. */ public function get_icon_sizes() { /** * Allow modifying the icon sizes. * * @since 1.7.9 * * @param array $icon_sizes { * Default icon sizes. * * @type string $key The icon slug. * @type array $value { * Individual icon size data. * * @type string $label Translatable label. * @type int $size The size value. * } * } * @param array $default_icon_sizes Default icon sizes for reference. */ $sizes = (array) apply_filters( 'wpforms_forms_icon_choices_get_icon_sizes', [], $this->default_icon_sizes ); return array_merge( $this->default_icon_sizes, $sizes ); } /** * Read icons metadata from disk. * * @since 1.7.9 * * @param array $strings Strings and values sent to the frontend. * @param array $form Current form. * * @return array */ public function get_strings( $strings, $form ) { $strings['continue'] = esc_html__( 'Continue', 'wpforms-lite' ); $strings['done'] = esc_html__( 'Done!', 'wpforms-lite' ); $strings['uh_oh'] = esc_html__( 'Uh oh!', 'wpforms-lite' ); $strings['icon_choices'] = [ 'is_installed' => false, 'is_active' => $this->is_active(), 'default_icon' => self::DEFAULT_ICON, 'default_icon_style' => self::DEFAULT_ICON_STYLE, 'default_color' => self::get_default_color(), 'icons' => [], 'icons_per_page' => self::DEFAULT_ICONS_PER_PAGE, 'strings' => [ 'install_prompt_content' => esc_html__( 'In order to use the Icon Choices feature, an icon library must be downloaded and installed. It\'s quick and easy, and you\'ll only have to do this once.', 'wpforms-lite' ), 'install_title' => esc_html__( 'Installing Icon Library', 'wpforms-lite' ), 'install_content' => esc_html__( 'This should only take a minute. Please don’t close or reload your browser window.', 'wpforms-lite' ), 'install_success_content' => esc_html__( 'The icon library has been installed successfully. We will now save your form and reload the form builder.', 'wpforms-lite' ), 'install_error_content' => wp_kses( sprintf( /* translators: %s - WPForms Support URL. */ __( 'There was an error installing the icon library. Please try again later or <a href="%s" target="_blank" rel="noreferrer noopener">contact support</a> if the issue persists.', 'wpforms-lite' ), esc_url( wpforms_utm_link( 'https://wpforms.com/account/support/', 'builder-modal', 'Icon Library Install Failure' ) ) ), [ 'a' => [ 'href' => true, 'target' => true, 'rel' => true, ], ] ), 'reinstall_prompt_content' => esc_html__( 'The icon library appears to be missing or damaged. It will now be reinstalled.', 'wpforms-lite' ), 'icon_picker_title' => esc_html__( 'Icon Picker', 'wpforms-lite' ), 'icon_picker_description' => esc_html__( 'Browse or search for the perfect icon.', 'wpforms-lite' ), 'icon_picker_search_placeholder' => esc_html__( 'Search 2000+ icons...', 'wpforms-lite' ), 'icon_picker_not_found' => esc_html__( 'Sorry, we didn\'t find any matching icons.', 'wpforms-lite' ), ], ]; if ( ! $this->is_installed() ) { return $strings; } $strings['icon_choices']['is_installed'] = true; $strings['icon_choices']['icons'] = $this->get_icons(); return $strings; } /** * Get an SVG icon code from a file for inline output in HTML. * * Note: the output does not need to escape. * * @since 1.7.9 * * @param string $icon Font Awesome icon name. * @param string $style Font Awesome style (solid, brands). * @param string|int $size Icon display size. * * @return string */ private function get_icon( string $icon, string $style, $size ): string { $size = sanitize_key( (string) $size ); $icon_sizes = $this->get_icon_sizes(); $size = ! empty( $icon_sizes[ $size ]['size'] ) ? (int) $icon_sizes[ $size ]['size'] : (int) $icon_sizes['large']['size']; return wpforms_get_icon_svg( $icon, $style, $size ); } /** * Get all available icons from the metadata file. * * @since 1.7.9 * * @return array */ private function get_icons() { if ( ! is_file( $this->icons_data_file ) || ! is_readable( $this->icons_data_file ) ) { return []; } // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents $icons = file_get_contents( $this->icons_data_file ); if ( ! $icons ) { return []; } return (array) json_decode( $icons, false ); } /** * Get default accent color. * * @since 1.8.1 * * @return string */ public static function get_default_color() { $render_engine = wpforms_get_render_engine(); return array_key_exists( $render_engine, self::DEFAULT_COLOR ) ? self::DEFAULT_COLOR[ $render_engine ] : self::DEFAULT_COLOR['modern']; } } Akismet.php 0000644 00000022474 15252506741 0006672 0 ustar 00 <?php namespace WPForms\Forms; use Akismet as AkismetPlugin; /** * Class Akismet. * * @since 1.7.6 */ class Akismet { /** * Is the Akismet plugin installed? * * @since 1.7.6 * * @return bool */ public static function is_installed(): bool { return file_exists( WP_PLUGIN_DIR . '/akismet/akismet.php' ); } /** * Is the Akismet plugin activated? * * @since 1.7.6 * * @return bool */ public static function is_activated(): bool { return is_callable( [ 'Akismet', 'get_api_key' ] ) && is_callable( [ 'Akismet', 'http_post' ] ); } /** * Has the Akismet plugin been configured wih a valid API key? * * @since 1.7.6 * * @return bool */ public static function is_configured(): bool { // Akismet will only allow an API key to be saved if it is a valid key. // We can assume that if there is an API key saved, it is valid. return self::is_activated() && ! empty( AkismetPlugin::get_api_key() ); } /** * Get the list of field types that are allowed to be sent to Akismet. * * @since 1.7.6 * * @return array List of field types that are allowed to be sent to Akismet */ private function get_field_type_allowlist(): array { $field_type_allowlist = [ 'text', 'textarea', 'name', 'email', 'phone', 'address', 'url', 'richtext', ]; /** * Filters the field types that are allowed to be sent to Akismet. * * @since 1.7.6 * * @param array $field_type_allowlist Field types allowed to be sent to Akismet. */ return (array) apply_filters( 'wpforms_forms_akismet_get_field_type_allowlist', $field_type_allowlist ); } /** * Get the entry data to be sent to Akismet. * * @since 1.7.6 * * @param array $fields Field data for the current form. * @param array $entry Entry data. * * @return array $entry_data Entry data to be sent to Akismet. */ private function get_entry_data( array $fields, array $entry ): array { $field_type_allowlist = $this->get_field_type_allowlist(); $entry_data = []; $entry_content = []; foreach ( $fields as $field_id => $field ) { $field_type = $field['type']; if ( ! in_array( $field_type, $field_type_allowlist, true ) ) { continue; } $field_content = $this->get_field_content( $field, $entry, $field_id ); if ( ! isset( $entry_data[ $field_type ] ) && in_array( $field_type, [ 'name', 'email', 'url' ], true ) ) { $entry_data[ $field_type ] = $field_content; continue; } $entry_content[] = $field_content; } $entry_data['content'] = implode( ' ', $entry_content ); return $entry_data; } /** * Get field content. * * @since 1.8.5 * @since 1.8.9.3 Changed $field_id type from string to int|string. * * @param array $field Field data. * @param array $entry Entry data. * @param int|string $field_id Field ID. * * @return string */ private function get_field_content( array $field, array $entry, $field_id ): string { if ( ! isset( $entry['fields'][ $field_id ] ) ) { return ''; } if ( ! is_array( $entry['fields'][ $field_id ] ) ) { return (string) $entry['fields'][ $field_id ]; } if ( ! empty( $field['type'] ) && $field['type'] === 'email' && ! empty( $entry['fields'][ $field_id ]['primary'] ) ) { return (string) $entry['fields'][ $field_id ]['primary']; } return implode( ' ', $entry['fields'][ $field_id ] ); } /** * Is the entry marked as spam by Akismet? * * @since 1.7.6 * * @param array $form_data Form data for the current form. * @param array $entry Entry data for the current entry. * * @return bool */ private function entry_is_spam( array $form_data, array $entry ): bool { $request = $this->get_request_args( $form_data, $entry ); // Tell Akismet to not use the submission for training if we're on the Preview page and the user is // an administrator. Checking for both the preview page and the administrator role prevents // abuse by simply adding a GET parameter. This check happens in the ajax request, // where `\WPForms\Forms\Preview::is_preview_page()` does not work, so we // need to check for the GET parameter directly. if ( // phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized isset( $_REQUEST['page_url'] ) && strpos( wp_unslash( $_REQUEST['page_url'] ), 'wpforms_form_preview' ) !== false && current_user_can( 'manage_options' ) ) { $request['is_test'] = true; } $response = $this->http_post( $request, 'comment-check' ); return ! empty( $response ) && isset( $response[1] ) && 'true' === trim( $response[1] ); } /** * Mark the entry as not spam in Akismet. * * @since 1.8.8 * * @param array $form_data Form data for the current form. * @param array $entry Entry data for the current entry. * * @return bool */ public function set_entry_not_spam( array $form_data, array $entry ) { if ( ! self::is_configured() ) { return false; } $request = $this->get_request_args( $form_data, $entry ); $response = $this->http_post( $request, 'submit-ham' ); // Yes, Akismet returns "Thanks for making the web a better place." as the response. return ! empty( $response ) && isset( $response[1] ) && 'Thanks for making the web a better place.' === trim( $response[1] ); } /** * Mark the entry as spam in Akismet. * * @since 1.8.9 * * @param array $form_data Form data for the current form. * @param array $entry Entry data for the current entry. * * @return bool */ public function submit_missed_spam( array $form_data, array $entry ) { if ( ! self::is_configured() ) { return false; } $request = $this->get_request_args( $form_data, $entry ); $response = $this->http_post( $request, 'submit-spam' ); // Yes, Akismet returns "Thanks for making the web a better place." as the response. return ! empty( $response ) && isset( $response[1] ) && 'Thanks for making the web a better place.' === trim( $response[1] ); } /** * Get the request arguments to be sent to Akismet. * * @since 1.8.8 * * @param array $form_data Form data for the current form. * @param array $entry Entry data for the current entry. * * @return array $request_args Request arguments to be sent to Akismet. */ private function get_request_args( $form_data, $entry ) { $entry_data = $this->get_entry_data( $form_data['fields'], $entry ); $entry_id = $entry['entry_id'] ?? null; // We can't use certain real-time functions when the entry is marked as not spam. // In this case, we need to use the smart tag value. if ( ! empty( $entry_id ) ) { $page_url = wpforms_process_smart_tags( '{page_url}', $form_data, [], $entry_id, 'akismet-request-args' ); $url_referer = wpforms_process_smart_tags( '{url_referer}', $form_data, [], $entry_id, 'akismet-request-args' ); $user_id = wpforms_process_smart_tags( '{user_id}', $form_data, [], $entry_id, 'akismet-request-args' ); $user_ip = wpforms_process_smart_tags( '{user_ip}', $form_data, [], $entry_id, 'akismet-request-args' ); $user_agent = ''; } else { $page_url = wpforms_current_url(); $url_referer = wp_get_referer(); $user_id = get_current_user_id(); $user_ip = wpforms_get_ip(); $user_agent = isset( $_SERVER['HTTP_USER_AGENT'] ) ? wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) : ''; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized } return [ 'blog' => get_option( 'home' ), 'blog_lang' => get_locale(), 'blog_charset' => get_bloginfo( 'charset' ), 'permalink' => $page_url, 'user_ip' => wpforms_is_collecting_ip_allowed( $form_data ) ? $user_ip : '', 'user_id' => $user_id, 'user_role' => AkismetPlugin::get_user_roles( $user_id ), 'user_agent' => $user_agent, 'referrer' => $url_referer ? $url_referer : '', 'comment_type' => 'contact-form', 'comment_author' => $entry_data['name'] ?? '', 'comment_author_email' => $entry_data['email'] ?? '', 'comment_author_url' => $entry_data['url'] ?? '', 'comment_content' => $entry_data['content'] ?? '', 'honeypot_field_name' => 'wpforms[hp]', ]; } /** * Send a POST request to the Akismet API. * * @since 1.8.8 * * @param array $request Request arguments to be sent to Akismet. * @param string $path API path. * * @return array */ private function http_post( $request, $path ) { // build_query() does not urlencode the values, but API explicitly requires it. $request = array_map( 'urlencode', $request ); return AkismetPlugin::http_post( build_query( $request ), $path ); } /** * Validate entry. * * @since 1.7.6 * * @param array $form_data Form data for the current form. * @param array $entry Entry data for the current entry. * * @return string|bool */ public function validate( array $form_data, array $entry ) { // If Akismet is turned on in form settings, is activated, is configured and the entry is spam. if ( ! empty( $form_data['settings']['akismet'] ) && self::is_configured() && $this->entry_is_spam( $form_data, $entry ) ) { // This string is being logged not printed, so it does not need to be translatable. return esc_html__( 'Anti-spam verification failed, please try again later.', 'wpforms-lite' ); } return false; } } Locator.php 0000644 00000101016 15252506741 0006666 0 ustar 00 <?php // phpcs:disable Generic.Commenting.DocComment.MissingShort /** @noinspection PhpUnnecessaryCurlyVarSyntaxInspection */ // phpcs:enable Generic.Commenting.DocComment.MissingShort namespace WPForms\Forms; use WP_Post; use WPForms\Tasks\Actions\FormsLocatorScanTask; /** * Class Locator. * * @since 1.7.4 */ class Locator { /** * Column name on Forms Overview admin page. * * @since 1.7.4 */ const COLUMN_NAME = 'locations'; /** * Locations meta key. * * @since 1.7.4 */ const LOCATIONS_META = 'wpforms_form_locations'; /** * WPForms widget name. * * @since 1.7.4 */ const WPFORMS_WIDGET_NAME = 'wpforms-widget'; /** * WPForms widget prefix. * * @since 1.7.4 */ const WPFORMS_WIDGET_PREFIX = self::WPFORMS_WIDGET_NAME . '-'; /** * WPForms widgets option name. * * @since 1.7.4 */ const WPFORMS_WIDGET_OPTION = 'widget_' . self::WPFORMS_WIDGET_NAME; /** * Text widget name. * * @since 1.7.4 */ const TEXT_WIDGET_NAME = 'text'; /** * Text widget prefix. * * @since 1.7.4 */ const TEXT_WIDGET_PREFIX = self::TEXT_WIDGET_NAME . '-'; /** * Text widgets option name. * * @since 1.7.4 */ const TEXT_WIDGET_OPTION = 'widget_' . self::TEXT_WIDGET_NAME; /** * Block widget name. * * @since 1.7.4 */ const BLOCK_WIDGET_NAME = 'block'; /** * Block widget prefix. * * @since 1.7.4 */ const BLOCK_WIDGET_PREFIX = self::BLOCK_WIDGET_NAME . '-'; /** * Block widgets' option name. * * @since 1.7.4 */ const BLOCK_WIDGET_OPTION = 'widget_' . self::BLOCK_WIDGET_NAME; /** * Location type for widget. * For a page/post, the location type is the post type. * * @since 1.7.4 */ const WIDGET = 'widget'; /** * WP template post type. * * @since 1.7.4 */ const WP_TEMPLATE = 'wp_template'; /** * WP template post type. * * @since 1.7.4.1 */ const WP_TEMPLATE_PART = 'wp_template_part'; /** * Standalone location types. * * @since 1.8.7 */ const STANDALONE_LOCATION_TYPES = [ 'form_pages', 'conversational_forms' ]; /** * Default title for WPForms widget. * For WPForms widget, we extract title from the widget. If it is empty, we use the default one. * * @since 1.7.4 * * @var string */ private $wpforms_widget_title = ''; /** * Default title for text widget. * For text widget, we extract title from the widget. If it is empty, we use the default one. * * @since 1.7.4 * * @var string */ private $text_widget_title = ''; /** * Fixed title for block widget. * * @since 1.7.4 * * @var string */ private $block_widget_title = ''; /** * Home url. * * @since 1.7.4 * * @var string */ private $home_url; /** * Scan status. * * @since 1.7.4 * * @var string */ private $scan_status; /** * Init class. * * @since 1.7.4 */ public function init() { $this->home_url = home_url(); $this->scan_status = (string) get_option( FormsLocatorScanTask::SCAN_STATUS ); $this->wpforms_widget_title = __( 'WPForms Widget', 'wpforms-lite' ); $this->text_widget_title = __( 'Text Widget', 'wpforms-lite' ); $this->block_widget_title = __( 'Block Widget', 'wpforms-lite' ); $this->hooks(); } /** * Register hooks. * * @since 1.7.4 */ private function hooks() { // View hooks. add_filter( 'wpforms_admin_forms_table_facades_columns_data', [ $this, 'add_column_data' ] ); add_filter( 'wpforms_overview_table_column_value', [ $this, 'column_value' ], 10, 3 ); add_filter( 'wpforms_overview_row_actions', [ $this, 'row_actions_all' ], 10, 2 ); add_action( 'wpforms_overview_enqueue', [ $this, 'localize_overview_script' ] ); // Monitoring hooks. add_action( 'save_post', [ $this, 'save_post' ], 10, 3 ); add_action( 'post_updated', [ $this, 'post_updated' ], 10, 3 ); add_action( 'wp_trash_post', [ $this, 'trash_post' ] ); add_action( 'untrash_post', [ $this, 'untrash_post' ] ); add_action( 'delete_post', [ $this, 'trash_post' ] ); add_action( 'permalink_structure_changed', [ $this, 'permalink_structure_changed' ], 10, 2 ); $wpforms_widget_option = self::WPFORMS_WIDGET_OPTION; $text_widget_option = self::TEXT_WIDGET_OPTION; $block_widget_option = self::BLOCK_WIDGET_OPTION; add_action( "update_option_{$wpforms_widget_option}" , [ $this, 'update_option' ], 10, 3 ); add_action( "update_option_{$text_widget_option}" , [ $this, 'update_option' ], 10, 3 ); add_action( "update_option_{$block_widget_option}", [ $this, 'update_option' ], 10, 3 ); } /** * Add locations' column to the table columns data. * * @since 1.8.6 * * @param array|mixed $columns Columns data. * * @return array */ public function add_column_data( $columns ): array { $columns = (array) $columns; $columns[ self::COLUMN_NAME ] = [ 'label' => esc_html__( 'Locations', 'wpforms-lite' ), 'label_html' => sprintf( '<span class="wpforms-locations-column-title">%1$s</span>' . '<span class="wpforms-locations-column-icon" title="%2$s"></span>', esc_html__( 'Locations', 'wpforms-lite' ), esc_html__( 'Form locations', 'wpforms-lite' ) ), ]; return $columns; } /** * Display column value. * * @since 1.7.4 * * @param mixed $value Column value. * @param WP_Post $form Form. * @param string $column_name Column name. * * @return mixed */ public function column_value( $value, $form, $column_name ) { if ( $column_name !== self::COLUMN_NAME ) { return $value; } $form_locations = get_post_meta( $form->ID, self::LOCATIONS_META, true ); if ( $form_locations === '' ) { $empty_values = [ '' => '—', FormsLocatorScanTask::SCAN_STATUS_IN_PROGRESS => '...', FormsLocatorScanTask::SCAN_STATUS_COMPLETED => '0', ]; return $empty_values[ $this->scan_status ]; } $values = $this->get_location_rows( $form_locations ); if ( ! $values ) { return '0'; } $column_value = sprintf( '<span class="wpforms-locations-count"><a href="#" title="%s">%d</a></span>', esc_attr__( 'View form locations', 'wpforms-lite' ), count( $values ) ); $column_value .= '<p class="locations-list">' . implode( '', $values ) . '</p>'; return $column_value; } /** * Row actions for view "All". * * @since 1.7.4 * * @param array $row_actions Row actions. * @param WP_Post $form Form object. * * @return array */ public function row_actions_all( $row_actions, $form ) { $form_locations = get_post_meta( $form->ID, self::LOCATIONS_META, true ); if ( ! $form_locations ) { return $row_actions; } $locations = [ 'locations' => sprintf( '<a href="#" title="%s">%s</a>', esc_attr__( 'View form locations', 'wpforms-lite' ), esc_html__( 'Locations', 'wpforms-lite' ) ), ]; // Insert Locations action before the first available position in the positions' list or at the end of $row_actions. $positions = [ 'preview_', 'duplicate', 'trash', ]; $keys = array_keys( $row_actions ); foreach ( $positions as $position ) { $pos = array_search( $position, $keys, true ); if ( $pos !== false ) { break; } } $pos = $pos === false ? count( $row_actions ) : $pos; return array_slice( $row_actions, 0, $pos ) + $locations + array_slice( $row_actions, $pos ); } /** * Localize the overview script to pass translation strings. * * @since 1.7.4 */ public function localize_overview_script() { wp_localize_script( 'wpforms-admin-forms-overview', 'wpforms_forms_locator', [ 'paneTitle' => __( 'Form Locations', 'wpforms-lite' ), 'close' => __( 'Close', 'wpforms-lite' ), ] ); } /** * Get id of the sidebar where the widget is positioned. * * @since 1.7.4 * * @param string $widget_id Widget id. * * @return string */ private function get_widget_sidebar_id( $widget_id ) { $sidebars_widgets = wp_get_sidebars_widgets(); foreach ( $sidebars_widgets as $sidebar_id => $sidebar_widgets ) { foreach ( $sidebar_widgets as $sidebar_widget ) { if ( $widget_id === $sidebar_widget ) { return (string) $sidebar_id; } } } return ''; } /** * Get the name of the sidebar where the widget is positioned. * * @since 1.7.4 * * @param string $widget_id Widget id. * * @return string */ private function get_widget_sidebar_name( $widget_id ) { $sidebar_id = $this->get_widget_sidebar_id( $widget_id ); if ( ! $sidebar_id ) { return ''; } $sidebar = $this->get_sidebar( $sidebar_id ); return isset( $sidebar['name'] ) ? (string) $sidebar['name'] : ''; } /** * Retrieves the registered sidebar with the given ID. * * @since 1.7.4 * * @global array $wp_registered_sidebars The registered sidebars. * * @param string $id The sidebar ID. * * @return array|null The discovered sidebar, or null if it is not registered. */ private function get_sidebar( $id ) { if ( function_exists( 'wp_get_sidebar' ) ) { return wp_get_sidebar( $id ); } global $wp_registered_sidebars; if ( ! $wp_registered_sidebars ) { return null; } foreach ( $wp_registered_sidebars as $sidebar ) { if ( $sidebar['id'] === $id ) { return $sidebar; } } if ( $id === 'wp_inactive_widgets' ) { return [ 'id' => 'wp_inactive_widgets', 'name' => __( 'Inactive widgets', 'wpforms-lite' ), ]; } return null; } /** * Get post location title. * * @since 1.7.4 * * @param array $form_location Form location. * * @return string */ private function get_post_location_title( $form_location ) { $title = $form_location['title']; if ( $this->is_wp_template( $form_location['type'] ) ) { return __( 'Site editor template', 'wpforms-lite' ) . ': ' . $title; } return $title; } /** * Whether locations' type is WP Template. * * @since 1.7.4.1 * * @param string $location_type Location type. * * @return bool */ private function is_wp_template( $location_type ) { return in_array( $location_type, [ self::WP_TEMPLATE, self::WP_TEMPLATE_PART ], true ); } /** * Whether a location type is standalone. * * @since 1.8.7 * * @param string $location_type Location type. * * @return bool */ private function is_standalone( string $location_type ): bool { return in_array( $location_type, self::STANDALONE_LOCATION_TYPES, true ); } /** * Get location title. * * @since 1.7.4 * * @param array $form_location Form location. * * @return string */ private function get_location_title( $form_location ) { if ( $form_location['type'] !== self::WIDGET ) { return $this->get_post_location_title( $form_location ); } $sidebar_name = $this->get_widget_sidebar_name( $form_location['id'] ); if ( ! $sidebar_name ) { // The widget is not found. return ''; } $title = $form_location['title']; if ( ! $title ) { if ( strpos( $form_location['id'], self::WPFORMS_WIDGET_PREFIX ) === 0 ) { $title = $this->wpforms_widget_title; } if ( strpos( $form_location['id'], 'text-' ) === 0 ) { $title = $this->text_widget_title; } } return $sidebar_name . ': ' . $title; } /** * Get location url. * * @since 1.7.4 * * @param array $form_location Form location. * * @return string */ private function get_location_url( $form_location ) { // Get widget or wp_template url. if ( $form_location['type'] === self::WIDGET || $this->is_wp_template( $form_location['type'] ) ) { return ''; } // Get standalone url. if ( $this->is_standalone( $form_location['type'] ) ) { return $form_location['url']; } // Get post url. if ( ! $this->is_post_visible( $form_location ) ) { return ''; } return $form_location['url']; } /** * Get location edit url. * * @since 1.7.4 * * @param array $form_location Form location. * * @return string */ private function get_location_edit_url( array $form_location ): string { // Get widget url. if ( $form_location['type'] === self::WIDGET ) { return current_user_can( 'edit_theme_options' ) ? admin_url( 'widgets.php' ) : ''; } // Get standalone url. if ( $this->is_standalone( $form_location['type'] ) ) { return add_query_arg( [ 'page' => 'wpforms-builder', 'view' => 'settings', 'form_id' => $form_location['form_id'], ], admin_url( 'admin.php' ) ); } // Get post url. if ( ! $this->is_post_visible( $form_location ) ) { return ''; } if ( $this->is_wp_template( $form_location['type'] ) ) { return add_query_arg( [ 'postType' => $form_location['type'], 'postId' => get_stylesheet() . '//' . str_replace( '/', '', $form_location['url'] ), ], admin_url( 'site-editor.php' ) ); } return (string) get_edit_post_link( $form_location['id'], '' ); } /** * Get location information to output as a row in the location pane. * * @since 1.7.4 * * @param array $form_location Form location. * * @return string * @noinspection PhpTernaryExpressionCanBeReducedToShortVersionInspection * @noinspection ElvisOperatorCanBeUsedInspection */ private function get_location_row( $form_location ) { $title = $this->get_location_title( $form_location ); $title = $title ? $title : __( '(no title)', 'wpforms-lite' ); $location_url = $this->get_location_url( $form_location ); $location_link = ''; if ( $location_url ) { $location_full_url = $this->home_url . $location_url; // phpcs:ignore Generic.Commenting.DocComment.MissingShort /** @noinspection HtmlUnknownTarget */ $location_link = sprintf( ' <a href="%1$s" target="_blank" class="wpforms-locations-link">%2$s <i class="fa fa-external-link" aria-hidden="true"></i></a>', esc_url( $location_full_url ), esc_url( $location_url ) ); } $location_edit_url = $this->get_location_edit_url( $form_location ); $location_edit_url = $location_edit_url ? $location_edit_url : '#'; // phpcs:ignore Generic.Commenting.DocComment.MissingShort /** @noinspection HtmlUnknownTarget */ $location_edit_link = sprintf( '<a href="%1$s">%2$s</a>', esc_url( $location_edit_url ), esc_html( $title ) ); // Escaped above. return sprintf( '<span class="wpforms-locations-list-item">%s</span>', $location_edit_link . wp_kses_post( urldecode( $location_link ) ) ); } /** * Get location information to output as rows in the location pane. * * @since 1.7.4 * * @param array $form_locations Form locations. * * @return array */ private function get_location_rows( $form_locations ) { $rows = []; foreach ( $form_locations as $form_location ) { $rows[] = $this->get_location_row( $form_location ); } $rows = array_unique( array_filter( $rows ) ); uasort( $rows, static function ( $a, $b ) { $pattern = '/href=".+widgets.php">(.+?)</i'; $widget_title_a = preg_match( $pattern, $a, $ma ) ? $ma[1] : ''; $widget_title_b = preg_match( $pattern, $b, $mb ) ? $mb[1] : ''; return strcmp( $widget_title_a, $widget_title_b ); } ); return $rows; } /** * Update form location on save_post action. * * @since 1.7.4 * * @param int $post_ID Post ID. * @param WP_Post $post Post object. * @param bool $update Whether this is an existing post being updated. * * @noinspection PhpUnusedParameterInspection */ public function save_post( $post_ID, $post, $update ) { if ( $update || ! in_array( $post->post_type, $this->get_post_types(), true ) || ! in_array( $post->post_status, $this->get_post_statuses(), true ) ) { return; } $form_ids = $this->get_form_ids( $post->post_content ); $this->update_form_locations_metas( null, $post, [], $form_ids ); } /** * Update form location on post_updated action. * * @since 1.7.4 * * @param int $post_id Post id. * @param WP_Post $post_after Post after the update. * @param WP_Post $post_before Post before the update. * * @noinspection PhpUnusedParameterInspection */ public function post_updated( $post_id, $post_after, $post_before ) { if ( ! in_array( $post_after->post_type, $this->get_post_types(), true ) || ! in_array( $post_after->post_status, $this->get_post_statuses(), true ) ) { return; } $form_ids_before = $this->get_form_ids( $post_before->post_content ); $form_ids_after = $this->get_form_ids( $post_after->post_content ); $this->update_form_locations_metas( $post_before, $post_after, $form_ids_before, $form_ids_after ); } /** * Update form locations on trash_post action. * * @since 1.7.4 * * @param int $post_id Post id. */ public function trash_post( $post_id ) { $post = get_post( $post_id ); $form_ids_before = $this->get_form_ids( $post->post_content ); $form_ids_after = []; $this->update_form_locations_metas( null, $post, $form_ids_before, $form_ids_after ); } /** * Update form locations on untrash_post action. * * @since 1.7.4 * * @param int $post_id Post id. */ public function untrash_post( $post_id ) { $post = get_post( $post_id ); $form_ids_before = []; $form_ids_after = $this->get_form_ids( $post->post_content ); $this->update_form_locations_metas( null, $post, $form_ids_before, $form_ids_after ); } /** * Prepare widgets for further search. * * @since 1.7.4 * * @param array|null $widgets Widgets. * @param string $type Widget type. * * @return array */ private function prepare_widgets( $widgets, $type ) { $params = [ 'wpforms' => [ 'option' => self::WPFORMS_WIDGET_OPTION, 'content' => 'form_id', ], 'text' => [ 'option' => self::TEXT_WIDGET_OPTION, 'content' => 'text', ], 'block' => [ 'option' => self::BLOCK_WIDGET_OPTION, 'content' => 'content', ], ]; if ( ! array_key_exists( $type, $params ) ) { return []; } $option = $params[ $type ]['option']; $content = $params[ $type ]['content']; $widgets = $widgets ?? (array) get_option( $option, [] ); return array_filter( $widgets, static function ( $widget ) use ( $content ) { return isset( $widget[ $content ] ); } ); } /** * Search forms in WPForms widgets. * * @since 1.7.4 * * @param array $widgets Widgets. * * @return array */ private function search_in_wpforms_widgets( $widgets = null ) { $widgets = $this->prepare_widgets( $widgets, 'wpforms' ); $locations = []; foreach ( $widgets as $id => $widget ) { $locations[] = [ 'type' => self::WIDGET, 'title' => $widget['title'], 'form_id' => $widget['form_id'], 'id' => self::WPFORMS_WIDGET_PREFIX . $id, ]; } return $locations; } /** * Search forms in text widgets. * * @since 1.7.4 * * @param array $widgets Widgets. * * @return array */ private function search_in_text_widgets( $widgets = null ) { $widgets = $this->prepare_widgets( $widgets, 'text' ); $locations = []; foreach ( $widgets as $id => $widget ) { $form_ids = $this->get_form_ids( $widget['text'] ); foreach ( $form_ids as $form_id ) { $locations[] = [ 'type' => self::WIDGET, 'title' => $widget['title'], 'form_id' => $form_id, 'id' => self::TEXT_WIDGET_PREFIX . $id, ]; } } return $locations; } /** * Search forms in block widgets. * * @since 1.7.4 * * @param array $widgets Widgets. * * @return array */ private function search_in_block_widgets( $widgets = null ) { $widgets = $this->prepare_widgets( $widgets, 'block' ); $locations = []; foreach ( $widgets as $id => $widget ) { $form_ids = $this->get_form_ids( $widget['content'] ); foreach ( $form_ids as $form_id ) { $locations[] = [ 'type' => self::WIDGET, 'title' => $this->block_widget_title, 'form_id' => $form_id, 'id' => self::BLOCK_WIDGET_PREFIX . $id, ]; } } return $locations; } /** * Search forms in widgets. * * @since 1.7.4 * * @return array */ public function search_in_widgets() { return array_merge( $this->search_in_wpforms_widgets(), $this->search_in_text_widgets(), $this->search_in_block_widgets() ); } /** * Get the difference of two arrays containing locations. * * @since 1.7.4 * * @param array $locations1 Locations to subtract from. * @param array $locations2 Locations to subtract. * * @return array */ private function array_udiff( $locations1, $locations2 ) { return array_udiff( $locations1, $locations2, static function ( $a, $b ) { return ( $a === $b ) ? 0 : - 1; } ); } /** * Remove locations from metas. * * @since 1.7.4 * * @param array $locations_to_remove Locations to remove. * * @return void */ private function remove_locations( $locations_to_remove ) { foreach ( $locations_to_remove as $location_to_remove ) { $locations = get_post_meta( $location_to_remove['form_id'], self::LOCATIONS_META, true ); if ( ! $locations ) { continue; } foreach ( $locations as $key => $location ) { if ( $location['id'] === $location_to_remove['id'] ) { unset( $locations[ $key ] ); } } update_post_meta( $location_to_remove['form_id'], self::LOCATIONS_META, $locations ); } } /** * Add locations to metas. * * @since 1.7.4 * * @param array $locations_to_add Locations to add. * * @return void */ private function add_locations( $locations_to_add ) { foreach ( $locations_to_add as $location_to_add ) { $locations = get_post_meta( $location_to_add['form_id'], self::LOCATIONS_META, true ); if ( ! $locations ) { $locations = []; } $locations[] = $location_to_add; update_post_meta( $location_to_add['form_id'], self::LOCATIONS_META, $locations ); } } /** * Update form locations on widget update. * * @since 1.7.4 * * @param mixed $old_value The old option value. * @param mixed $value The new option value. * @param string $option Option name. */ public function update_option( $old_value, $value, $option ) { switch ( $option ) { case self::WPFORMS_WIDGET_OPTION: $old_locations = $this->search_in_wpforms_widgets( $old_value ); $new_locations = $this->search_in_wpforms_widgets( $value ); break; case self::TEXT_WIDGET_OPTION: $old_locations = $this->search_in_text_widgets( $old_value ); $new_locations = $this->search_in_text_widgets( $value ); break; case self::BLOCK_WIDGET_OPTION: $old_locations = $this->search_in_block_widgets( $old_value ); $new_locations = $this->search_in_block_widgets( $value ); break; default: // phpcs:ignore WPForms.Formatting.EmptyLineBeforeReturn.AddEmptyLineBeforeReturnStatement return; } $this->remove_locations( $this->array_udiff( $old_locations, $new_locations ) ); $this->add_locations( $this->array_udiff( $new_locations, $old_locations ) ); } /** * Delete locations and schedule new rescan on change of permalink structure. * * @since 1.7.4 * * @param string $old_permalink_structure The previous permalink structure. * @param string $permalink_structure The new permalink structure. * * @noinspection PhpUnusedParameterInspection */ public function permalink_structure_changed( $old_permalink_structure, $permalink_structure ) { // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundAfterLastUsed /** * Run Forms Locator delete action. * * @since 1.7.4 */ do_action( FormsLocatorScanTask::DELETE_ACTION ); // phpcs:ignore WPForms.PHP.ValidateHooks.InvalidHookName, WordPress.NamingConventions.PrefixAllGlobals.DynamicHooknameFound /** * Run Forms Locator scan action. * * @since 1.7.4 */ do_action( FormsLocatorScanTask::RESCAN_ACTION ); // phpcs:ignore WPForms.PHP.ValidateHooks.InvalidHookName, WordPress.NamingConventions.PrefixAllGlobals.DynamicHooknameFound } /** * Update form locations metas. * * @since 1.7.4 * @since 1.8.2.3 Added `$post_before` parameter. * * @param WP_Post|null $post_before The post before the update. * @param WP_Post $post_after The post after the update. * @param array $form_ids_before Form IDs before the update. * @param array $form_ids_after Form IDs after the update. */ private function update_form_locations_metas( $post_before, $post_after, $form_ids_before, $form_ids_after ) { // Determine which locations to remove and which to add. $form_ids_to_remove = array_diff( $form_ids_before, $form_ids_after ); $form_ids_to_add = array_diff( $form_ids_after, $form_ids_before ); // Loop through each form ID to remove the locations' meta. foreach ( $form_ids_to_remove as $form_id ) { update_post_meta( $form_id, self::LOCATIONS_META, $this->get_locations_without_current_post( $form_id, $post_after->ID ) ); } // Determine the titles and slugs. $old_title = $post_before->post_title ?? ''; $old_slug = $post_before->post_name ?? ''; $new_title = $post_after->post_title; $new_slug = $post_after->post_name; // If the title and slug are the same and there are no form IDs to add, bail. if ( empty( $form_ids_to_add ) && $old_title === $new_title && $old_slug === $new_slug ) { return; } // Merge the form IDs and remove duplicates. $form_ids = array_unique( array_merge( $form_ids_to_add, $form_ids_after ) ); $this->save_location_meta( $form_ids, $post_after->ID, $post_after ); } /** * Save the location meta. * * @since 1.8.2.3 * * @param array $form_ids Form IDs. * @param int $post_id Post ID. * @param WP_Post $post_after Post after the update. */ private function save_location_meta( $form_ids, $post_id, $post_after ) { // Build the URL. $url = get_permalink( $post_id ); $url = ( $url === false || is_wp_error( $url ) ) ? '' : $url; $url = str_replace( $this->home_url, '', $url ); // Loop through each Form ID and save the location meta. foreach ( $form_ids as $form_id ) { $locations = $this->get_locations_without_current_post( $form_id, $post_id ); $locations[] = [ 'type' => $post_after->post_type, 'title' => $post_after->post_title, 'form_id' => $form_id, 'id' => $post_id, 'status' => $post_after->post_status, 'url' => $url, ]; update_post_meta( $form_id, self::LOCATIONS_META, $locations ); } } /** * Get post types for search in. * * @since 1.7.4 * * @return string[] */ public function get_post_types() { $args = [ 'public' => true, 'publicly_queryable' => true, ]; $post_types = get_post_types( $args, 'names', 'or' ); unset( $post_types['attachment'] ); $post_types[] = self::WP_TEMPLATE; $post_types[] = self::WP_TEMPLATE_PART; return $post_types; } /** * Get post statuses for search in. * * @since 1.7.4 * * @return string[] */ public function get_post_statuses() { return [ 'publish', 'pending', 'draft', 'future', 'private' ]; } /** * Get form ids from the content. * * @since 1.7.4 * * @param string $content Content. * * @return int[] */ public function get_form_ids( $content ) { $form_ids = []; if ( preg_match_all( /** * Extract id from conventional wpforms shortcode or wpforms block. * Examples: * [wpforms id="32" title="true" description="true"] * <!-- wp:wpforms/form-selector {"clientId":"b5f8e16a-fc28-435d-a43e-7c77719f074c", "formId":"32","displayTitle":true,"displayDesc":true} /--> * In both, we should find 32. */ '#\[\s*wpforms.+id\s*=\s*"(\d+?)".*]|<!-- wp:wpforms/form-selector {.*?"formId":"(\d+?)".*?} /-->#', $content, $matches ) ) { array_shift( $matches ); $form_ids = array_map( 'intval', array_unique( array_filter( array_merge( ...$matches ) ) ) ); } return $form_ids; } /** * Get form locations without a current post. * * @since 1.7.4 * * @param int $form_id Form id. * @param int $post_id Post id. * * @return array */ private function get_locations_without_current_post( $form_id, $post_id ) { $locations = get_post_meta( $form_id, self::LOCATIONS_META, true ); if ( ! is_array( $locations ) ) { $locations = []; } return array_filter( $locations, static function ( $location ) use ( $post_id ) { return $location['id'] !== $post_id; } ); } /** * Determine whether a post is visible. * * @since 1.7.4 * * @param array $location Post location. * * @return bool */ private function is_post_visible( $location ) { $edit_cap = 'edit_post'; $read_cap = 'read_post'; $post_id = $location['id']; if ( ! get_post_type_object( $location['type'] ) ) { // Post type is not registered. return false; } $post_status_obj = get_post_status_object( $location['status'] ); if ( ! $post_status_obj ) { // Post status is not registered, assume it's not public. return current_user_can( $edit_cap, $post_id ); } if ( $post_status_obj->public ) { return true; } if ( ! is_user_logged_in() ) { // User must be logged in to view unpublished posts. return false; } if ( $post_status_obj->protected ) { // User must have edit permissions on the draft to preview. return current_user_can( $edit_cap, $post_id ); } if ( $post_status_obj->private ) { return current_user_can( $read_cap, $post_id ); } return false; } /** * Build a standalone location. * * @since 1.8.7 * * @param int $form_id The form ID. * @param array $form_data Form data. * @param string $status Form status. * * @return array Location. */ public function build_standalone_location( int $form_id, array $form_data, string $status = 'publish' ): array { if ( empty( $form_id ) || empty( $form_data ) ) { return []; } // Form templates should not have any locations. if ( get_post_type( $form_id ) === 'wpforms-template' ) { return []; } foreach ( self::STANDALONE_LOCATION_TYPES as $location_type ) { if ( empty( $form_data['settings'][ "{$location_type}_enable" ] ) ) { continue; } return $this->build_standalone_location_type( $location_type, $form_id, $form_data, $status ); } return []; } /** * Build a standalone location. * * @since 1.8.8 * * @param string $location_type Standalone location type. * @param int $form_id The form ID. * @param array $form_data Form data. * @param string $status Form status. * * @return array Location. */ private function build_standalone_location_type( string $location_type, int $form_id, array $form_data, string $status ): array { $title_key = "{$location_type}_title"; $slug_key = "{$location_type}_page_slug"; $title = $form_data['settings'][ $title_key ] ?? ''; $slug = $form_data['settings'][ $slug_key ] ?? ''; // Return the location array. return [ 'type' => $location_type, 'title' => $title, 'form_id' => (int) $form_data['id'], 'id' => $form_id, 'status' => $status, 'url' => '/' . $slug . '/', ]; } /** * Add standalone form locations to post meta. * * Post meta is used to store all forms' locations, * which is displayed on the WPForms Overview page. * * @since 1.8.7 * * @param int $form_id Form ID. * @param array $data Form data. */ public function add_standalone_location_to_locations_meta( int $form_id, array $data ) { // Build standalone location. $location = $this->build_standalone_location( $form_id, $data ); // No location? Bail. if ( empty( $location ) ) { return; } // Setup data. $new_location[] = $location; $post_meta = get_post_meta( $form_id, self::LOCATIONS_META, true ); // If there is post meta, merge it with the new location. if ( ! empty( $post_meta ) ) { // Remove any previously set standalone locations. $post_meta = $this->remove_standalone_location_from_array( $form_id, $post_meta ); // Merge locations and remove duplicates. $new_location = array_unique( array_merge( $post_meta, $new_location ), SORT_REGULAR ); } // Update post meta. update_post_meta( $form_id, self::LOCATIONS_META, $new_location ); } /** * Remove a form page from an array. * * @since 1.8.7 * * @param int $form_id The form ID. * @param array $post_meta The post meta. * * @return array $post_meta Filtered post meta. */ private function remove_standalone_location_from_array( int $form_id, array $post_meta ): array { // No form ID or post meta? Bail. if ( empty( $form_id ) || empty( $post_meta ) ) { return []; } // Loop over all locations. foreach ( $post_meta as $key => $location ) { // Verify the location keys exist. if ( ! isset( $location['form_id'], $location['type'] ) ) { continue; } // If the form ID and location type match. if ( $location['form_id'] === $form_id && $this->is_standalone( $location['type'] ) ) { // Unset the form page location. unset( $post_meta[ $key ] ); } } return $post_meta; } } AntiSpam.php 0000644 00000023304 15252506741 0007002 0 ustar 00 <?php namespace WPForms\Forms; /** * Class Anti-Spam v3. * * This class is used for modern Anti-Spam approach. * * @since 1.9.0 */ class AntiSpam { /** * Field ID to insert the honeypot field before. * * @since 1.9.0 * * @var int */ private $insert_before_field_id = 1; /** * Array with IDs of all honeypot fields on the current page grouped by form IDs ([form_id => field_id]). * * @since 1.9.0.3 * * @var array */ private $forms_data = []; /** * Initialise the actions for the modern Anti-Spam. * * @since 1.9.0 */ public function init() { $this->hooks(); } /** * Register hooks. * * @since 1.9.0 */ private function hooks() { // Frontend hooks. add_filter( 'wpforms_frontend_strings', [ $this, 'add_frontend_strings' ] ); add_filter( 'wpforms_frontend_fields_base_level', [ $this, 'get_random_field' ], 20 ); add_action( 'wpforms_display_field_before', [ $this, 'maybe_insert_honeypot_field' ], 1, 2 ); add_action( 'wpforms_display_fields_after', [ $this, 'maybe_insert_honeypot_init_js' ] ); // Builder hooks. add_filter( 'wpforms_builder_panel_settings_init_form_data', [ $this, 'init_builder_settings_form_data' ] ); add_filter( 'wpforms_admin_builder_templates_apply_to_new_form_modify_data', [ $this, 'update_template_form_data' ] ); add_filter( 'wpforms_admin_builder_templates_apply_to_existing_form_modify_data', [ $this, 'update_template_form_data' ] ); add_filter( 'wpforms_templates_class_base_template_modify_data', [ $this, 'update_template_form_data' ] ); add_filter( 'wpforms_templates_class_base_template_replace_modify_data', [ $this, 'update_template_form_data' ] ); add_filter( 'wpforms_form_handler_convert_form_data', [ $this, 'update_template_form_data' ] ); } /** * Store a random field id to insert a honeypot field later. * * @since 1.9.0 * * @param array|mixed $fields_data Form fields data. * * @return array|mixed Form fields data. */ public function get_random_field( $fields_data ) { if ( ! is_array( $fields_data ) ) { return $fields_data; } $random_field_id = array_rand( $fields_data ); if ( ! empty( $random_field_id ) ) { $this->insert_before_field_id = $random_field_id; } return $fields_data; } /** * Insert honeypot field before a random field. * * @since 1.9.0 * * @param array $field Field. * @param array $form_data Form data. */ public function maybe_insert_honeypot_field( array $field, array $form_data ) { if ( $this->insert_before_field_id !== (int) $field['id'] || ! $this->is_honeypot_enabled( $form_data ) ) { return; } $honeypot_field_id = $this->get_honeypot_field_id( $form_data ); $form_id = (int) $form_data['id']; $label = $this->get_honeypot_label( $form_data ); $id_attr = sprintf( 'wpforms-%1$s-field_%2$s', $form_id, $honeypot_field_id ); $is_amp = wpforms_is_amp(); $this->forms_data[ $form_id ] = $honeypot_field_id; if ( $is_amp ) { echo '<amp-layout layout="nodisplay">'; } ?> <div id="<?php echo esc_attr( $id_attr ); ?>-container" class="wpforms-field wpforms-field-text" data-field-type="text" data-field-id="<?php echo esc_attr( $honeypot_field_id ); ?>" > <label class="wpforms-field-label" for="<?php echo esc_attr( $id_attr ); ?>" ><?php echo esc_html( $label ); ?></label> <input type="text" id="<?php echo esc_attr( $id_attr ); ?>" class="wpforms-field-medium" name="wpforms[fields][<?php echo esc_attr( $honeypot_field_id ); ?>]" > </div> <?php if ( $is_amp ) { echo '</amp-layout>'; } } /** * Insert the inline styles. * * @since 1.9.0 * * @param array $form_data Form data. * * @noinspection PhpUnusedParameterInspection */ public function maybe_insert_honeypot_init_js( array $form_data ) { // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.Found if ( ! $this->forms_data || wpforms_is_amp() ) { return; } $ids = []; foreach ( $this->forms_data as $form_id => $honeypot_field_id ) { $ids[] = sprintf( '#wpforms-%1$d-field_%2$d-container', $form_id, $honeypot_field_id ); } if ( ! $ids ) { return; } $styles = sprintf( '%1$s { position: absolute !important; overflow: hidden !important; display: inline !important; height: 1px !important; width: 1px !important; z-index: -1000 !important; padding: 0 !important; } %1$s input { visibility: hidden; } #wpforms-conversational-form-page %1$s label { counter-increment: none; }', esc_attr( implode( ',', $ids ) ) ); // There must be no empty lines inside the script. Otherwise, wpautop adds <p> tags which break script execution. printf( "<script> ( function() { const style = document.createElement( 'style' ); style.appendChild( document.createTextNode( '%s' ) ); document.head.appendChild( style ); document.currentScript?.remove(); } )(); </script>", esc_js( $styles ) ); } /** * Get honeypot field label. * * @since 1.9.0 * * @param array $form_data Form data. */ private function get_honeypot_label( array $form_data ): string { $labels = []; foreach ( $form_data['fields'] ?? [] as $field ) { if ( ! empty( $field['label'] ) ) { $labels[] = $field['label']; } } $words = explode( ' ', implode( ' ', $labels ) ); $count_words = count( $words ); $label_keys = (array) array_rand( $words, min( $count_words, 3 ) ); shuffle( $label_keys ); $label_words = array_map( static function ( $key ) use ( $words ) { return $words[ $key ]; }, $label_keys ); return implode( ' ', $label_words ); } /** * Add strings to the frontend. * * @since 1.9.0 * * @param array|mixed $strings Frontend strings. * * @return array Frontend strings. */ public function add_frontend_strings( $strings ): array { $strings = (array) $strings; // Store the honeypot field ID for validation and adding inline styles. $strings['hn_data'] = $this->forms_data; return $strings; } /** * Validate whether the modern Anti-Spam is enabled. * * @since 1.9.0 * * @param array $form_data Form data. * @param array $fields Fields. * @param array $entry Form submission raw data ($_POST). * * @return bool True if the entry is valid, false otherwise. * @noinspection PhpUnusedParameterInspection */ public function validate( array $form_data, array $fields, array &$entry ): bool { // Bail out if the modern Anti-Spam is not enabled. if ( ! $this->is_honeypot_enabled( $form_data ) ) { return true; } $honeypot_fields = array_diff_key( $entry['fields'], $form_data['fields'] ); $is_valid = true; // Compatibility with the WPML plugin (WPFML addon). // In case the form contains an Entry Preview field, they add an extra field with ID 0 to the entry. if ( isset( $entry['fields'][0] ) && defined( 'WPML_WP_FORMS_VERSION' ) && wpforms_has_field_type( 'entry-preview', $form_data ) ) { unset( $honeypot_fields[0] ); } foreach ( $honeypot_fields as $key => $honeypot_field ) { // Remove the honeypot field from the entry. unset( $entry['fields'][ $key ] ); // If the honeypot field is not empty, the entry is invalid. if ( ! empty( $honeypot_field ) ) { $is_valid = false; } } return $is_valid; } /** * Check if the modern Anti-Spam is enabled. * * @since 1.9.0 * * @param array $form_data Form data. * * @return bool True if the modern Anti-Spam is enabled, false otherwise. */ private function is_honeypot_enabled( array $form_data ): bool { static $is_enabled; if ( isset( $is_enabled ) ) { return $is_enabled; } /** * Filters whether the modern Anti-Spam is enabled. * * @since 1.9.0 * * @param bool $is_enabled True if the modern Anti-Spam is enabled, false otherwise. */ $is_enabled = (bool) apply_filters( 'wpforms_forms_anti_spam_v3_is_honeypot_enabled', ! empty( $form_data['settings']['antispam_v3'] ) ); return $is_enabled; } /** * Get the honeypot field ID. * * @since 1.9.0 * * @param array $form_data Form data. * * @return int Honeypot field ID. */ private function get_honeypot_field_id( array $form_data ): int { $max_key = max( array_keys( $form_data['fields'] ) ); // Find the first available field ID. for ( $i = 1; $i <= $max_key; $i++ ) { if ( ! isset( $form_data['fields'][ $i ] ) ) { return $i; } } // If no available field ID found, use the max ID + 1. return $max_key + 1; } /** * Update the form data on the builder settings panel. * * @since 1.9.0 * * @param array|bool $form_data Form data. * * @return array|bool */ public function init_builder_settings_form_data( $form_data ) { if ( ! $form_data ) { return $form_data; } // Update default time limit duration for the existing form. if ( empty( $form_data['settings']['anti_spam']['time_limit']['enable'] ) ) { $form_data['settings']['anti_spam']['time_limit']['duration'] = '2'; } return $form_data; } /** * Update the template form data. Set the modern Anti-Spam setting. * * @since 1.9.0 * * @param array|mixed $form_data Form data. * * @return array */ public function update_template_form_data( $form_data ): array { $form_data = (array) $form_data; // Unset the old Anti-Spam setting. unset( $form_data['settings']['antispam'] ); // Enable the modern Anti-Spam setting. $form_data['settings']['antispam_v3'] = $form_data['settings']['antispam_v3'] ?? '1'; $form_data['settings']['anti_spam'] = $form_data['settings']['anti_spam'] ?? []; // Enable the time limit setting. $form_data['settings']['anti_spam']['time_limit'] = [ 'enable' => '1', 'duration' => '2', ]; return $form_data; } } Honeypot.php 0000644 00000003374 15252506741 0007100 0 ustar 00 <?php namespace WPForms\Forms; /** * Class Honeypot. * * @since 1.6.2 */ class Honeypot { /** * Initialise the actions for the Honeypot. * * @since 1.6.2 */ public function init() { $this->hooks(); } /** * Register hooks. * * @since 1.6.2 */ public function hooks() { add_action( 'wpforms_frontend_output', [ $this, 'render' ], 15, 5 ); } /** * Return function to render the honeypot. * * @since 1.6.2 * * @param array $form_data Form data and settings. */ public function render( $form_data ) { if ( empty( $form_data['settings']['honeypot'] ) || '1' !== $form_data['settings']['honeypot'] ) { return; } $names = [ 'Name', 'Phone', 'Comment', 'Message', 'Email', 'Website' ]; echo '<div class="wpforms-field wpforms-field-hp">'; echo '<label for="wpforms-' . $form_data['id'] . '-field-hp" class="wpforms-field-label">' . $names[ array_rand( $names ) ] . '</label>'; // phpcs:ignore echo '<input type="text" name="wpforms[hp]" id="wpforms-' . $form_data['id'] . '-field-hp" class="wpforms-field-medium">'; // phpcs:ignore echo '</div>'; } /** * Validate honeypot. * * @since 1.6.2 * * @param array $form_data Form data. * @param array $fields Fields. * @param array $entry Form entry. * * @return bool|string False or an string with the error. */ public function validate( array $form_data, array $fields, array $entry ) { $honeypot = false; if ( ! empty( $form_data['settings']['honeypot'] ) && '1' === $form_data['settings']['honeypot'] && ! empty( $entry['hp'] ) ) { $honeypot = esc_html__( 'WPForms honeypot field triggered.', 'wpforms-lite' ); } return apply_filters( 'wpforms_process_honeypot', $honeypot, $fields, $entry, $form_data ); } } Submission.php 0000644 00000015620 15252506741 0007423 0 ustar 00 <?php namespace WPForms\Forms; /** * Class Submission. * * @since 1.7.4 */ class Submission { /** * The form fields. * * @since 1.7.4 * * @var array */ protected $fields; /** * The form entry. * * @since 1.7.4 * * @var array */ private $entry; /** * The form ID. * * @since 1.7.4 * * @var int */ private $form_id; /** * The form data. * * @since 1.7.4 * * @var array */ protected $form_data; /** * The date. * * @since 1.7.4 * * @var string */ private $date; /** * Register the submission data. * * @since 1.7.4 * @since 1.8.2 Added a return of instance. * * @param array $fields The form fields. * @param array $entry The form entry. * @param int $form_id The form ID. * @param array $form_data The form data. * * @return Submission */ public function register( array $fields, array $entry, $form_id, array $form_data = [] ) { $this->fields = $fields; $this->entry = $entry; $this->form_id = $form_id; $this->form_data = $form_data; $this->date = gmdate( 'Y-m-d H:i:s' ); return $this; } /** * Prepare the submission data. * * @since 1.7.4 * * @return array|void */ public function prepare_entry_data() { /** * Provide the opportunity to disable entry saving. * * @since 1.0.0 * * @param bool $entry_save Entry save flag. Defaults to true. * @param array $fields Fields data. * @param array $entry Entry data. * @param array $form_data Form data. */ if ( ! apply_filters( 'wpforms_entry_save', true, $this->fields, $this->entry, $this->form_data ) ) { // phpcs:ignore WPForms.PHP.ValidateHooks.InvalidHookName return; } $submitted_fields = $this->get_fields(); $user_info = $this->get_user_info( $submitted_fields ); /** * Information about the entry, that is ready to be saved into the main entries table, * which is used for displaying a list of entries and partially for search. * * @since 1.5.9 * * @param array $entry_data Information about the entry, that will be saved into the DB. * @param array $form_data Form data. */ return (array) apply_filters( // phpcs:ignore WPForms.PHP.ValidateHooks.InvalidHookName 'wpforms_entry_save_args', [ 'form_id' => absint( $this->form_id ), 'user_id' => absint( $user_info['user_id'] ), 'fields' => wp_json_encode( $submitted_fields ), 'ip_address' => sanitize_text_field( $user_info['user_ip'] ), 'user_agent' => sanitize_text_field( $user_info['user_agent'] ), 'date' => $this->date, 'user_uuid' => sanitize_text_field( $user_info['user_uuid'] ), ], $this->form_data ); } /** * Prepare the payment submission data. * * @since 1.8.2 * * @return array */ public function prepare_payment_data() { $submitted_fields = $this->get_fields(); $total_amount = wpforms_get_total_payment( $submitted_fields ); /** * Information about the payment, that is ready to be saved into the main payments table, * which is used for displaying a list of payments and partially for search. * * @since 1.8.2 * * @param array $payment_data Information about the payment, that will be saved into the DB. * @param array $fields Final/sanitized submitted field data. * @param array $form_data Form data and settings. */ $payment_data = (array) apply_filters( 'wpforms_forms_submission_prepare_payment_data', [ 'form_id' => absint( $this->form_id ), 'subtotal_amount' => $total_amount, 'total_amount' => $total_amount, 'currency' => wpforms_get_currency(), 'entry_id' => absint( $this->entry['entry_id'] ), 'date_created_gmt' => $this->date, 'date_updated_gmt' => $this->date, ], $submitted_fields, $this->form_data ); if ( empty( $payment_data['type'] ) ) { $payment_data['type'] = ! empty( $payment_data['subscription_id'] ) ? 'subscription' : 'one-time'; } return $payment_data; } /** * Prepare the payment meta data for each payment. * * @since 1.8.2 * * @return array */ public function prepare_payment_meta() { $submitted_fields = $this->get_fields(); $user_info = $this->get_user_info( $submitted_fields ); /** * Payment meta that is ready to be saved into the payments_meta table. * * @since 1.8.2 * * @param array $payment_meta Payment meta that will be saved into the DB. * @param array $fields Final/sanitized submitted field data. * @param array $form_data Form data and settings. */ return (array) apply_filters( 'wpforms_forms_submission_prepare_payment_meta', [ 'fields' => ! $this->entry['entry_id'] ? wp_json_encode( $submitted_fields ) : '', 'user_id' => absint( $user_info['user_id'] ), 'user_agent' => sanitize_text_field( $user_info['user_agent'] ), 'user_uuid' => sanitize_text_field( $user_info['user_uuid'] ), 'ip_address' => sanitize_text_field( $user_info['user_ip'] ), ], $submitted_fields, $this->form_data ); } /** * Get entry fields. * * @since 1.8.2 * * @return array */ private function get_fields() { /** * Filter the entry data before saving. * * @since 1.0.0 * * @param array $fields Fields data. * @param array $entry Entry data. * @param array $form_data Form data. */ return (array) apply_filters( 'wpforms_entry_save_data', $this->fields, $this->entry, $this->form_data ); // phpcs:ignore WPForms.PHP.ValidateHooks.InvalidHookName } /** * Get user info. * * @since 1.8.2 * * @param array $fields Fields data. * * @return array */ private function get_user_info( $fields ) { $user_info = [ 'user_ip' => '', 'user_agent' => '', 'user_id' => is_user_logged_in() ? get_current_user_id() : 0, 'user_uuid' => wpforms_is_collecting_cookies_allowed() && ! empty( $_COOKIE['_wpfuuid'] ) ? sanitize_key( $_COOKIE['_wpfuuid'] ) : '', ]; /** * Allow developers disable saving user IP and User Agent within the entry. * * @since 1.5.1 * * @param bool $disable True if you need to disable storing IP and UA within the entry. Defaults to false. * @param array $fields Fields data. * @param array $form_data Form data. */ // phpcs:ignore WPForms.PHP.ValidateHooks.InvalidHookName $is_ip_disabled = apply_filters( 'wpforms_disable_entry_user_ip', '__return_false', $fields, $this->form_data ); // If GDPR enhancements are enabled and user details are disabled // globally or in the form settings, discard the IP and UA. if ( ! $is_ip_disabled || ! wpforms_is_collecting_ip_allowed( $this->form_data ) ) { return $user_info; } $user_info['user_ip'] = wpforms_get_ip(); if ( empty( $_SERVER['HTTP_USER_AGENT'] ) ) { return $user_info; } $user_info['user_agent'] = substr( sanitize_text_field( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ), 0, 256 ); return $user_info; } } Preview.php 0000644 00000024476 15252506741 0006722 0 ustar 00 <?php namespace WPForms\Forms; /** * Form preview. * * @since 1.5.1 */ class Preview { /** * Form data. * * @since 1.5.1 * * @var array */ public $form_data; /** * Post type. * * @since 1.8.8 * * @var string */ private $post_type; /** * Whether this is a form template. * * @since 1.8.8 * * @var bool */ private $is_form_template; /** * Constructor. * * @since 1.5.1 */ public function __construct() { if ( ! $this->is_preview_page() ) { return; } $this->hooks(); } /** * Check if current page request meets requirements for form preview page. * * @since 1.5.1 * * @return bool */ public function is_preview_page(): bool { // Only proceed for the form preview page. // phpcs:ignore WordPress.Security.NonceVerification.Recommended if ( empty( $_GET['wpforms_form_preview'] ) ) { return false; } // Only logged-in users can access the preview page. if ( ! is_user_logged_in() ) { return false; } // phpcs:ignore WordPress.Security.NonceVerification.Recommended $form_id = absint( $_GET['wpforms_form_preview'] ); // Make sure the user is allowed to preview the form. if ( ! wpforms_current_user_can( 'view_form_single', $form_id ) ) { return false; } // Fetch form details. $this->form_data = wpforms()->obj( 'form' )->get( $form_id, [ 'content_only' => true ] ); // Get the post type for preview item. $this->post_type = get_post_type( $form_id ); // Check if this is a form template. $this->is_form_template = $this->post_type === 'wpforms-template'; // Check valid form was found. if ( empty( $this->form_data ) || empty( $this->form_data['id'] ) ) { return false; } return true; } /** * Hooks. * * @since 1.5.1 */ public function hooks() { add_filter( 'wpforms_frontend_assets_header_force_load', '__return_true' ); add_action( 'wp_enqueue_scripts', [ $this, 'enqueue_assets' ] ); add_action( 'pre_get_posts', [ $this, 'pre_get_posts' ] ); add_filter( 'the_title', [ $this, 'the_title' ], 100, 1 ); add_filter( 'the_content', [ $this, 'the_content' ], 999 ); add_filter( 'get_the_excerpt', [ $this, 'the_content' ], 999 ); add_filter( 'home_template_hierarchy', [ $this, 'force_page_template_hierarchy' ] ); add_filter( 'frontpage_template_hierarchy', [ $this, 'force_page_template_hierarchy' ] ); add_filter( 'wpforms_smarttags_process_page_title_value', [ $this, 'smart_tags_process_page_title_value' ], 10, 5 ); add_filter( 'post_thumbnail_html', '__return_empty_string' ); } /** * Enqueue additional form preview styles. * * @since 1.8.8 */ public function enqueue_assets() { $min = wpforms_get_min_suffix(); // Enqueue the form preview styles. wp_enqueue_style( 'wpforms-preview', WPFORMS_PLUGIN_URL . "assets/css/frontend/wpforms-form-preview{$min}.css", [], WPFORMS_VERSION ); } /** * Modify query, limit to one post. * * @since 1.5.1 * @since 1.7.0 Added `page_id`, `post_type` and `post__in` query variables. * * @param \WP_Query $query The WP_Query instance. */ public function pre_get_posts( $query ) { if ( is_admin() || ! $query->is_main_query() ) { return; } $query->set( 'page_id', '' ); $query->set( 'post_type', $this->post_type ?? 'wpforms' ); $query->set( 'post__in', empty( $this->form_data['id'] ) ? [] : [ (int) $this->form_data['id'] ] ); $query->set( 'posts_per_page', 1 ); // The preview page reads as the home page and as an non-singular posts page, neither of which are actually the case. // So we hardcode the correct values for those properties in the query. $query->is_home = false; $query->is_singular = true; $query->is_single = true; } /** * Customize form preview page title. * * @since 1.5.1 * * @param string $title Page title. * * @return string */ public function the_title( $title ) { if ( ! in_the_loop() ) { return $title; } if ( $this->is_form_template ) { return sprintf( /* translators: %s - form name. */ esc_html__( '%s Template Preview', 'wpforms-lite' ), ! empty( $this->form_data['settings']['form_title'] ) ? sanitize_text_field( $this->form_data['settings']['form_title'] ) : esc_html__( 'Form Template', 'wpforms-lite' ) ); } return sprintf( /* translators: %s - form name. */ esc_html__( '%s Preview', 'wpforms-lite' ), ! empty( $this->form_data['settings']['form_title'] ) ? sanitize_text_field( $this->form_data['settings']['form_title'] ) : esc_html__( 'Form', 'wpforms-lite' ) ); } /** * Customize form preview page content. * * @since 1.5.1 * * @return string */ public function the_content() { if ( ! isset( $this->form_data['id'] ) ) { return ''; } if ( ! wpforms_current_user_can( 'view_form_single', $this->form_data['id'] ) ) { return ''; } $admin_url = admin_url( 'admin.php' ); $links = []; if ( wpforms_current_user_can( 'edit_form_single', $this->form_data['id'] ) ) { $links[] = [ 'url' => esc_url( add_query_arg( [ 'page' => 'wpforms-builder', 'view' => 'fields', 'form_id' => absint( $this->form_data['id'] ), ], $admin_url ) ), 'text' => $this->is_form_template ? esc_html__( 'Edit Form Template', 'wpforms-lite' ) : esc_html__( 'Edit Form', 'wpforms-lite' ), ]; } if ( wpforms()->is_pro() && wpforms_current_user_can( 'view_entries_form_single', $this->form_data['id'] ) ) { $links[] = [ 'url' => esc_url( add_query_arg( [ 'page' => 'wpforms-entries', 'view' => 'list', 'form_id' => absint( $this->form_data['id'] ), ], $admin_url ) ), 'text' => esc_html__( 'View Entries', 'wpforms-lite' ), ]; } if ( ! $this->is_form_template && wpforms_current_user_can( wpforms_get_capability_manage_options(), $this->form_data['id'] ) && wpforms()->obj( 'payment' )->get_by( 'form_id', $this->form_data['id'] ) ) { $links[] = [ 'url' => esc_url( add_query_arg( [ 'page' => 'wpforms-payments', 'form_id' => absint( $this->form_data['id'] ), ], $admin_url ) ), 'text' => esc_html__( 'View Payments', 'wpforms-lite' ), ]; } if ( ! empty( $_GET['new_window'] ) ) { // phpcs:ignore $links[] = [ 'url' => 'javascript:window.close();', 'text' => esc_html__( 'Close this window', 'wpforms-lite' ), ]; } $content = ''; $content .= $this->add_preview_notice(); $content .= '<p>'; $content .= $this->is_form_template ? esc_html__( 'This is a preview of the latest saved revision of your form template. If this preview does not match your template, save your changes and then refresh this page. This template preview is not publicly accessible.', 'wpforms-lite' ) : esc_html__( 'This is a preview of the latest saved revision of your form. If this preview does not match your form, save your changes and then refresh this page. This form preview is not publicly accessible.', 'wpforms-lite' ); if ( ! empty( $links ) ) { $content .= '<br>'; $content .= '<span class="wpforms-preview-notice-links">'; foreach ( $links as $key => $link ) { $content .= '<a href="' . $link['url'] . '">' . $link['text'] . '</a>'; $l = array_keys( $links ); if ( end( $l ) !== $key ) { $content .= ' <span style="display:inline-block;margin:0 6px;opacity: 0.5">|</span> '; } } $content .= '</span>'; } $content .= '</p>'; $content .= '<p>'; $content .= sprintf( wp_kses( /* translators: %s - WPForms doc link. */ __( 'For form testing tips, check out our <a href="%s" target="_blank" rel="noopener noreferrer">complete guide!</a>', 'wpforms-lite' ), [ 'a' => [ 'href' => [], 'target' => [], 'rel' => [], ], ] ), esc_url( wpforms_utm_link( 'https://wpforms.com/docs/how-to-properly-test-your-wordpress-forms-before-launching-checklist/', $this->is_form_template ? 'Form Template Preview' : 'Form Preview', 'Form Testing Tips Documentation' ) ) ); $content .= '</p>'; $content .= do_shortcode( '[wpforms id="' . absint( $this->form_data['id'] ) . '"]' ); return $content; } /** * Add preview notice. * * @since 1.8.8 * * @return string HTML content. */ private function add_preview_notice(): string { if ( ! $this->is_form_template ) { return ''; } $content = '<div class="wpforms-preview-notice">'; $content .= sprintf( '<strong>%s</strong> %s', esc_html__( 'Heads up!', 'wpforms-lite' ), esc_html__( 'You\'re viewing a preview of a form template.', 'wpforms-lite' ) ); if ( wpforms()->is_pro() ) { /** This filter is documented in wpforms/src/Pro/Tasks/Actions/PurgeTemplateEntryTask.php */ $delay = (int) apply_filters( 'wpforms_pro_tasks_actions_purge_template_entry_task_delay', DAY_IN_SECONDS ); // phpcs:ignore WPForms.PHP.ValidateHooks.InvalidHookName $message = sprintf( /* translators: %s - time period, e.g. 24 hours. */ __( 'Entries are automatically deleted after %s.', 'wpforms-lite' ), // The `- 1` hack is to avoid the "1 day" message in favor of "24 hours". human_time_diff( time(), time() + $delay - 1 ) ); $content .= sprintf( '<p>%s</p>', esc_html( $message ) ); } $content .= '</div>'; return wp_kses_post( $content ); } /** * Force page template types. * * @since 1.7.2 * * @param array $templates A list of template candidates, in descending order of priority. * * @return array */ public function force_page_template_hierarchy( $templates ) { return [ 'page.php', 'single.php', 'index.php' ]; } /** * Adjust value of the {page_title} smart tag. * * @since 1.7.7 * * @param string $content Content. * @param array $form_data Form data. * @param array $fields List of fields. * @param string $entry_id Entry ID. * @param object $smart_tag_object The smart tag object or the Generic object for those cases when class unregistered. * * @return string */ public function smart_tags_process_page_title_value( $content, $form_data, $fields, $entry_id, $smart_tag_object ) { return sprintf( /* translators: %s - form name. */ esc_html__( '%s Preview', 'wpforms-lite' ), ! empty( $form_data['settings']['form_title'] ) ? sanitize_text_field( $form_data['settings']['form_title'] ) : esc_html__( 'Form', 'wpforms-lite' ) ); } } Token.php 0000644 00000025046 15252506741 0006353 0 ustar 00 <?php namespace WPForms\Forms; /** * Class Token. * * This token class generates tokens that are used in our Anti-Spam checking mechanism. * * @since 1.6.2 */ class Token { /** * Initialise the actions for the Anti-spam. * * @since 1.6.2 */ public function init() { $this->hooks(); } /** * Register hooks. * * @since 1.6.2 */ public function hooks() { add_filter( 'wpforms_frontend_form_atts', [ $this, 'add_token_to_form_atts' ], 10, 2 ); add_filter( 'wpforms_frontend_strings', [ $this, 'add_frontend_strings' ] ); add_action( 'wp_ajax_nopriv_wpforms_get_token', [ $this, 'ajax_get_token' ] ); add_action( 'wp_ajax_wpforms_get_token', [ $this, 'ajax_get_token' ] ); } /** * Return a valid token. * * @since 1.6.2 * @since 1.7.1 Added the $form_data argument. * * @param mixed $current True to use current time, otherwise a timestamp string. * @param array $form_data Form data and settings. * * @return string Token. */ public function get( $current = true, $form_data = [] ) { // If $current was not passed, or it is true, we use the current timestamp. // If $current was passed in as a string, we'll use that passed in timestamp. if ( $current !== true ) { $time = $current; } else { $time = time(); } // Format the timestamp to be less exact, as we want to deal in days. // June 19th, 2020 would get formatted as: 1906202017125. // Day of the month, month number, year, day number of the year, week number of the year. $token_data = gmdate( 'dmYzW', $time ); if ( ! empty( $form_data['id'] ) ) { $token_data .= "::{$form_data['id']}"; } // Combine our token date and our token salt, and md5 it. return md5( $token_data . \WPForms\Helpers\Crypto::get_secret_key() ); } /** * Generate the array of valid tokens to check for. These include two days * before the current date to account for long cache times. * * These two filters are available if a user wants to extend the times. * 'wpforms_form_token_check_before_today' * 'wpforms_form_token_check_after_today' * * @since 1.6.2 * @since 1.7.1 Added the $form_data argument. * * @param array $form_data Form data and settings. * * @return array Array of all valid tokens to check against. */ public function get_valid_tokens( $form_data = [] ) { $current_date = time(); $valid_token_times_before = []; $days_in_5_years = 5 * 365; // Create an array of 5 years worth of days. for ( $i = 1; $i <= $days_in_5_years; $i++ ) { $valid_token_times_before[] = $i * DAY_IN_SECONDS; } // Create our array of times to check before today. A user with a longer // cache time can extend this. A user with a shorter cache time can remove times. $valid_token_times_before = apply_filters( 'wpforms_form_token_check_before_today', $valid_token_times_before ); // Mostly to catch edge cases like the form page loading and submitting on two different days. // This probably won't be filtered by users too much, but they could extend it. $valid_token_times_after = apply_filters( 'wpforms_form_token_check_after_today', [ ( 45 * MINUTE_IN_SECONDS ), // Add in 45 minutes past today to catch some midnight edge cases. ] ); // Built up our valid tokens. $valid_tokens = []; // Add in all the previous times we check. foreach ( $valid_token_times_before as $time ) { $valid_tokens[] = $this->get( $current_date - $time, $form_data ); } // Add in our current date. $valid_tokens[] = $this->get( $current_date, $form_data ); // Add in the times after our check. foreach ( $valid_token_times_after as $time ) { $valid_tokens[] = $this->get( $current_date + $time, $form_data ); } return $valid_tokens; } /** * Check if the given token is valid or not. * * Tokens are valid for some period of time (see wpforms_token_validity_in_hours * and wpforms_token_validity_in_days to extend the validation period). * By default tokens are valid for day. * * @since 1.6.2 * @since 1.7.1 Added the $form_data argument. * * @param string $token Token to validate. * @param array $form_data Form data and settings. * * @return bool Whether the token is valid or not. */ public function verify( string $token, array $form_data = [] ): bool { // Check to see if our token is inside the valid tokens. return in_array( $token, $this->get_valid_tokens( $form_data ), true ); } /** * Add the token to the form attributes. * * @since 1.6.2 * @since 1.7.1 Added the $form_data argument. * * @param array $attrs Form attributes. * @param array $form_data Form data and settings. * * @return array Form attributes. */ public function add_token_to_form_atts( array $attrs, array $form_data ) { $attrs['atts']['data-token'] = $this->get( true, $form_data ); $attrs['atts']['data-token-time'] = time(); return $attrs; } /** * Validate Anti-spam if enabled. * * @since 1.6.2 * * @param array $form_data Form data. * @param array $fields Fields. * @param array $entry Form entry. * * @return bool|string True or a string with the error. */ public function validate( array $form_data, array $fields, array $entry ) { // Bail out if we don't have the antispam setting. if ( empty( $form_data['settings']['antispam'] ) ) { return true; } // Bail out if the antispam setting isn't enabled. if ( $form_data['settings']['antispam'] !== '1' ) { return true; } $is_valid_token = isset( $entry['token'] ) && $this->verify( (string) $entry['token'], $form_data ); if ( $this->process_antispam_filter_wrapper( $is_valid_token, $fields, $entry, $form_data ) ) { return true; } // Prepare the log data. $form_title = $form_data['settings']['form_title'] ?? ''; $form_id = $form_data['id'] ?? 'unknown'; if ( $is_valid_token ) { // Token is OK, but antispam filter is not passed. $log_message = 'Filter is not passed'; $error_message = $this->get_antispam_filter_message(); } else { // Invalid token. $log_message = 'Token is invalid'; $error_message = $this->get_invalid_token_message(); } wpforms_log( 'Antispam: ' . $log_message, [ 'message' => $error_message, 'referer' => esc_url_raw( (string) wp_get_referer() ), 'form' => ! empty( $form_title ) ? $form_title . ' (ID: ' . $form_id . ')' : 'ID: ' . $form_id, 'token' => $entry['token'] ?? '', 'user_ip' => wpforms_get_ip(), 'entry_data' => ! wpforms_setting( 'gdpr' ) ? $entry : 'Not logged', ], [ 'type' => [ 'spam', 'error' ], 'form_id' => $form_data['id'], 'force' => true, ] ); return $error_message; } /** * Helper to run our filter on all the responses for the antispam checks. * * @since 1.6.2 * * @param bool $is_valid_not_spam Is valid entry or not. * @param array $fields Form Fields. * @param array $entry Form entry. * @param array $form_data Form Data. * * @return bool Is valid or not. */ public function process_antispam_filter_wrapper( bool $is_valid_not_spam, array $fields, array $entry, array $form_data ): bool { /** * Allows developers to filter the antispam check result. * * @since 1.6.2 * * @param bool $is_valid_not_spam True if entry valid, false otherwise. * @param array $fields Fields data. * @param array $entry Entry data. * @param array $form_data Form data. */ return (bool) apply_filters( 'wpforms_process_antispam', $is_valid_not_spam, $fields, $entry, $form_data ); // phpcs:ignore WPForms.PHP.ValidateHooks.InvalidHookName } /** * Helper to get the invalid token message. * * @since 1.6.2.1 * * @return string Invalid token message. */ private function get_invalid_token_message(): string { return $this->get_error_message( esc_html__( 'Antispam token is invalid.', 'wpforms-lite' ) ); } /** * Helper to get the antispam filter error message. * * @since 1.8.9 * * @return string Missing token message. */ private function get_antispam_filter_message(): string { return $this->get_error_message( esc_html__( 'Antispam filter did not allow your data to pass through.', 'wpforms-lite' ) ); } /** * Get error message depends on user. * * @since 1.6.4.1 * * @param string $text Message text. * * @return string */ private function get_error_message( string $text ): string { $text .= ' ' . esc_html__( 'Please reload the page and try submitting the form again.', 'wpforms-lite' ); return wpforms_current_user_can() ? $text . $this->maybe_get_support_text() : $text; } /** * If a user is a super admin, add a support link to the message. * * @since 1.6.2.1 * * @return string Support text if super admin, empty string if not. */ private function maybe_get_support_text(): string { // If a user isn't a super admin, don't return any text. if ( ! is_super_admin() ) { return ''; } // If the user is an admin, return text with a link to support. // We add a space here to separate the sentences, but outside the localized text to avoid it being removed. return ' ' . sprintf( /* translators: placeholders are links. */ esc_html__( 'Please check out our %1$stroubleshooting guide%2$s for details on resolving this issue.', 'wpforms-lite' ), '<a href="https://wpforms.com/docs/getting-support-wpforms/">', '</a>' ); } /** * Add token related strings to the frontend. * * @since 1.8.8 * * @param array|mixed $strings Frontend strings. * * @return array Frontend strings. */ public function add_frontend_strings( $strings ): array { $strings = (array) $strings; $strings['error_updating_token'] = esc_html__( 'Error updating token. Please try again or contact support if the issue persists.', 'wpforms-lite' ); $strings['network_error'] = esc_html__( 'Network error or server is unreachable. Check your connection or try again later.', 'wpforms-lite' ); // Default token lifetime is 24 hours in seconds. $token_lifetime = DAY_IN_SECONDS; /** * Filter token cache lifetime in seconds. * * @since 1.8.8 * * @param integer $token_lifetime Token lifetime in seconds. */ $strings['token_cache_lifetime'] = apply_filters( 'wpforms_forms_token_cache_lifetime', $token_lifetime ); return $strings; } /** * Update token via ajax handler. * * @since 1.8.8 */ public function ajax_get_token() { $form_data = []; $form_data['id'] = filter_input( INPUT_POST, 'formId', FILTER_VALIDATE_INT ); $response = [ 'token' => $this->get( true, $form_data ), ]; wp_send_json_success( $response ); } } Fields/Hidden/Field.php 0000644 00000007161 15252506741 0010715 0 ustar 00 <?php namespace WPForms\Forms\Fields\Hidden; use WPForms\Forms\Fields\Traits\ProField as ProFieldTrait; use WPForms_Field; /** * Hidden text field. * * @since 1.9.4 */ class Field extends WPForms_Field { use ProFieldTrait; /** * Primary class constructor. * * @since 1.9.4 */ public function init() { // Define field type information. $this->name = esc_html__( 'Hidden Field', 'wpforms-lite' ); $this->type = 'hidden'; $this->icon = 'fa-eye-slash'; $this->order = 305; $this->group = 'fancy'; $this->allow_read_only = false; $this->default_settings = [ 'label_hide' => '1', ]; $this->init_pro_field(); $this->hooks(); } /** * Hooks. * * @since 1.9.4 */ protected function hooks(): void { add_filter( 'wpforms_field_new_class', [ $this, 'preview_field_new_class' ], 10, 2 ); } /** * Field options panel inside the builder. * * @since 1.9.4 * * @param array $field Field data and settings. */ public function field_options( $field ) { /* * Basic field options. */ // Options open markup. $this->field_option( 'basic-options', $field, [ 'markup' => 'open', 'after_title' => $this->get_field_options_notice(), ] ); // Label. $this->field_option( 'label', $field, [ 'tooltip' => esc_html__( 'Enter text for the form field label. Never displayed on the front-end.', 'wpforms-lite' ), ] ); // Set the label to disable. $this->field_element( 'text', $field, [ 'type' => 'hidden', 'slug' => 'label_disable', 'value' => '1', ] ); // Options close markup. $args = [ 'markup' => 'close', ]; $this->field_option( 'basic-options', $field, $args ); // Advanced options open markup. $this->field_option( 'advanced-options', $field, [ 'markup' => 'open', ] ); // Default value. $this->field_option( 'default_value', $field ); // Custom CSS classes. $this->field_option( 'css', $field ); // Hide Label. $this->field_option( 'label_hide', $field, [ 'class' => 'wpforms-disabled', ] ); // Advanced options close markup. $this->field_option( 'advanced-options', $field, [ 'markup' => 'close', ] ); } /** * Get a new field CSS class. * * @since 1.9.4 * * @param string|mixed $css_class Preview new field CSS class. * @param array $field Field data. * * @return string */ public function preview_field_new_class( $css_class, array $field ): string { $css_class = (string) $css_class; if ( empty( $field['type'] ) || $field['type'] !== $this->type ) { return $css_class; } return trim( $css_class . ' label_hide' ); } /** * Field preview inside the builder. * * @since 1.9.4 * * @param array $field Field data and settings. */ public function field_preview( $field ) { // Define data. $default_value = ! empty( $field['default_value'] ) ? $field['default_value'] : ''; // The Hidden field label is always hidden. $field['label_hide'] = '1'; // Label. $this->field_preview_option( 'label', $field, [ 'label_badge' => $this->get_field_preview_badge(), ] ); // Primary input. echo '<input type="text" class="primary-input" value="' . esc_attr( $default_value ) . '" readonly>'; } /** * Field display on the form front-end. * * @since 1.9.4 * * @param array $field Field data and settings. * @param array $deprecated Not used any more field attributes. * @param array $form_data Form data and settings. */ public function field_display( $field, $deprecated, $form_data ) { } } Fields/Html/Field.php 0000644 00000011403 15252506741 0010420 0 ustar 00 <?php namespace WPForms\Forms\Fields\Html; use WPForms\Forms\Fields\Traits\ProField as ProFieldTrait; use WPForms_Field; /** * HTML block text field. * * @since 1.9.4 */ class Field extends WPForms_Field { use ProFieldTrait; /** * Primary class constructor. * * @since 1.9.4 */ public function init() { // Define field type information. $this->name = esc_html__( 'HTML', 'wpforms-lite' ); $this->keywords = esc_html__( 'code', 'wpforms-lite' ); $this->type = 'html'; $this->icon = 'fa-code'; $this->order = 185; $this->group = 'fancy'; $this->allow_read_only = false; $this->default_settings = [ 'name' => '', ]; $this->init_pro_field(); $this->hooks(); } /** * Hooks. * * @since 1.9.4 */ protected function hooks() { } /** * Extend from `parent::field_option()` to add `name` option. * * @since 1.9.4 * * @param string $option Field option to render. * @param array $field Field data and settings. * @param array $args Field preview arguments. * @param bool $do_echo Print or return the value. Print by default. * * @return string|null * @noinspection PhpMissingReturnTypeInspection * @noinspection ReturnTypeCanBeDeclaredInspection */ public function field_option( $option, $field, $args = [], $do_echo = true ) { // phpcs:ignore Universal.NamingConventions.NoReservedKeywordParameterNames.echoFound if ( $option !== 'name' ) { return parent::field_option( $option, $field, $args, $do_echo ); } $output = $this->field_element( 'label', $field, [ 'slug' => 'name', 'value' => esc_html__( 'Label', 'wpforms-lite' ), 'tooltip' => esc_html__( 'Enter text for the form field label. It will help identify your HTML blocks inside the form builder, but will not be displayed in the form.', 'wpforms-lite' ), ], false ); $output .= $this->field_element( 'text', $field, [ 'slug' => 'name', 'value' => ! empty( $field['name'] ) ? esc_attr( $field['name'] ) : '', ], false ); $output = $this->field_element( 'row', $field, [ 'slug' => 'name', 'content' => $output, ], false ); if ( $do_echo ) { echo $output; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped return null; } return $output; } /** * Field options panel inside the builder. * * @since 1.9.4 * * @param array $field Field settings. */ public function field_options( $field ) { /* * Basic field options. */ // Options open markup. $this->field_option( 'basic-options', $field, [ 'markup' => 'open', 'after_title' => $this->get_field_options_notice(), ] ); // Name (Label). $this->field_option( 'name', $field ); // Code. $this->field_option( 'code', $field ); // Set the label to disable. $args = [ 'type' => 'hidden', 'slug' => 'label_disable', 'value' => '1', ]; $this->field_element( 'text', $field, $args ); // Options close markup. $args = [ 'markup' => 'close', ]; $this->field_option( 'basic-options', $field, $args ); /* * Advanced field options. */ // Options open markup. $args = [ 'markup' => 'open', ]; $this->field_option( 'advanced-options', $field, $args ); // Custom CSS classes. $this->field_option( 'css', $field ); // Options close markup. $args = [ 'markup' => 'close', ]; $this->field_option( 'advanced-options', $field, $args ); } /** * Field preview inside the builder. * * @since 1.9.4 * * @param array $field Field settings. */ public function field_preview( $field ) { $label = ! empty( $field['name'] ) ? $field['name'] : ''; $label_badge = empty( $label ) ? '' : $this->get_field_preview_badge(); $code_badge = empty( $label ) ? $this->get_field_preview_badge() : ''; ?> <label class="label-title"> <div class="text"> <?php echo esc_html( $label ) . $label_badge; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?> </div> <div class="grey"> <i class="fa fa-code"></i> <?php esc_html_e( 'HTML / Code Block', 'wpforms-lite' ); ?> <?php echo $code_badge; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?> </div> </label> <div class="description"><?php esc_html_e( 'Contents of this field are not displayed in the form builder preview.', 'wpforms-lite' ); ?></div> <?php } /** * Field display on the form front-end. * * @since 1.9.4 * * @param array $field Field data and settings. * @param array $deprecated Deprecated field attributes. Use field properties. * @param array $form_data Form data and settings. * * @noinspection HtmlUnknownAttribute */ public function field_display( $field, $deprecated, $form_data ) { } } Fields/PaymentCheckbox/Field.php 0000644 00000041432 15252506741 0012605 0 ustar 00 <?php namespace WPForms\Forms\Fields\PaymentCheckbox; use WPForms_Field; /** * Checkbox payment field. * * @since 1.8.2 */ class Field extends WPForms_Field { /** * Primary class constructor. * * @since 1.8.2 */ public function init() { // Define field type information. $this->name = esc_html__( 'Checkbox Items', 'wpforms-lite' ); $this->keywords = esc_html__( 'product, store, ecommerce, pay, payment', 'wpforms-lite' ); $this->type = 'payment-checkbox'; $this->icon = 'fa-check-square-o'; $this->order = 50; $this->group = 'payment'; $this->defaults = [ 1 => [ 'label' => esc_html__( 'First Item', 'wpforms-lite' ), 'value' => '10', 'image' => '', 'icon' => '', 'icon_style' => '', 'default' => '', ], 2 => [ 'label' => esc_html__( 'Second Item', 'wpforms-lite' ), 'value' => '25', 'image' => '', 'icon' => '', 'icon_style' => '', 'default' => '', ], 3 => [ 'label' => esc_html__( 'Third Item', 'wpforms-lite' ), 'value' => '50', 'image' => '', 'icon' => '', 'icon_style' => '', 'default' => '', ], ]; $this->default_settings = [ 'choices' => $this->defaults, ]; $this->hooks(); } /** * Register hooks. * * @since 1.8.1 */ private function hooks() { // Customize HTML field values. add_filter( 'wpforms_html_field_value', [ $this, 'field_html_value' ], 10, 4 ); add_filter( "wpforms_{$this->type}_field_html_value_images", [ $this, 'field_html_value_images' ], 10, 3 ); // Define additional field properties. add_filter( "wpforms_field_properties_{$this->type}", [ $this, 'field_properties' ], 5, 3 ); // This field requires fieldset+legend instead of the field label. add_filter( "wpforms_frontend_modern_is_field_requires_fieldset_{$this->type}", '__return_true', PHP_INT_MAX, 2 ); } /** * Define additional field properties. * * @since 1.8.2 * * @param array $properties Field properties. * @param array $field Field settings. * @param array $form_data Form data and settings. * * @return array */ public function field_properties( $properties, $field, $form_data ) { // phpcs:ignore Generic.Metrics.CyclomaticComplexity.TooHigh // Define data. $form_id = absint( $form_data['id'] ); $field_id = absint( $field['id'] ); $choices = $field['choices']; // Remove primary input, unset for attribute for label. unset( $properties['inputs']['primary'], $properties['label']['attr']['for'] ); // Set input container (ul) properties. $properties['input_container'] = [ 'class' => [], 'data' => [], 'attr' => [], 'id' => "wpforms-{$form_id}-field_{$field_id}", ]; $is_choice_limit_set = ! empty( $field['choice_limit'] ) && (int) $field['choice_limit'] > 0; if ( $is_choice_limit_set ) { $properties['input_container']['data']['choice-limit'] = $field['choice_limit']; } // Set input properties. foreach ( $choices as $key => $choice ) { // Choice labels should not be left blank, but if they are, we provide a basic value. $label = $choice['label'] ?? ''; if ( $label === '' ) { if ( 1 === count( $choices ) ) { $label = esc_html__( 'Checked', 'wpforms-lite' ); } else { /* translators: %s - item number. */ $label = sprintf( esc_html__( 'Item %s', 'wpforms-lite' ), $key ); } } $properties['inputs'][ $key ] = [ 'container' => [ 'attr' => [], 'class' => [ "choice-{$key}" ], 'data' => [], 'id' => '', ], 'label' => [ 'attr' => [ 'for' => "wpforms-{$form_id}-field_{$field_id}_{$key}", ], 'class' => [ 'wpforms-field-label-inline' ], 'data' => [], 'id' => '', 'text' => $label, ], 'attr' => [ 'name' => "wpforms[fields][{$field_id}][]", 'value' => $key, ], 'class' => [ 'wpforms-payment-price' ], 'data' => [ 'amount' => wpforms_format_amount( wpforms_sanitize_amount( $choice['value'] ?? '' ) ), ], 'id' => "wpforms-{$form_id}-field_{$field_id}_{$key}", 'icon' => $choice['icon'] ?? '', 'icon_style' => $choice['icon_style'] ?? '', 'image' => $choice['image'] ?? '', 'required' => ! empty( $field['required'] ) ? 'required' : '', 'default' => isset( $choice['default'] ), ]; // Rule for validator only if needed. if ( $is_choice_limit_set ) { $properties['inputs'][ $key ]['data']['rule-check-limit'] = 'true'; } } // Required class for pagebreak validation. if ( ! empty( $field['required'] ) ) { $properties['input_container']['class'][] = 'wpforms-field-required'; } // Custom properties if image choices are enabled. if ( ! empty( $field['choices_images'] ) ) { $properties['input_container']['class'][] = 'wpforms-image-choices'; $properties['input_container']['class'][] = 'wpforms-image-choices-' . sanitize_html_class( $field['choices_images_style'] ); foreach ( $properties['inputs'] as $key => $inputs ) { $properties['inputs'][ $key ]['container']['class'][] = 'wpforms-image-choices-item'; if ( in_array( $field['choices_images_style'], [ 'modern', 'classic' ], true ) ) { $properties['inputs'][ $key ]['class'][] = 'wpforms-screen-reader-element'; } } } elseif ( ! empty( $field['choices_icons'] ) ) { $properties = wpforms()->obj( 'icon_choices' )->field_properties( $properties, $field ); } // Add selected class for choices with defaults. foreach ( $properties['inputs'] as $key => $inputs ) { if ( ! empty( $inputs['default'] ) ) { $properties['inputs'][ $key ]['container']['class'][] = 'wpforms-selected'; } } return $properties; } /** * Get field populated single property value. * * @since 1.8.2 * * @param string $raw_value Value from a GET param, always a string. * @param string $input Represent a subfield inside the field. May be empty. * @param array $properties Field properties. * @param array $field Current field specific data. * * @return array Modified field properties. */ protected function get_field_populated_single_property_value( $raw_value, $input, $properties, $field ) { /* * When the form is submitted, we get only choice values from the Fallback. * As payment-checkbox (checkboxes) field doesn't support 'show_values' option - * we should transform that into label to check against using general logic in parent method. */ if ( ! is_string( $raw_value ) || empty( $field['choices'] ) || ! is_array( $field['choices'] ) ) { return $properties; } // The form submits only the sum, so shortcut for Dynamic. if ( ! is_numeric( $raw_value ) ) { return parent::get_field_populated_single_property_value( $raw_value, $input, $properties, $field ); } $get_value = wpforms_format_amount( wpforms_sanitize_amount( $raw_value ) ); foreach ( $field['choices'] as $choice ) { if ( isset( $choice['label'], $choice['value'] ) && wpforms_format_amount( wpforms_sanitize_amount( $choice['value'] ) ) === $get_value ) { $trans_value = $choice['label']; // Stop iterating over choices. break; } } if ( empty( $trans_value ) ) { return $properties; } return parent::get_field_populated_single_property_value( $trans_value, $input, $properties, $field ); } /** * Field options panel inside the builder. * * @since 1.8.2 * * @param array $field Field settings. */ public function field_options( $field ) { /* * Basic field options. */ // Options open markup. $this->field_option( 'basic-options', $field, [ 'markup' => 'open', ] ); // Label. $this->field_option( 'label', $field ); // Choices option. $this->field_option( 'choices_payments', $field ); // Show price after item labels. $fld = $this->field_element( 'toggle', $field, [ 'slug' => 'show_price_after_labels', 'value' => isset( $field['show_price_after_labels'] ) ? '1' : '0', 'desc' => esc_html__( 'Show Price After Item Labels', 'wpforms-lite' ), 'tooltip' => esc_html__( 'Check this option to show price of the item after the label.', 'wpforms-lite' ), ], false ); $args = [ 'slug' => 'show_price_after_labels', 'content' => $fld, ]; $this->field_element( 'row', $field, $args ); // Choices Images. $this->field_option( 'choices_images', $field ); // Hide Choices Images. $this->field_option( 'choices_images_hide', $field ); // Choice Images Style (theme). $this->field_option( 'choices_images_style', $field ); // Choices Icons. $this->field_option( 'choices_icons', $field ); // Choices Icons Color. $this->field_option( 'choices_icons_color', $field ); // Choices Icons Size. $this->field_option( 'choices_icons_size', $field ); // Choices Icons Style. $this->field_option( 'choices_icons_style', $field ); // Description. $this->field_option( 'description', $field ); // Required toggle. $this->field_option( 'required', $field ); // Options close markup. $this->field_option( 'basic-options', $field, [ 'markup' => 'close', ] ); /* * Advanced field options. */ // Options open markup. $this->field_option( 'advanced-options', $field, [ 'markup' => 'open', ] ); // Input columns. $this->field_option( 'input_columns', $field ); // Choice Limit. $this->field_option( 'choice_limit', $field ); // Custom CSS classes. $this->field_option( 'css', $field ); // Hide label. $this->field_option( 'label_hide', $field ); // Options close markup. $this->field_option( 'advanced-options', $field, [ 'markup' => 'close', ] ); } /** * Field preview inside the builder. * * @since 1.8.2 * * @param array $field Field settings. */ public function field_preview( $field ) { // Label. $this->field_preview_option( 'label', $field ); // Choices. $this->field_preview_option( 'choices', $field ); // Description. $this->field_preview_option( 'description', $field ); } /** * Field display on the form front-end. * * @since 1.8.2 * * @param array $field Field settings. * @param array $deprecated Deprecated array. * @param array $form_data Form data and settings. * * @noinspection HtmlUnknownAttribute * @noinspection HtmlUnknownTarget */ public function field_display( $field, $deprecated, $form_data ) { // Define data. $container = $field['properties']['input_container']; $choices = $field['properties']['inputs']; printf( '<ul %s>', wpforms_html_attributes( $container['id'], $container['class'], $container['data'], $container['attr'] ) // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ); foreach ( $choices as $key => $choice ) { $label = $choice['label']['text'] ?? ''; /* translators: %s - item number. */ $label = $label !== '' ? $label : sprintf( esc_html__( 'Item %s', 'wpforms-lite' ), $key ); $label .= ! empty( $field['show_price_after_labels'] ) && isset( $choice['data']['amount'] ) ? $this->get_price_after_label( $choice['data']['amount'] ) : ''; printf( '<li %s>', wpforms_html_attributes( $choice['container']['id'], $choice['container']['class'], $choice['container']['data'], $choice['container']['attr'] ) // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ); if ( empty( $field['dynamic_choices'] ) && ! empty( $field['choices_images'] ) ) { // Image choices. printf( '<label %s>', wpforms_html_attributes( $choice['label']['id'], $choice['label']['class'], $choice['label']['data'], $choice['label']['attr'] ) // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ); echo '<span class="wpforms-image-choices-image">'; if ( ! empty( $choice['image'] ) ) { printf( '<img src="%s" alt="%s"%s>', esc_url( $choice['image'] ), esc_attr( $choice['label']['text'] ), ! empty( $choice['label']['text'] ) ? ' title="' . esc_attr( $choice['label']['text'] ) . '"' : '' ); } echo '</span>'; if ( $field['choices_images_style'] === 'none' ) { echo '<br>'; } printf( '<input type="checkbox" %s %s %s>', wpforms_html_attributes( $choice['id'], $choice['class'], $choice['data'], $choice['attr'] ), // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped esc_attr( $choice['required'] ), checked( '1', $choice['default'], false ) ); echo '<span class="wpforms-image-choices-label">' . wp_kses_post( $label ) . '</span>'; echo '</label>'; } elseif ( empty( $field['dynamic_choices'] ) && ! empty( $field['choices_icons'] ) ) { // Icon Choices. wpforms()->obj( 'icon_choices' )->field_display( $field, $choice, 'checkbox', $label ); } else { // Normal display. printf( '<input type="checkbox" %s %s %s>', wpforms_html_attributes( $choice['id'], $choice['class'], $choice['data'], $choice['attr'] ), // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped esc_attr( $choice['required'] ), checked( '1', $choice['default'], false ) ); printf( '<label %s>%s</label>', wpforms_html_attributes( $choice['label']['id'], $choice['label']['class'], $choice['label']['data'], $choice['label']['attr'] ), // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped wp_kses_post( $label ) ); } echo '</li>'; } echo '</ul>'; } /** * Validate field on submitting the form. * * @since 1.8.2 * * @param int $field_id Field ID. * @param array $field_submit Submitted field value (raw data). * @param array $form_data Form data and settings. */ public function validate( $field_id, $field_submit, $form_data ) { $field_id = (int) $field_id; $error = ''; // Basic required check - If field is marked as required, check for entry data. if ( ! empty( $form_data['fields'][ $field_id ]['required'] ) && empty( $field_submit ) ) { $error = wpforms_get_required_label(); } if ( ! empty( $field_submit ) ) { foreach ( (array) $field_submit as $checked_choice ) { // Validate that the option selected is real. if ( empty( $form_data['fields'][ $field_id ]['choices'][ (int) $checked_choice ] ) ) { $error = esc_html__( 'Invalid payment option.', 'wpforms-lite' ); break; } } } $field_submit = (array) $field_submit; $this->validate_field_choice_limit( $field_id, $field_submit, $form_data ); if ( ! empty( $error ) ) { wpforms()->obj( 'process' )->errors[ $form_data['id'] ][ $field_id ] = $error; } } /** * Format and sanitize field. * * @since 1.8.2 * * @param int $field_id Field ID. * @param array $field_submit Array of selected choice IDs. * @param array $form_data Form data and settings. */ public function format( $field_id, $field_submit, $form_data ) { // phpcs:ignore Generic.Metrics.CyclomaticComplexity.TooHigh, Generic.Metrics.NestingLevel.MaxExceeded $field_submit = array_values( (array) $field_submit ); $field = $form_data['fields'][ $field_id ]; $name = sanitize_text_field( $field['label'] ); $amount = 0; $images = []; $choice_values = []; $choice_labels = []; $choice_keys = []; if ( ! empty( $field_submit ) ) { foreach ( $field_submit as $choice_checked ) { foreach ( $field['choices'] as $choice_id => $choice ) { // Exit early. if ( (int) $choice_checked !== (int) $choice_id ) { continue; } $value = (float) wpforms_sanitize_amount( $choice['value'] ?? '' ); // Increase the total amount. $amount += $value; $value = wpforms_format_amount( $value, true ); $choice_label = ''; if ( ! empty( $choice['label'] ) ) { $choice_label = sanitize_text_field( $choice['label'] ); $value = $choice_label . ' - ' . $value; } $choice_labels[] = $choice_label; $choice_values[] = $value; $choice_keys[] = $choice_id; } } if ( ! empty( $choice_keys ) && ! empty( $field['choices_images'] ) ) { foreach ( $choice_keys as $choice_key ) { $images[] = ! empty( $field['choices'][ $choice_key ]['image'] ) ? esc_url_raw( $field['choices'][ $choice_key ]['image'] ) : ''; } } } wpforms()->obj( 'process' )->fields[ $field_id ] = [ 'name' => $name, 'value' => implode( "\r\n", $choice_values ), 'value_choice' => implode( "\r\n", $choice_labels ), 'value_raw' => implode( ',', array_map( 'absint', $field_submit ) ), 'amount' => wpforms_format_amount( $amount ), 'amount_raw' => $amount, 'currency' => wpforms_get_currency(), 'images' => $images, 'id' => absint( $field_id ), 'type' => sanitize_key( $this->type ), ]; } } Fields/Registry.php 0000644 00000002351 15252506741 0010303 0 ustar 00 <?php namespace WPForms\Forms\Fields; use WPForms_Field; /** * Registry of instantiated field objects, keyed by field type slug. * * Every field announces itself via the `wpforms_field_registered` action once * initialized, giving a single authoritative collection of all available field * types (Lite, Pro, and addons) without hardcoded lists or per-type lookups. * * @since 2.0.0 */ class Registry { /** * Registered field objects, keyed by field type slug. * * @since 2.0.0 * * @var array */ private $fields = []; /** * Register hooks. * * @since 2.0.0 */ public function hooks(): void { add_action( 'wpforms_field_registered', [ $this, 'add' ] ); } /** * Register a field object. * * @since 2.0.0 * * @param WPForms_Field $field Field object to register. */ public function add( WPForms_Field $field ): void { if ( empty( $field->type ) ) { return; } $this->fields[ $field->type ] = $field; } /** * Get a map of field type slug to human-readable field name. * * @since 2.0.0 * * @return array */ public function get_names(): array { $names = []; foreach ( $this->fields as $type => $field ) { $names[ $type ] = (string) $field->name; } return $names; } } Fields/DateTime/Field.php 0000644 00000061260 15252506741 0011216 0 ustar 00 <?php namespace WPForms\Forms\Fields\DateTime; use WPForms\Forms\Fields\Traits\ProField as ProFieldTrait; use WPForms_Field; /** * Date / Time field. * * @since 1.9.4 */ class Field extends WPForms_Field { use ProFieldTrait; /** * Field settings defaults. * * @since 1.9.4 */ public const DEFAULTS = [ 'format' => 'date-time', 'date_placeholder' => '', 'date_format' => 'm/d/Y', 'date_type' => 'datepicker', 'time_placeholder' => '', 'time_format' => 'g:i A', 'time_interval' => '30', 'date_limit_days_sun' => '0', 'date_limit_days_mon' => '1', 'date_limit_days_tue' => '1', 'date_limit_days_wed' => '1', 'date_limit_days_thu' => '1', 'date_limit_days_fri' => '1', 'date_limit_days_sat' => '0', 'time_limit_hours_start_hour' => '09', 'time_limit_hours_start_min' => '00', 'time_limit_hours_start_ampm' => 'am', 'time_limit_hours_end_hour' => '06', 'time_limit_hours_end_min' => '00', 'time_limit_hours_end_ampm' => 'pm', ]; /** * Alternative Date Format. * * @since 1.9.4 */ public const ALT_DATE_FORMAT = 'd/m/Y'; /** * Primary class constructor. * * @since 1.9.4 */ public function init() { // Define field type information. $this->name = esc_html__( 'Date / Time', 'wpforms-lite' ); $this->type = 'date-time'; $this->icon = 'fa-calendar-o'; $this->order = 60; $this->group = 'fancy'; $this->default_settings = self::DEFAULTS; $this->init_pro_field(); $this->hooks(); } /** * Hooks. * * @since 1.9.4 */ protected function hooks(): void { // Set custom option wrapper classes. add_filter( 'wpforms_builder_field_option_class', [ $this, 'field_option_class' ], 10, 2 ); } /** * Field options panel inside the builder. * * @since 1.9.4 * * @param array $field Field data and settings. * * @noinspection PackedHashtableOptimizationInspection * @noinspection HtmlUnknownAttribute */ public function field_options( $field ) { // phpcs:ignore Generic.Metrics.CyclomaticComplexity.TooHigh /** * Basic field options */ // Options open markup. $this->field_option( 'basic-options', $field, [ 'markup' => 'open', 'after_title' => $this->get_field_options_notice(), ] ); // Label. $this->field_option( 'label', $field ); // Format option. $format = ! empty( $field['format'] ) ? esc_attr( $field['format'] ) : self::DEFAULTS['format']; $format_label = $this->field_element( 'label', $field, [ 'slug' => 'format', 'value' => esc_html__( 'Format', 'wpforms-lite' ), 'tooltip' => esc_html__( 'Select format for the date field.', 'wpforms-lite' ), ], false ); $format_select = $this->field_element( 'select', $field, [ 'slug' => 'format', 'value' => $format, 'options' => [ 'date-time' => esc_html__( 'Date and Time', 'wpforms-lite' ), 'date' => esc_html__( 'Date', 'wpforms-lite' ), 'time' => esc_html__( 'Time', 'wpforms-lite' ), ], ], false ); $this->field_element( 'row', $field, [ 'slug' => 'format', 'content' => $format_label . $format_select, ] ); // Description. $this->field_option( 'description', $field ); // Required toggle. $this->field_option( 'required', $field ); // Options close markup. $this->field_option( 'basic-options', $field, [ 'markup' => 'close', ] ); /* * Advanced field options */ // Options open markup. $this->field_option( 'advanced-options', $field, [ 'markup' => 'open', ] ); // Size. $this->field_option( 'size', $field ); // Custom options. // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped echo '<div class="format-selected-' . $format . ' format-selected">'; // Date. $date_placeholder = ! empty( $field['date_placeholder'] ) ? $field['date_placeholder'] : ''; $date_format = ! empty( $field['date_format'] ) ? esc_attr( $field['date_format'] ) : self::DEFAULTS['date_format']; $date_type = ! empty( $field['date_type'] ) ? esc_attr( $field['date_type'] ) : 'datepicker'; // Backwards compatibility with old datepicker format. if ( $date_format === 'mm/dd/yyyy' ) { $date_format = self::DEFAULTS['date_format']; } elseif ( $date_format === 'dd/mm/yyyy' ) { $date_format = self::ALT_DATE_FORMAT; } elseif ( $date_format === 'mmmm d, yyyy' ) { $date_format = 'F j, Y'; } $date_formats = wpforms_date_formats(); printf( '<div class="wpforms-clear wpforms-field-option-row wpforms-field-option-row-date no-gap" id="wpforms-field-option-row-%d-date" data-subfield="date" data-field-id="%d">', esc_attr( $field['id'] ), esc_attr( $field['id'] ) ); $this->field_element( 'label', $field, [ 'slug' => 'date_placeholder', 'value' => esc_html__( 'Date', 'wpforms-lite' ), 'tooltip' => esc_html__( 'Advanced date options.', 'wpforms-lite' ), ] ); echo '<div class="wpforms-field-options-columns-2 wpforms-field-options-columns">'; echo '<div class="type wpforms-field-options-column">'; printf( '<select id="wpforms-field-option-%d-date_type" name="fields[%d][date_type]">', esc_attr( $field['id'] ), esc_attr( $field['id'] ) ); printf( '<option value="datepicker" %s>%s</option>', selected( $date_type, 'datepicker', false ), esc_html__( 'Date Picker', 'wpforms-lite' ) ); printf( '<option value="dropdown" %s>%s</option>', selected( $date_type, 'dropdown', false ), esc_html__( 'Date Dropdown', 'wpforms-lite' ) ); echo '</select>'; printf( '<label for="wpforms-field-option-%d-date_type" class="sub-label">%s</label>', esc_attr( $field['id'] ), esc_html__( 'Type', 'wpforms-lite' ) ); echo '</div>'; echo '<div class="format wpforms-field-options-column">'; printf( '<select id="wpforms-field-option-%d-date_format" name="fields[%d][date_format]">', esc_attr( $field['id'] ), esc_attr( $field['id'] ) ); foreach ( $date_formats as $key => $value ) { if ( in_array( $key, $this->get_regular_date_formats(), true ) ) { printf( '<option value="%s" %s>%s (%s)</option>', esc_attr( $key ), selected( $date_format, $key, false ), esc_html( date( $value ) ), // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date esc_html( $key ) ); } else { printf( '<option value="%s" class="datepicker-only" %s>%s</option>', esc_attr( $key ), selected( $date_format, $key, false ), esc_html( date( $value ) ) // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date ); } } echo '</select>'; printf( '<label for="wpforms-field-option-%d-date_format" class="sub-label">%s</label>', esc_attr( $field['id'] ), esc_html__( 'Format', 'wpforms-lite' ) ); echo '</div>'; echo '</div>'; echo '<div class="placeholder wpforms-field-option-row">'; printf( '<input type="text" class="placeholder" id="wpforms-field-option-%d-date_placeholder" name="fields[%d][date_placeholder]" value="%s">', esc_attr( $field['id'] ), esc_attr( $field['id'] ), esc_attr( $date_placeholder ) ); printf( '<label for="wpforms-field-option-%d-date_placeholder" class="sub-label">%s</label>', esc_attr( $field['id'] ), esc_html__( 'Placeholder', 'wpforms-lite' ) ); echo '</div>'; // Limit Days options. $this->field_options_limit_days( $field ); echo '</div>'; // Time. $time_placeholder = ! empty( $field['time_placeholder'] ) ? $field['time_placeholder'] : ''; $time_format = ! empty( $field['time_format'] ) ? esc_attr( $field['time_format'] ) : self::DEFAULTS['time_format']; $time_formats = wpforms_time_formats(); $time_interval = ! empty( $field['time_interval'] ) ? esc_attr( $field['time_interval'] ) : '30'; /** * Filters the time intervals available for the Time field. * * @since 1.6.0 * * @param array $time_intervals Array of time intervals. */ $time_intervals = apply_filters( // phpcs:ignore WPForms.PHP.ValidateHooks.InvalidHookName 'wpforms_datetime_time_intervals', [ '15' => esc_html__( '15 minutes', 'wpforms-lite' ), '30' => esc_html__( '30 minutes', 'wpforms-lite' ), '60' => esc_html__( '1 hour', 'wpforms-lite' ), ] ); printf( '<div class="wpforms-clear wpforms-field-option-row wpforms-field-option-row-time no-gap" id="wpforms-field-option-row-%d-time" data-subfield="time" data-field-id="%d">', esc_attr( $field['id'] ), esc_attr( $field['id'] ) ); $this->field_element( 'label', $field, [ 'slug' => 'time_placeholder', 'value' => esc_html__( 'Time', 'wpforms-lite' ), 'tooltip' => esc_html__( 'Advanced time options.', 'wpforms-lite' ), ] ); echo '<div class="wpforms-field-options-columns-2 wpforms-field-options-columns">'; echo '<div class="interval wpforms-field-options-column">'; printf( '<select id="wpforms-field-option-%d-time_interval" name="fields[%d][time_interval]">', esc_attr( $field['id'] ), esc_attr( $field['id'] ) ); foreach ( $time_intervals as $key => $value ) { printf( '<option value="%s" %s>%s</option>', esc_attr( $key ), selected( $time_interval, $key, false ), $value // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ); } echo '</select>'; printf( '<label for="wpforms-field-option-%d-time_interval" class="sub-label">%s</label>', esc_attr( $field['id'] ), esc_html__( 'Interval', 'wpforms-lite' ) ); echo '</div>'; echo '<div class="format wpforms-field-options-column">'; printf( '<select id="wpforms-field-option-%d-time_format" name="fields[%d][time_format]">', esc_attr( $field['id'] ), esc_attr( $field['id'] ) ); foreach ( $time_formats as $key => $value ) { printf( '<option value="%s" %s>%s</option>', esc_attr( $key ), selected( $time_format, $key, false ), esc_html( $value ) ); } echo '</select>'; printf( '<label for="wpforms-field-option-%d-time_format" class="sub-label">%s</label>', esc_attr( $field['id'] ), esc_html__( 'Format', 'wpforms-lite' ) ); echo '</div>'; echo '</div>'; echo '<div class="placeholder wpforms-field-option-row">'; printf( '<input type="text" class="placeholder" id="wpforms-field-option-%d-time_placeholder" name="fields[%d][time_placeholder]" value="%s">', esc_attr( $field['id'] ), esc_attr( $field['id'] ), esc_attr( $time_placeholder ) ); printf( '<label for="wpforms-field-option-%d-time_placeholder" class="sub-label">%s</label>', esc_attr( $field['id'] ), esc_html__( 'Placeholder', 'wpforms-lite' ) ); echo '</div>'; // Limit Hours options. $this->field_options_limit_hours( $field ); echo '</div>'; echo '</div>'; // Custom CSS classes. $this->field_option( 'css', $field ); // Hide label. $this->field_option( 'label_hide', $field ); // Hide sublabels. $sublabel_class = isset( $field['format'] ) && $field['format'] !== self::DEFAULTS['format'] ? 'wpforms-hidden' : ''; $this->field_option( 'sublabel_hide', $field, [ 'class' => $sublabel_class ] ); // Options close markup. $this->field_option( 'advanced-options', $field, [ 'markup' => 'close', ] ); } /** * Get regular date formats. * * @since 1.9.8.3 * * @return array */ private function get_regular_date_formats(): array { return [ self::DEFAULTS['date_format'], self::ALT_DATE_FORMAT, 'Y/m/d', 'm.d.Y', 'd.m.Y', 'Y.m.d', ]; } /** * Display limit days options. * * @since 1.9.4 * * @param array $field Field setting. */ private function field_options_limit_days( array $field ): void { echo '<div class="wpforms-clear"></div>'; $output = $this->field_element( 'toggle', $field, [ 'slug' => 'date_limit_days', 'value' => ! empty( $field['date_limit_days'] ) ? '1' : '0', 'desc' => esc_html__( 'Limit Days', 'wpforms-lite' ), 'tooltip' => esc_html__( 'Check this option to adjust which days of the week can be selected.', 'wpforms-lite' ), 'class' => 'wpforms-panel-field-toggle', ], false ); $this->field_element( 'row', $field, [ 'slug' => 'date_limit_days', 'content' => $output, 'class' => 'wpforms-clear', ] ); $week_days = [ 'sun' => esc_html__( 'Sun', 'wpforms-lite' ), 'mon' => esc_html__( 'Mon', 'wpforms-lite' ), 'tue' => esc_html__( 'Tue', 'wpforms-lite' ), 'wed' => esc_html__( 'Wed', 'wpforms-lite' ), 'thu' => esc_html__( 'Thu', 'wpforms-lite' ), 'fri' => esc_html__( 'Fri', 'wpforms-lite' ), 'sat' => esc_html__( 'Sat', 'wpforms-lite' ), ]; // Rearrange days array according to the Start of Week setting. $start_of_week = get_option( 'start_of_week' ); $start_of_week = ! empty( $start_of_week ) ? (int) $start_of_week : 0; if ( $start_of_week > 0 ) { $days_after = $week_days; $days_begin = array_splice( $days_after, 0, $start_of_week ); $days = array_merge( $days_after, $days_begin ); } else { $days = $week_days; } // Limit Days body. $field = $this->field_options_limit_days_body( $days, $field ); // Disable Past Dates. $this->field_options_limit_days_disable_past_dates( $field ); // Disable Today's Date. $output = $this->field_element( 'toggle', $field, [ 'slug' => 'date_disable_todays_date', 'value' => ! empty( $field['date_disable_todays_date'] ) ? '1' : '0', 'desc' => esc_html__( 'Disable Today\'s Date', 'wpforms-lite' ), 'tooltip' => esc_html__( 'Check this option to prevent today\'s date from being selected.', 'wpforms-lite' ), ], false ); $this->field_element( 'row', $field, [ 'slug' => 'date_disable_todays_date', 'content' => $output, 'class' => ! isset( $field['date_disable_past_dates'] ) ? 'wpforms-hide' : '', ] ); } /** * Display limit hours options. * * @since 1.9.4 * * @param array $field Field setting. */ private function field_options_limit_hours( array $field ): void { echo '<div class="wpforms-clear"></div>'; $output = $this->field_element( 'toggle', $field, [ 'slug' => 'time_limit_hours', 'value' => ! empty( $field['time_limit_hours'] ) ? '1' : '0', 'desc' => esc_html__( 'Limit Hours', 'wpforms-lite' ), 'tooltip' => esc_html__( 'Check this option to adjust the range of times that can be selected.', 'wpforms-lite' ), 'class' => 'wpforms-panel-field-toggle', ], false ); $this->field_element( 'row', $field, [ 'slug' => 'time_limit_hours', 'content' => $output, ] ); // Determine a time format type. // If the format contains `g` or `h`, then this is 12-hour format, otherwise 24 hours. $time_format = empty( $field['time_format'] ) || preg_match( '/[gh]/', $field['time_format'] ) ? 12 : 24; // Limit Hours body. $output = $this->field_options_limit_hours_body( $field, $time_format ); printf( '<div class="wpforms-field-option-row wpforms-field-option-row-%1$s %2$s" id="wpforms-field-option-row-%3$d-%1$s" data-toggle="%4$s" data-toggle-value="1" data-field-id="%3$d">%5$s</div>', 'time_limit_hours_options', 'wpforms-panel-field-toggle-body', esc_attr( $field['id'] ), esc_attr( 'fields[' . (int) $field['id'] . '][time_limit_hours]' ), $output // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ); } /** * Generate an array of numeric options for date/time selectors. * * @since 1.9.4 * * @param integer $min Minimum value. * @param integer $max Maximum value. * @param integer $step Step. * * @return array */ private function get_selector_numeric_options( int $min, int $max, int $step = 1 ): array { $range = range( $min, $max, $step ); $options = []; foreach ( $range as $i ) { $value = str_pad( $i, 2, '0', STR_PAD_LEFT ); $options[ $value ] = $value; } return $options; } /** * Add class to field options wrapper to indicate if field confirmation is enabled. * * @since 1.9.4 * * @param string|mixed $css_class CSS class. * @param array $field Field data. * * @return string */ public function field_option_class( $css_class, array $field ): string { $css_class = (string) $css_class; if ( $this->type === $field['type'] ) { $date_type = ! empty( $field['date_type'] ) ? sanitize_html_class( $field['date_type'] ) : 'datepicker'; $css_class .= " wpforms-date-type-$date_type"; } return $css_class; } /** * Field preview inside the builder. * * @since 1.9.4 * * @param array $field Field data and settings. */ public function field_preview( $field ) { $date_placeholder = ! empty( $field['date_placeholder'] ) ? $field['date_placeholder'] : ''; $time_placeholder = ! empty( $field['time_placeholder'] ) ? $field['time_placeholder'] : ''; $format = ! empty( $field['format'] ) ? $field['format'] : self::DEFAULTS['format']; $date_type = ! empty( $field['date_type'] ) ? $field['date_type'] : 'datepicker'; $date_format = ! empty( $field['date_format'] ) ? $field['date_format'] : self::DEFAULTS['date_format']; if ( in_array( $date_format, $this->get_month_day_formats(), true ) ) { $date_first_select = 'MM'; $date_second_select = 'DD'; $date_third_select = 'YYYY'; } elseif ( in_array( $date_format, $this->get_day_month_formats(), true ) ) { $date_first_select = 'DD'; $date_second_select = 'MM'; $date_third_select = 'YYYY'; } else { $date_first_select = 'YYYY'; $date_second_select = 'MM'; $date_third_select = 'DD'; } // Label. $this->field_preview_option( 'label', $field, [ 'label_badge' => $this->get_field_preview_badge(), ] ); printf( '<div class="%s format-selected">', sanitize_html_class( 'format-selected-' . $format ) ); // Date. printf( '<div class="wpforms-date %s">', sanitize_html_class( 'wpforms-date-type-' . $date_type ) ); echo '<div class="wpforms-date-datepicker">'; printf( '<input type="text" placeholder="%s" class="primary-input" readonly>', esc_attr( $date_placeholder ) ); printf( '<label class="wpforms-sub-label">%s</label>', esc_html__( 'Date', 'wpforms-lite' ) ); echo '</div>'; echo '<div class="wpforms-date-dropdown">'; printf( '<select readonly class="first"><option>%s</option></select>', esc_html( $date_first_select ) ); printf( '<select readonly class="second"><option>%s</option></select>', esc_html( $date_second_select ) ); printf( '<select readonly class="third"><option>%s</option></select>', esc_html( $date_third_select ) ); printf( '<label class="wpforms-sub-label">%s</label>', esc_html__( 'Date', 'wpforms-lite' ) ); echo '</div>'; echo '</div>'; // Time. echo '<div class="wpforms-time">'; printf( '<input type="text" placeholder="%s" class="primary-input" readonly>', esc_attr( $time_placeholder ) ); printf( '<label class="wpforms-sub-label">%s</label>', esc_html__( 'Time', 'wpforms-lite' ) ); echo '</div>'; echo '</div>'; // Description. $this->field_preview_option( 'description', $field ); } /** * Get month-day date formats. * * @since 1.9.8.3 * * @return array */ private function get_month_day_formats(): array { return [ 'mm/dd/yyyy', self::DEFAULTS['date_format'], 'm.d.Y' ]; } /** * Get day-month date formats. * * @since 1.9.8.3 * * @return array */ private function get_day_month_formats(): array { return [ 'dd/mm/yyyy', self::ALT_DATE_FORMAT, 'd.m.Y' ]; } /** * Field display on the form front-end. * * @since 1.9.4 * * @param array $field Field data and settings. * @param array $deprecated Deprecated array of field attributes. * @param array $form_data Form data and settings. */ public function field_display( $field, $deprecated, $form_data ) { } /** * Field options: Limit Days body section. * * @since 1.9.4 * * @param array $days Array of days. * @param array $field Field data and settings. * * @return array Modified field data array. */ public function field_options_limit_days_body( array $days, array $field ): array { // Limit Days body. $output = ''; foreach ( $days as $day => $day_translation ) { $day_slug = 'date_limit_days_' . $day; // Set defaults. if ( ! isset( $field['date_format'] ) ) { $field[ $day_slug ] = $this->default_settings[ $day_slug ]; } $output .= '<label class="sub-label">'; $output .= $this->field_element( 'checkbox', $field, [ 'slug' => $day_slug, 'value' => ! empty( $field[ $day_slug ] ) ? '1' : '0', 'nodesc' => '1', 'class' => 'wpforms-field-options-column', ], false ); $output .= '<br>' . $day_translation . '</label>'; } printf( '<div class="wpforms-field-option-row wpforms-field-option-row-date_limit_days_options wpforms-panel-field-toggle-body wpforms-field-options-columns wpforms-field-options-columns-7 checkboxes-row" id="wpforms-field-option-row-%1$d-date_limit_days_options" data-toggle="%2$s" data-toggle-value="1" data-field-id="%1$d">%3$s</div>', esc_attr( $field['id'] ), esc_attr( 'fields[' . (int) $field['id'] . '][date_limit_days]' ), $output // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ); return $field; } /** * Field options: Limit Days - Disable Past Dates section. * * @since 1.9.4 * * @param array $field Field data. */ public function field_options_limit_days_disable_past_dates( array $field ): void { $output = $this->field_element( 'toggle', $field, [ 'slug' => 'date_disable_past_dates', 'value' => ! empty( $field['date_disable_past_dates'] ) ? '1' : '0', 'desc' => esc_html__( 'Disable Past Dates', 'wpforms-lite' ), 'tooltip' => esc_html__( 'Check this option to prevent any previous date from being selected.', 'wpforms-lite' ), ], false ); $this->field_element( 'row', $field, [ 'slug' => 'date_disable_past_dates', 'content' => $output, ] ); } /** * Field options: Limit Hours - body section. * * @since 1.9.4 * * @param array $field Field data. * @param int $time_format Time format. * * @return string */ private function field_options_limit_hours_body( array $field, int $time_format ): string { $output = ''; foreach ( [ 'start', 'end' ] as $option ) { $output .= '<div class="wpforms-field-options-columns wpforms-field-options-columns-4">'; // Open columns container. $slug = 'time_limit_hours_' . $option . '_hour'; $output .= $this->field_element( 'select', $field, [ 'slug' => $slug, 'value' => ! empty( $field[ $slug ] ) ? $field[ $slug ] : $this->default_settings[ $slug ], 'options' => $time_format === 12 ? $this->get_selector_numeric_options( 1, $time_format ) : $this->get_selector_numeric_options( 0, $time_format - 1 ), 'class' => 'wpforms-field-options-column', ], false ); $slug = 'time_limit_hours_' . $option . '_min'; $output .= $this->field_element( 'select', $field, [ 'slug' => $slug, 'value' => ! empty( $field[ $slug ] ) ? $field[ $slug ] : $this->default_settings[ $slug ], 'options' => $this->get_selector_numeric_options( 0, 59, 5 ), 'class' => 'wpforms-field-options-column', ], false ); $slug = 'time_limit_hours_' . $option . '_ampm'; $output .= $this->field_element( 'select', $field, [ 'slug' => $slug, 'value' => ! empty( $field[ $slug ] ) ? $field[ $slug ] : $this->default_settings[ $slug ], 'options' => [ 'am' => 'AM', 'pm' => 'PM', ], 'class' => [ 'wpforms-field-options-column', $time_format === 24 ? 'wpforms-hidden-strict' : '', ], ], false ); $slug = 'time_limit_hours_' . $option . '_hour'; $output .= $this->field_element( 'label', $field, [ 'slug' => $slug, 'value' => $option === 'start' ? esc_html__( 'Start Time', 'wpforms-lite' ) : esc_html__( 'End Time', 'wpforms-lite' ), 'class' => [ 'sub-label', 'wpforms-field-options-column', ], ], false ); $output .= sprintf( '<div class="%s wpforms-field-options-column"></div>', $time_format === 12 ? 'wpforms-hidden-strict' : '' ); $output .= '</div>'; // Close columns container. } return $output; } } Fields/Url/Field.php 0000644 00000006050 15252506741 0010260 0 ustar 00 <?php namespace WPForms\Forms\Fields\Url; use WPForms\Forms\Fields\Traits\ProField as ProFieldTrait; use WPForms_Field; /** * URL text field. * * @since 1.9.4 */ class Field extends WPForms_Field { use ProFieldTrait; /** * Primary class constructor. * * @since 1.9.4 */ public function init() { // Define field type information. $this->name = esc_html__( 'Website / URL', 'wpforms-lite' ); $this->keywords = esc_html__( 'uri, link, hyperlink', 'wpforms-lite' ); $this->type = 'url'; $this->icon = 'fa-link'; $this->order = 90; $this->group = 'fancy'; $this->init_pro_field(); $this->hooks(); } /** * Hooks. * * @since 1.9.4 */ protected function hooks() { } /** * Field options panel inside the builder. * * @since 1.9.4 * * @param array $field Field data. */ public function field_options( $field ) { /** * Basic field options. */ // Options open markup. $this->field_option( 'basic-options', $field, [ 'markup' => 'open', 'after_title' => $this->get_field_options_notice(), ] ); // Label. $this->field_option( 'label', $field ); // Description. $this->field_option( 'description', $field ); // Required toggle. $this->field_option( 'required', $field ); // Options close markup. $args = [ 'markup' => 'close', ]; $this->field_option( 'basic-options', $field, $args ); /* * Advanced field options. */ // Options open markup. $args = [ 'markup' => 'open', ]; $this->field_option( 'advanced-options', $field, $args ); // Size. $this->field_option( 'size', $field ); // Placeholder. $this->field_option( 'placeholder', $field ); // Default value. $this->field_option( 'default_value', $field ); // Custom CSS classes. $this->field_option( 'css', $field ); // Hide label. $this->field_option( 'label_hide', $field ); // Options close markup. $args = [ 'markup' => 'close', ]; $this->field_option( 'advanced-options', $field, $args ); } /** * Field preview inside the builder. * * @since 1.9.4 * * @param array $field Field data. */ public function field_preview( $field ) { // Define data. $placeholder = ! empty( $field['placeholder'] ) ? $field['placeholder'] : ''; $default_value = ! empty( $field['default_value'] ) ? $field['default_value'] : ''; // Label. $this->field_preview_option( 'label', $field, [ 'label_badge' => $this->get_field_preview_badge(), ] ); // Primary input. echo '<input type="url" placeholder="' . esc_attr( $placeholder ) . '" value="' . esc_attr( $default_value ) . '" class="primary-input" readonly>'; // Description. $this->field_preview_option( 'description', $field ); } /** * Field display on the form front-end. * * @since 1.9.4 * * @param array $field Field data and settings. * @param array $deprecated Deprecated field attributes. Use field properties. * @param array $form_data Form data and settings. */ public function field_display( $field, $deprecated, $form_data ) { } } Fields/PaymentSingle/Field.php 0000644 00000055453 15252506741 0012310 0 ustar 00 <?php namespace WPForms\Forms\Fields\PaymentSingle; /** * Single item payment field. * * @since 1.8.2 */ class Field extends \WPForms_Field { /** * User field format. * * @since 1.8.2 * * @var string */ const FORMAT_USER = 'user'; /** * Single field format. * * @since 1.8.2 * * @var string */ const FORMAT_SINGLE = 'single'; /** * Hidden field format. * * @since 1.8.2 * * @var string */ const FORMAT_HIDDEN = 'hidden'; /** * Minimum price default value. * * @since 1.8.6 * * @var int */ const MIN_PRICE_DEFAULT = 10; /** * Primary class constructor. * * @since 1.8.2 */ public function init() { // Define field type information. $this->name = esc_html__( 'Single Item', 'wpforms-lite' ); $this->keywords = esc_html__( 'product, store, ecommerce, pay, payment', 'wpforms-lite' ); $this->type = 'payment-single'; $this->icon = 'fa-file-o'; $this->order = 30; $this->group = 'payment'; $this->hooks(); } /** * Define additional field hooks. * * @since 1.8.2 */ private function hooks() { // Define additional field properties. add_filter( "wpforms_field_properties_{$this->type}", [ $this, 'field_properties' ], 5, 3 ); add_action( 'wpforms_display_field_after', [ $this, 'field_minimum_price_description' ], 10, 2 ); add_filter( 'wpforms_field_preview_class', [ $this, 'preview_field_class' ], 10, 2 ); // Customize HTML field value. add_filter( 'wpforms_html_field_value', [ $this, 'field_html_value' ], 10, 4 ); } /** * Define additional field properties. * * @since 1.8.2 * * @param array $properties Field properties. * @param array $field Field settings. * @param array $form_data Form data and settings. * * @return array */ public function field_properties( $properties, $field, $form_data ) { // phpcs:ignore Generic.Metrics.CyclomaticComplexity.TooHigh // Basic IDs. $form_id = absint( $form_data['id'] ); $field_id = absint( $field['id'] ); // Set options container (<select>) properties. $properties['input_container'] = [ 'class' => [ 'wpforms-payment-price' ], 'data' => [], 'id' => "wpforms-{$form_id}-field_{$field_id}", ]; // User format data and class. $field_format = ! empty( $field['format'] ) ? $field['format'] : self::FORMAT_SINGLE; if ( $this->is_user_defined( $field ) ) { $properties['inputs']['primary']['data']['rule-currency'] = '["$",false]'; $properties['inputs']['primary']['class'][] = 'wpforms-payment-user-input'; if ( ! empty( $field['min_price'] ) ) { $properties['inputs']['primary']['data']['rule-required-minimum-price'] = wpforms_sanitize_amount( $field['min_price'] ); } } // Null 'for' value for label as there no input for it. if ( ! $this->is_user_defined( $field ) ) { unset( $properties['label']['attr']['for'] ); } $properties['inputs']['primary']['class'][] = 'wpforms-payment-price'; // Check size. if ( ! empty( $field['size'] ) ) { $properties['inputs']['primary']['class'][] = 'wpforms-field-' . esc_attr( $field['size'] ); } $required = ! empty( $form_data['fields'][ $field_id ]['required'] ); if ( $required ) { $properties['inputs']['primary']['data']['rule-required-positive-number'] = true; } // Price. if ( ! empty( $field['price'] ) ) { $field_value = wpforms_sanitize_amount( $field['price'] ); } elseif ( $required && $field_format === self::FORMAT_SINGLE ) { $field_value = wpforms_format_amount( 0 ); } else { $field_value = ''; } $properties['inputs']['primary']['attr']['value'] = ! empty( $field_value ) ? wpforms_format_amount( $field_value, true ) : $field_value; // Single item and hidden format should hide the input field. if ( $this->is_hidden( $field ) ) { $properties['container']['class'][] = 'wpforms-field-hidden'; $properties['label']['class'][] = 'wpforms-hidden'; } if ( $this->is_payment_quantities_enabled( $field ) ) { $properties['container']['class'][] = ' wpforms-payment-quantities-enabled'; } return $properties; } /** * Get field populated single property value. * * @since 1.8.2 * * @param string $raw_value Value from a GET param, always a string. * @param string $input Represent a subfield inside the field. May be empty. * @param array $properties Field properties. * @param array $field Current field specific data. * * @return array Modified field properties. */ protected function get_field_populated_single_property_value( $raw_value, $input, $properties, $field ) { if ( ! is_string( $raw_value ) ) { return $properties; } if ( ! $this->is_user_defined( $field ) ) { return $properties; } $get_value = stripslashes( sanitize_text_field( $raw_value ) ); $get_value = ! empty( $get_value ) ? wpforms_sanitize_amount( $get_value ) : ''; $get_value_formatted = ! empty( $get_value ) ? wpforms_format_amount( $get_value ) : ''; // `primary` by default. if ( ! empty( $input ) && isset( $properties['inputs'][ $input ] ) ) { $properties['inputs'][ $input ]['attr']['value'] = $get_value_formatted; } return $properties; } /** * Field options panel inside the builder. * * @since 1.8.2 * * @param array $field Field data and settings. */ public function field_options( $field ) { /* * Basic field options. */ $this->field_option( 'basic-options', $field, [ 'markup' => 'open' ] ); $this->field_option( 'label', $field ); $this->field_option( 'description', $field ); $this->price_option( $field ); $this->format_option( $field ); $this->min_price_option( $field ); $this->field_option( 'quantity', $field, [ 'hidden' => ! $this->is_single_item( $field ) ] ); $this->field_option( 'required', $field ); $this->field_option( 'basic-options', $field, [ 'markup' => 'close' ] ); $this->field_option( 'advanced-options', $field, [ 'markup' => 'open' ] ); $this->field_option( 'size', $field ); $this->price_label_option( $field ); $visibility = ! empty( $field['format'] ) && $this->is_user_defined( $field ) ? '' : 'wpforms-hidden'; $this->field_option( 'placeholder', $field, [ 'class' => $visibility ] ); $this->field_option( 'css', $field ); $this->field_option( 'label_hide', $field ); $this->field_option( 'advanced-options', $field, [ 'markup' => 'close' ] ); } /** * Price label option. * * @since 1.8.8 * * @param array $field Field Data. * * @return void */ private function price_label_option( array $field ) { // Price display. $output = $this->field_element( 'label', $field, [ 'slug' => 'price_label', 'value' => esc_html__( 'Price Display', 'wpforms-lite' ), 'tooltip' => esc_html__( 'Specify how the price is displayed under the product name.', 'wpforms-lite' ), ], false ); $output .= $this->field_element( 'text', $field, [ 'slug' => 'price_label', 'class' => 'wpforms-single-item-price-label-display', 'value' => $this->get_single_item_price_label( $field ), ], false ); $this->field_element( 'row', $field, [ 'slug' => 'price_label', 'content' => $output, 'class' => $this->is_single_item( $field ) ? '' : 'wpforms-hidden', ] ); } /** * Get price label for single item type. * * @since 1.8.8 * * @param array $field Field data and settings. */ private function get_single_item_price_label( array $field ) { if ( ! isset( $field['price_label'] ) ) { return sprintf( /* translators: %s - Single item field price label. */ esc_html__( 'Price: %s', 'wpforms-lite' ), '{price}' ); } return $field['price_label']; } /** * Field price option. * * @since 1.8.6 * * @param array $field Field data and settings. */ private function price_option( $field ) { $price = ! empty( $field['price'] ) ? wpforms_format_amount( wpforms_sanitize_amount( $field['price'] ) ) : ''; $tooltip = esc_html__( 'Enter the price of the item, without a currency symbol.', 'wpforms-lite' ); $output = $this->field_element( 'label', $field, [ 'slug' => 'price', 'value' => esc_html__( 'Item Price', 'wpforms-lite' ), 'tooltip' => $tooltip, ], false ); $output .= $this->field_element( 'text', $field, [ 'slug' => 'price', 'value' => $price, 'class' => 'wpforms-money-input', 'placeholder' => wpforms_format_amount( 0 ), ], false ); $this->field_element( 'row', $field, [ 'slug' => 'price', 'content' => $output, ] ); } /** * Field format option. * * @since 1.8.6 * * @param array $field Field data and settings. */ private function format_option( $field ) { $format = ! empty( $field['format'] ) ? esc_attr( $field['format'] ) : self::FORMAT_SINGLE; $tooltip = esc_html__( 'Select the item type.', 'wpforms-lite' ); $options = [ self::FORMAT_SINGLE => esc_html__( 'Single Item', 'wpforms-lite' ), self::FORMAT_USER => esc_html__( 'User Defined', 'wpforms-lite' ), self::FORMAT_HIDDEN => esc_html__( 'Hidden', 'wpforms-lite' ), ]; $output = $this->field_element( 'label', $field, [ 'slug' => 'format', 'value' => esc_html__( 'Item Type', 'wpforms-lite' ), 'tooltip' => $tooltip, ], false ); $output .= $this->field_element( 'select', $field, [ 'slug' => 'format', 'value' => $format, 'options' => $options, ], false ); $this->field_element( 'row', $field, [ 'slug' => 'format', 'content' => $output, ] ); } /** * Field minimum price option. * * @since 1.8.6 * * @param array $field Field data and settings. */ private function min_price_option( $field ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing if ( isset( $_POST['action'] ) && $_POST['action'] === 'wpforms_new_field_payment-single' ) { // Use a default minimum price when adding new field. $min_price = wpforms_format_amount( self::MIN_PRICE_DEFAULT ); } elseif ( isset( $field['min_price'] ) ) { // Use saved minimum price if it exists. $min_price = wpforms_format_amount( wpforms_sanitize_amount( $field['min_price'] ) ); } else { // Use 0 as a fallback for old forms. $min_price = 0; } $tooltip = esc_html__( 'Enter the minimum price of the item, without a currency symbol.', 'wpforms-lite' ); $is_hidden = empty( $field['format'] ) || ! $this->is_user_defined( $field ) ? 'wpforms-hidden' : ''; $output = $this->field_element( 'label', $field, [ 'slug' => 'min_price', 'value' => esc_html__( 'Minimum Price', 'wpforms-lite' ), 'tooltip' => $tooltip, ], false ); $output .= $this->field_element( 'text', $field, [ 'slug' => 'min_price', 'value' => $min_price, 'data' => [ 'minimum-price' => self::MIN_PRICE_DEFAULT, ], 'class' => 'wpforms-money-input', ], false ); $notice = sprintf( /* translators: %1$s - the default minimum price. */ esc_html__( 'Requiring a minimum price of at least %1$s helps protect you against card testing by fraudsters.', 'wpforms-lite' ), esc_html( wpforms_format_amount( self::MIN_PRICE_DEFAULT, true ) ) ); $is_notice_hidden = $this->is_min_price_passed( $field ) || $is_hidden ? 'wpforms-hidden' : ''; $output .= sprintf( '<div class="wpforms-alert-warning wpforms-alert wpforms-item-minimum-price-alert %1$s"> <h4>%2$s</h4> <p>%3$s</p> </div>', esc_attr( $is_notice_hidden ), esc_html__( 'Security Recommendation', 'wpforms-lite' ), $notice ); $this->field_element( 'row', $field, [ 'slug' => 'min_price', 'content' => $output, 'class' => $is_hidden, ] ); } /** * Field preview inside the builder. * * @since 1.8.2 * * @param array $field Field data and settings. */ public function field_preview( $field ) { $price = ! empty( $field['price'] ) ? wpforms_format_amount( wpforms_sanitize_amount( $field['price'] ), true ) : wpforms_format_amount( 0, true ); $min_price = ! empty( $field['min_price'] ) ? wpforms_format_amount( wpforms_sanitize_amount( $field['min_price'] ), true ) : wpforms_format_amount( self::MIN_PRICE_DEFAULT, true ); $placeholder = ! empty( $field['placeholder'] ) ? $field['placeholder'] : wpforms_format_amount( 0 ); $format = ! empty( $field['format'] ) ? $field['format'] : self::FORMAT_SINGLE; $value = ! empty( $field['price'] ) ? wpforms_format_amount( wpforms_sanitize_amount( $field['price'] ) ) : ''; $is_single = $this->is_single_item( $field ); $single_label = str_replace( '{price}', '<span class="price">' . esc_html( $price ) . '</span>', wp_kses( $this->get_single_item_price_label( $field ), wpforms_builder_preview_get_allowed_tags() ) ); $this->field_preview_option( 'label', $field ); echo '<div class="format-selected-' . esc_attr( $format ) . ' format-selected">'; $hidden = ! $is_single ? 'wpforms-hidden' : ''; echo '<p class="item-price item-price-single ' . esc_attr( $hidden ) . '">'; echo wp_kses( '<span class="price-label">' . $single_label . '</span>', [ 'span' => [ 'class' => [], ], ] ); echo '</p>'; $hidden = ! $this->is_hidden( $field ) ? 'wpforms-hidden' : ''; echo '<p class="item-price item-price-hidden ' . esc_attr( $hidden ) . '">'; printf( wp_kses( /* translators: %1$s - Item Price value. */ __( 'Price: <span class="price">%1$s</span>', 'wpforms-lite' ), [ 'span' => [ 'class' => [], ], ] ), esc_html( $price ) ); echo '</p>'; $hidden = ! $is_single ? 'wpforms-hidden' : ''; $this->field_preview_option( 'quantity', $field, [ 'class' => $hidden ] ); echo '<div class="single-item-user-defined-block">'; printf( '<input type="text" placeholder="%s" class="primary-input" value="%s" readonly>', esc_attr( $placeholder ), esc_attr( $value ) ); $hidden = $this->is_min_price_passed( $field ) ? 'wpforms-hidden' : ''; echo '<i class="fa fa-exclamation-triangle ' . esc_attr( $hidden ) . '"></i>'; echo '</div>'; $this->field_preview_option( 'description', $field ); $hidden = ! isset( $field['min_price'] ) || empty( (float) wpforms_sanitize_amount( $field['min_price'] ) ) ? 'wpforms-hidden' : ''; echo '<div class="item-min-price ' . esc_attr( $hidden ) . '">'; printf( wp_kses( /* translators: %1$s - Minimum Price value. */ __( 'Minimum Price: <span class="min-price">%1$s</span>', 'wpforms-lite' ), [ 'span' => [ 'class' => [], ], ] ), esc_html( $min_price ) ); echo '</div>'; echo '<p class="item-price-hidden-note">'; esc_html_e( 'Note: Item type is set to hidden and will not be visible when viewing the form.', 'wpforms-lite' ); echo '</p>'; echo '</div>'; } /** * Field display on the form front-end. * * @since 1.8.2 * * @param array $field Field data and settings. * @param array $deprecated Deprecated field attributes. * @param array $form_data Form data and settings. */ public function field_display( $field, $deprecated, $form_data ) { // Shortcut for easier access. $primary = $field['properties']['inputs']['primary']; $field_format = ! empty( $field['format'] ) ? $field['format'] : self::FORMAT_SINGLE; // Placeholder attribute is only applicable to password, search, tel, text and url inputs, not hidden. // aria-errormessage attribute is not allowed for hidden inputs. if ( ! $this->is_user_defined( $field ) ) { unset( $primary['attr']['placeholder'], $primary['attr']['aria-errormessage'] ); } switch ( $field_format ) { case self::FORMAT_SINGLE: case self::FORMAT_HIDDEN: if ( $field_format === self::FORMAT_SINGLE ) { $price = ! empty( $field['price'] ) ? $field['price'] : 0; $field_label = str_replace( '{price}', '<span class="wpforms-price">' . esc_html( wpforms_format_amount( wpforms_sanitize_amount( $price ), true ) ) . '</span>', $this->get_single_item_price_label( $field ) ); echo '<div class="wpforms-single-item-price-content">'; echo '<div class="wpforms-single-item-price ' . wpforms_sanitize_classes( $primary['class'], true ) . '">'; echo wp_kses( $field_label, [ 'span' => [ 'class' => [], ], ] ); echo '</div>'; $this->display_quantity_dropdown( $field ); echo '</div>'; } // Primary price field. printf( '<input type="hidden" %s>', wpforms_html_attributes( $primary['id'], $primary['class'], $primary['data'], $primary['attr'] ) // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ); break; case self::FORMAT_USER: printf( '<input type="text" %s>', wpforms_html_attributes( $primary['id'], $primary['class'], $primary['data'], $primary['attr'] ) // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ); break; default: break; } } /** * Validate field on form submit. * * @since 1.8.2 * * @param int $field_id Field ID. * @param string $field_submit Submitted field value (raw data). * @param array $form_data Form data and settings. */ public function validate( $field_id, $field_submit, $form_data ) { $is_required = ! empty( $form_data['fields'][ $field_id ]['required'] ); // If field is required, check for data. if ( empty( $field_submit ) && $is_required ) { wpforms()->obj( 'process' )->errors[ $form_data['id'] ][ $field_id ] = wpforms_get_required_label(); return; } /** * Whether to validate amount or not of the Payment Single item field. * * @since 1.8.4 * * @param bool $validate Whether to validate amount or not. Default true. * @param int $field_id Field ID. * @param string $field_submit Field data submitted by a user. * @param array $form_data Form data and settings. */ $validate_amount = apply_filters( 'wpforms_forms_fields_payment_single_field_validate_amount', true, $field_id, $field_submit, $form_data ); // If field format is not user provided, validate the amount posted. if ( ! empty( $field_submit ) && $validate_amount && ! $this->is_user_defined( $form_data['fields'][ $field_id ] ) ) { $price = wpforms_sanitize_amount( $form_data['fields'][ $field_id ]['price'] ); $submit = wpforms_sanitize_amount( $field_submit ); if ( $price !== $submit ) { wpforms()->obj( 'process' )->errors[ $form_data['id'] ][ $field_id ] = esc_html__( 'Amount mismatch', 'wpforms-lite' ); } } // If field format is provided by user, additionally compare the amount with a minimum price. if ( ! empty( $field_submit ) && $validate_amount && $this->is_user_defined( $form_data['fields'][ $field_id ] ) ) { $submit = wpforms_sanitize_amount( $field_submit ); if ( $submit < 0 ) { wpforms()->obj( 'process' )->errors[ $form_data['id'] ][ $field_id ] = esc_html__( 'Amount can\'t be negative' , 'wpforms-lite' ); } if ( empty( $form_data['fields'][ $field_id ]['min_price'] ) && ! $is_required ) { return; } $min_price = wpforms_sanitize_amount( $form_data['fields'][ $field_id ]['min_price'] ); if ( $submit < $min_price ) { wpforms()->obj( 'process' )->errors[ $form_data['id'] ][ $field_id ] = esc_html__( 'Amount can\'t be less than the required minimum.' , 'wpforms-lite' ); } } } /** * Format and sanitize field. * * @since 1.8.2 * * @param int $field_id Field ID. * @param string $field_submit Field data submitted by a user. * @param array $form_data Form data and settings. */ public function format( $field_id, $field_submit, $form_data ) { $field = $form_data['fields'][ $field_id ]; $name = ! empty( $field['label'] ) ? sanitize_text_field( $field['label'] ) : ''; // Only trust the value if the field has the user defined format OR it is the entry preview. if ( $this->is_user_defined( $field ) || wpforms_is_ajax( 'wpforms_get_entry_preview' ) ) { $amount = wpforms_sanitize_amount( $field_submit ); } else { $amount = wpforms_sanitize_amount( $field['price'] ); } $field_data = [ 'name' => $name, 'value' => wpforms_format_amount( $amount, true ), 'amount' => wpforms_format_amount( $amount ), 'amount_raw' => $amount, 'currency' => wpforms_get_currency(), 'id' => absint( $field_id ), 'type' => sanitize_key( $this->type ), ]; if ( $this->is_payment_quantities_enabled( $field ) ) { $field_data['quantity'] = $this->get_submitted_field_quantity( $field, $form_data ); } wpforms()->obj( 'process' )->fields[ $field_id ] = $field_data; } /** * Display the minimum price description for the field. * * @since 1.8.6 * * @param array $field Field data and settings. * @param array $form_data Form data and settings. */ public function field_minimum_price_description( $field, $form_data ) { if ( ! $this->is_user_defined( $field ) || ! isset( $field['min_price'] ) || empty( (float) wpforms_sanitize_amount( $field['min_price'] ) ) ) { return; } $description = sprintf( /* translators: %1$s - Minimum Price value. */ __( 'Minimum Price: %1$s', 'wpforms-lite' ), wpforms_format_amount( wpforms_sanitize_amount( $field['min_price'] ), true ) ); printf( '<div class="wpforms-field-description">%s</div>', esc_html( $description ) ); } /** * Add class to the builder field preview. * * @since 1.8.6 * * @param string $css Class names. * @param array $field Field properties. * * @return string */ public function preview_field_class( $css, $field ) { $css = parent::preview_field_class( $css, $field ); if ( $field['type'] !== $this->type ) { return $css; } if ( ! $this->is_user_defined( $field ) ) { return $css; } if ( $this->is_min_price_passed( $field ) ) { return $css; } $css .= ' min-price-warning'; return $css; } /** * Define if format of field is User Defined. * * @since 1.8.6 * * @param array $field Field data. * * @return bool */ private function is_user_defined( $field ) { return ! empty( $field['format'] ) && $field['format'] === self::FORMAT_USER; } /** * Define if format of field is Single Item. * * @since 1.8.7 * * @param array $field Field data. * * @return bool */ private function is_single_item( $field ) { return empty( $field['format'] ) || $field['format'] === self::FORMAT_SINGLE; } /** * Define if format of field is Hidden. * * @since 1.8.8 * * @param array $field Field data. * * @return bool */ private function is_hidden( $field ) { return empty( $field['format'] ) || $field['format'] === self::FORMAT_HIDDEN; } /** * Define if minimum price is equal or more than default one. * * @since 1.8.6 * * @param array $field Field data. * * @return bool */ private function is_min_price_passed( $field ) { return isset( $field['min_price'] ) && (float) wpforms_sanitize_amount( $field['min_price'] ) >= (float) self::MIN_PRICE_DEFAULT; } } Fields/PaymentMultiple/Field.php 0000644 00000036236 15252506741 0012660 0 ustar 00 <?php namespace WPForms\Forms\Fields\PaymentMultiple; use WPForms_Field; /** * Radio payment field. * * @since 1.8.2 */ class Field extends WPForms_Field { /** * Primary class constructor. * * @since 1.8.2 */ public function init() { // Define field type information. $this->name = esc_html__( 'Multiple Items', 'wpforms-lite' ); $this->keywords = esc_html__( 'product, store, ecommerce, pay, payment', 'wpforms-lite' ); $this->type = 'payment-multiple'; $this->icon = 'fa-list-ul'; $this->order = 50; $this->group = 'payment'; $this->defaults = [ 1 => [ 'label' => esc_html__( 'First Item', 'wpforms-lite' ), 'value' => '10', 'icon' => '', 'icon_style' => '', 'image' => '', 'default' => '', ], 2 => [ 'label' => esc_html__( 'Second Item', 'wpforms-lite' ), 'value' => '25', 'icon' => '', 'icon_style' => '', 'image' => '', 'default' => '', ], 3 => [ 'label' => esc_html__( 'Third Item', 'wpforms-lite' ), 'value' => '50', 'icon' => '', 'icon_style' => '', 'image' => '', 'default' => '', ], ]; $this->default_settings = [ 'choices' => $this->defaults, ]; $this->hooks(); } /** * Register hooks. * * @since 1.8.1 */ private function hooks() { // Customize HTML field values. add_filter( 'wpforms_html_field_value', [ $this, 'field_html_value' ], 10, 4 ); add_filter( "wpforms_{$this->type}_field_html_value_images", [ $this, 'field_html_value_images' ], 10, 3 ); // Define additional field properties. add_filter( "wpforms_field_properties_{$this->type}", [ $this, 'field_properties' ], 5, 3 ); // This field requires fieldset+legend instead of the field label. add_filter( "wpforms_frontend_modern_is_field_requires_fieldset_{$this->type}", '__return_true', PHP_INT_MAX, 2 ); } /** * Define additional field properties. * * @since 1.8.2 * * @param array $properties Field properties. * @param array $field Field settings. * @param array $form_data Form data and settings. * * @return array */ public function field_properties( $properties, $field, $form_data ) { // phpcs:ignore Generic.Metrics.CyclomaticComplexity.TooHigh // Define data. $form_id = absint( $form_data['id'] ); $field_id = absint( $field['id'] ); $choices = $field['choices']; // Remove primary input, unset for attribute for label. unset( $properties['inputs']['primary'], $properties['label']['attr']['for'] ); // Set input container (ul) properties. $properties['input_container'] = [ 'class' => [], 'data' => [], 'attr' => [], 'id' => "wpforms-{$form_id}-field_{$field_id}", ]; // Set input properties. foreach ( $choices as $key => $choice ) { $properties['inputs'][ $key ] = [ 'container' => [ 'attr' => [], 'class' => [ "choice-{$key}" ], 'data' => [], 'id' => '', ], 'label' => [ 'attr' => [ 'for' => "wpforms-{$form_id}-field_{$field_id}_{$key}", ], 'class' => [ 'wpforms-field-label-inline' ], 'data' => [], 'id' => '', 'text' => $this->get_choices_label( $choice['label'] ?? '', $key, $field ), ], 'attr' => [ 'name' => "wpforms[fields][{$field_id}]", 'value' => $key, ], 'class' => [ 'wpforms-payment-price' ], 'data' => [ 'amount' => wpforms_format_amount( wpforms_sanitize_amount( $choice['value'] ?? '' ) ), ], 'id' => "wpforms-{$form_id}-field_{$field_id}_{$key}", 'icon' => $choice['icon'] ?? '', 'icon_style' => $choice['icon_style'] ?? '', 'image' => $choice['image'] ?? '', 'required' => ! empty( $field['required'] ) ? 'required' : '', 'default' => isset( $choice['default'] ), ]; } // Required class for pagebreak validation. if ( ! empty( $field['required'] ) ) { $properties['input_container']['class'][] = 'wpforms-field-required'; } // Custom properties if image choices are enabled. if ( ! empty( $field['choices_images'] ) ) { $properties['input_container']['class'][] = 'wpforms-image-choices'; $properties['input_container']['class'][] = 'wpforms-image-choices-' . sanitize_html_class( $field['choices_images_style'] ); foreach ( $properties['inputs'] as $key => $inputs ) { $properties['inputs'][ $key ]['container']['class'][] = 'wpforms-image-choices-item'; if ( in_array( $field['choices_images_style'], [ 'modern', 'classic' ], true ) ) { $properties['inputs'][ $key ]['class'][] = 'wpforms-screen-reader-element'; } } } elseif ( ! empty( $field['choices_icons'] ) ) { $properties = wpforms()->obj( 'icon_choices' )->field_properties( $properties, $field ); } // Add selected class for choices with defaults. foreach ( $properties['inputs'] as $key => $inputs ) { if ( ! empty( $inputs['default'] ) ) { $properties['inputs'][ $key ]['container']['class'][] = 'wpforms-selected'; } } return $properties; } /** * Get field populated single property value. * * @since 1.8.2 * * @param string $raw_value Value from a GET param, always a string. * @param string $input Represent a subfield inside the field. May be empty. * @param array $properties Field properties. * @param array $field Current field specific data. * * @return array Modified field properties. */ protected function get_field_populated_single_property_value( $raw_value, $input, $properties, $field ) { /* * When the form is submitted, we get only values (prices) from the Fallback. * As payment-multiple (radio) field doesn't support 'show_values' option - * we should transform value into label to check against using general logic in parent method. */ if ( ! is_string( $raw_value ) || empty( $field['choices'] ) || ! is_array( $field['choices'] ) ) { return $properties; } // The form submits only the sum, so shortcut for Dynamic. if ( ! is_numeric( $raw_value ) ) { return parent::get_field_populated_single_property_value( $raw_value, $input, $properties, $field ); } $get_value = wpforms_format_amount( wpforms_sanitize_amount( $raw_value ) ); foreach ( $field['choices'] as $choice ) { if ( isset( $choice['label'], $choice['value'] ) && wpforms_format_amount( wpforms_sanitize_amount( $choice['value'] ) ) === $get_value ) { $trans_value = $choice['label']; // Stop iterating over choices. break; } } if ( empty( $trans_value ) ) { return $properties; } return parent::get_field_populated_single_property_value( $trans_value, $input, $properties, $field ); } /** * Field options panel inside the builder. * * @since 1.8.2 * * @param array $field Field settings. */ public function field_options( $field ) { /* * Basic field options. */ // Options open markup. $this->field_option( 'basic-options', $field, [ 'markup' => 'open', ] ); // Label. $this->field_option( 'label', $field ); // Choices option. $this->field_option( 'choices_payments', $field ); // Show price after item labels. $fld = $this->field_element( 'toggle', $field, [ 'slug' => 'show_price_after_labels', 'value' => isset( $field['show_price_after_labels'] ) ? '1' : '0', 'desc' => esc_html__( 'Show Price After Item Labels', 'wpforms-lite' ), 'tooltip' => esc_html__( 'Check this option to show price of the item after the label.', 'wpforms-lite' ), ], false ); $args = [ 'slug' => 'show_price_after_labels', 'content' => $fld, ]; $this->field_element( 'row', $field, $args ); // Choices Images. $this->field_option( 'choices_images', $field ); // Hide Choices Images. $this->field_option( 'choices_images_hide', $field ); // Choice Images Style (theme). $this->field_option( 'choices_images_style', $field ); // Choices Icons. $this->field_option( 'choices_icons', $field ); // Choices Icons Color. $this->field_option( 'choices_icons_color', $field ); // Choices Icons Size. $this->field_option( 'choices_icons_size', $field ); // Choices Icons Style. $this->field_option( 'choices_icons_style', $field ); // Description. $this->field_option( 'description', $field ); // Required toggle. $this->field_option( 'required', $field ); // Options close markup. $this->field_option( 'basic-options', $field, [ 'markup' => 'close', ] ); /* * Advanced field options. */ // Options open markup. $this->field_option( 'advanced-options', $field, [ 'markup' => 'open', ] ); // Input columns. $this->field_option( 'input_columns', $field ); // Custom CSS classes. $this->field_option( 'css', $field ); // Hide label. $this->field_option( 'label_hide', $field ); // Options close markup. $this->field_option( 'advanced-options', $field, [ 'markup' => 'close', ] ); } /** * Field preview inside the builder. * * @since 1.8.2 * * @param array $field Field settings. */ public function field_preview( $field ) { // Label. $this->field_preview_option( 'label', $field ); // Choices. $this->field_preview_option( 'choices', $field ); // Description. $this->field_preview_option( 'description', $field ); } /** * Field display on the form front-end. * * @since 1.8.2 * * @param array $field Field settings. * @param array $deprecated Deprecated array. * @param array $form_data Form data and settings. * * @noinspection HtmlUnknownAttribute * @noinspection HtmlUnknownTarget */ public function field_display( $field, $deprecated, $form_data ) { // Define data. $container = $field['properties']['input_container']; $choices = $field['properties']['inputs']; printf( '<ul %s>', wpforms_html_attributes( $container['id'], $container['class'], $container['data'], $container['attr'] ) // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ); foreach ( $choices as $key => $choice ) { $label = $choice['label']['text'] ?? ''; /* translators: %s - item number. */ $label = $label !== '' ? $label : sprintf( esc_html__( 'Item %s', 'wpforms-lite' ), $key ); $label .= ! empty( $field['show_price_after_labels'] ) && isset( $choice['data']['amount'] ) ? $this->get_price_after_label( $choice['data']['amount'] ) : ''; printf( '<li %s>', wpforms_html_attributes( $choice['container']['id'], $choice['container']['class'], $choice['container']['data'], $choice['container']['attr'] ) // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ); if ( empty( $field['dynamic_choices'] ) && ! empty( $field['choices_images'] ) ) { // Image choices. printf( '<label %s>', wpforms_html_attributes( $choice['label']['id'], $choice['label']['class'], $choice['label']['data'], $choice['label']['attr'] ) // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ); echo '<span class="wpforms-image-choices-image">'; if ( ! empty( $choice['image'] ) ) { printf( '<img src="%s" alt="%s"%s>', esc_url( $choice['image'] ), esc_attr( $choice['label']['text'] ), ! empty( $choice['label']['text'] ) ? ' title="' . esc_attr( $choice['label']['text'] ) . '"' : '' ); } echo '</span>'; if ( $field['choices_images_style'] === 'none' ) { echo '<br>'; } printf( '<input type="radio" %s %s %s>', wpforms_html_attributes( $choice['id'], $choice['class'], $choice['data'], $choice['attr'] ), // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped esc_attr( $choice['required'] ), checked( '1', $choice['default'], false ) ); echo '<span class="wpforms-image-choices-label">' . wp_kses_post( $label ) . '</span>'; echo '</label>'; } elseif ( empty( $field['dynamic_choices'] ) && ! empty( $field['choices_icons'] ) ) { // Icon Choices. wpforms()->obj( 'icon_choices' )->field_display( $field, $choice, 'radio', $label ); } else { // Normal display. printf( '<input type="radio" %s %s %s>', wpforms_html_attributes( $choice['id'], $choice['class'], $choice['data'], $choice['attr'] ), // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped esc_attr( $choice['required'] ), checked( '1', $choice['default'], false ) ); printf( '<label %s>%s</label>', wpforms_html_attributes( $choice['label']['id'], $choice['label']['class'], $choice['label']['data'], $choice['label']['attr'] ), // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped wp_kses_post( $label ) ); } echo '</li>'; } echo '</ul>'; } /** * Validate field on submitting the form. * * @since 1.8.2 * * @param int $field_id Field ID. * @param mixed $field_submit Submitted field value (raw data). * @param array $form_data Form data and settings. */ public function validate( $field_id, $field_submit, $form_data ) { // Basic required check - If field is marked as required, check for entry data. if ( ! empty( $form_data['fields'][ $field_id ]['required'] ) && empty( $field_submit ) ) { wpforms()->obj( 'process' )->errors[ $form_data['id'] ][ $field_id ] = wpforms_get_required_label(); } // Validate that the option selected is real. if ( is_string( $field_submit ) && ! empty( $field_submit ) && empty( $form_data['fields'][ $field_id ]['choices'][ $field_submit ] ) ) { wpforms()->obj( 'process' )->errors[ $form_data['id'] ][ $field_id ] = esc_html__( 'Invalid payment option.', 'wpforms-lite' ); } } /** * Format and sanitize field. * * @since 1.8.2 * * @param int $field_id Field ID. * @param string $field_submit Submitted form data. * @param array $form_data Form data and settings. */ public function format( $field_id, $field_submit, $form_data ) { $field = $form_data['fields'][ $field_id ]; $name = sanitize_text_field( $field['label'] ); $value = ''; $amount = 0; $choice_label = ''; $image = ''; if ( ! empty( $field_submit ) && ! empty( $field['choices'][ $field_submit ] ) ) { $amount = wpforms_sanitize_amount( $field['choices'][ $field_submit ]['value'] ?? '' ); $value = wpforms_format_amount( $amount, true ); if ( ! empty( $field['choices'][ $field_submit ]['label'] ) ) { $choice_label = sanitize_text_field( $field['choices'][ $field_submit ]['label'] ); $value = $choice_label . ' - ' . $value; } if ( ! empty( $field['choices_images'] ) ) { $image = ! empty( $field['choices'][ $field_submit ]['image'] ) ? esc_url_raw( $field['choices'][ $field_submit ]['image'] ) : ''; } } wpforms()->obj( 'process' )->fields[ $field_id ] = [ 'name' => $name, 'value' => $value, 'value_choice' => $choice_label, 'value_raw' => sanitize_text_field( $field_submit ), 'amount' => wpforms_format_amount( $amount ), 'amount_raw' => $amount, 'currency' => wpforms_get_currency(), 'image' => $image, 'id' => absint( $field_id ), 'type' => sanitize_key( $this->type ), ]; } } Fields/PaymentTotal/Field.php 0000644 00000057155 15252506741 0012153 0 ustar 00 <?php namespace WPForms\Forms\Fields\PaymentTotal; use WPForms\Forms\Fields\Helpers\RequirementsAlerts; use WPForms_Builder_Panel_Settings; use WPForms_Field; /** * Total payment field. * * @since 1.8.2 */ class Field extends WPForms_Field { /** * Primary class constructor. * * @since 1.8.2 */ public function init() { // Define field type information. $this->name = esc_html__( 'Total', 'wpforms-lite' ); $this->keywords = esc_html__( 'store, ecommerce, pay, payment, sum', 'wpforms-lite' ); $this->type = 'payment-total'; $this->icon = 'fa-money'; $this->order = 110; $this->group = 'payment'; $this->allow_read_only = false; $this->hooks(); } /** * Hooks. * * @since 1.8.2 */ private function hooks(): void { // Define additional field properties. add_filter( "wpforms_field_properties_{$this->type}", [ $this, 'field_properties' ], 5, 3 ); // Recalculate total for a form. add_filter( 'wpforms_process_filter', [ $this, 'calculate_total' ], 10, 3 ); // Add classes to the builder field preview. add_filter( 'wpforms_field_preview_class', [ $this, 'preview_field_class' ], 10, 2 ); // Add a new option on the confirmation page. add_action( 'wpforms_form_settings_confirmations_single_after', [ $this, 'add_confirmation_setting' ], 10, 2 ); add_action( 'wpforms_lite_form_settings_confirmations_single_after', [ $this, 'add_confirmation_setting' ], 10, 2 ); add_action( 'wpforms_frontend_confirmation_message_after', [ $this, 'order_summary_confirmation' ], 10, 4 ); } /** * Define additional field properties. * * @since 1.8.2 * * @param array $properties Field properties. * @param array $field Field data and settings. * @param array $form_data Form data and settings. * * @return array * @noinspection PhpMissingParamTypeInspection * @noinspection PhpUnusedParameterInspection */ public function field_properties( $properties, $field, $form_data ) { // Input Primary: initial total is always zero. $properties['inputs']['primary']['attr']['value'] = '0'; // Input Primary: add class for targeting calculations. $properties['inputs']['primary']['class'][] = 'wpforms-payment-total'; // Input Primary: add a data attribute if total is required. if ( ! empty( $field['required'] ) ) { $properties['inputs']['primary']['data']['rule-required-payment'] = true; } // Check size. if ( ! empty( $field['size'] ) ) { $properties['container']['class'][] = 'wpforms-field-' . esc_attr( $field['size'] ); } // Input Primary: add class for targeting summary. if ( $this->is_summary_enabled( $field ) ) { $properties['container']['class'][] = 'wpforms-summary-enabled'; } // Unset for attribute for label. unset( $properties['label']['attr']['for'] ); return $properties; } /** * Whether the current field can be populated dynamically. * * @since 1.8.2 * * @param array $properties Field properties. * @param array $field Current field specific data. * * @return bool */ public function is_dynamic_population_allowed( $properties, $field ): bool { return false; } /** * Whether the current field can be populated dynamically. * * @since 1.8.2 * * @param array $properties Field properties. * @param array $field Current field specific data. * * @return bool */ public function is_fallback_population_allowed( $properties, $field ): bool { return false; } /** * Do not trust the posted total since that relies on JavaScript. * * Instead, we re-calculate on the server side. * * @since 1.8.2 * * @param array $fields List of fields with their data. * @param array $entry Submitted form data. * @param array $form_data Form data and settings. * * @return array */ public function calculate_total( $fields, $entry, $form_data ) { return self::calculate_total_static( $fields, $entry, $form_data ); } /** * Static version of calculate_total(). * * @since 1.8.4 * * @param array $fields List of fields with their data. * @param array $entry Submitted form data. * @param array $form_data Form data and settings. * * @return array * @noinspection PhpMissingParamTypeInspection * @noinspection PhpUnusedParameterInspection */ public static function calculate_total_static( $fields, $entry, $form_data ) { if ( ! is_array( $fields ) ) { return $fields; } // At this point we have passed processing and validation, so we know // the amounts in $fields are safe to use. $total = wpforms_get_total_payment( $fields ); $amount = wpforms_sanitize_amount( $total ); foreach ( $fields as $id => $field ) { if ( ! empty( $field['type'] ) && $field['type'] === 'payment-total' ) { $fields[ $id ]['value'] = wpforms_format_amount( $amount, true ); $fields[ $id ]['amount'] = wpforms_format_amount( $amount ); $fields[ $id ]['amount_raw'] = $amount; } } return $fields; } /** * Field options panel inside the builder. * * @since 1.8.2 * * @param array $field Field data and settings. */ public function field_options( $field ) { /* * Basic field options. */ // Options open markup. $args = [ 'markup' => 'open', ]; $this->field_option( 'basic-options', $field, $args ); // Label. $this->field_option( 'label', $field ); // Description. $this->field_option( 'description', $field ); // Enable Summary. $this->summary_option( $field ); // Summary Notice. $this->summary_option_notice( $field ); // Required toggle. $this->field_option( 'required', $field ); // Options close markup. $args = [ 'markup' => 'close', ]; $this->field_option( 'basic-options', $field, $args ); /* * Advanced field options. */ // Options open markup. $args = [ 'markup' => 'open', ]; $this->field_option( 'advanced-options', $field, $args ); // Size. $this->field_option( 'size', $field, [ 'exclude' => [ 'small' ], // phpcs:ignore WordPressVIPMinimum.Performance.WPQueryParams.PostNotIn_exclude ] ); // Custom CSS classes. $this->field_option( 'css', $field ); // Hide label. $this->field_option( 'label_hide', $field ); // Options close markup. $args = [ 'markup' => 'close', ]; $this->field_option( 'advanced-options', $field, $args ); } /** * Field preview inside the builder. * * @since 1.8.2 * * @param array $field Field data and settings. */ public function field_preview( $field ) { // Label. $this->field_preview_option( 'label', $field ); [ $items, $foot, $total_width ] = $this->prepare_builder_preview_data(); // Summary preview. // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped echo wpforms_render( 'fields/total/summary-preview', [ 'items' => $items, 'foot' => $foot, 'total_width' => $total_width, ], true ); // Primary field. echo '<div class="wpforms-total-amount">' . esc_html( wpforms_format_amount( 0, true ) ) . '</div>'; // Description. $this->field_preview_option( 'description', $field ); } /** * Field display on the form front-end. * * @since 1.8.2 * * @param array $field Field data and settings. * @param array $deprecated Deprecated, not used parameter. * @param array $form_data Form data and settings. * * @noinspection HtmlWrongAttributeValue * @noinspection HtmlUnknownAttribute */ public function field_display( $field, $deprecated, $form_data ) { $primary = $field['properties']['inputs']['primary']; $type = ! empty( $field['required'] ) ? 'text' : 'hidden'; $attrs = $primary['attr']; if ( ! empty( $field['required'] ) ) { $attrs['style'] = 'position:absolute!important;clip:rect(0,0,0,0)!important;height:1px!important;width:1px!important;border:0!important;overflow:hidden!important;padding:0!important;margin:0!important;'; $attrs['readonly'] = 'readonly'; } // aria-errormessage attribute is not allowed for hidden inputs. unset( $attrs['aria-errormessage'] ); $is_summary_enabled = $this->is_summary_enabled( $field ); // Prepare data for the order summary preview if summary is enabled, or we are on the editor page. if ( $is_summary_enabled || wpforms_is_editor_page() ) { [ $items, $foot, $total_width ] = $this->prepare_payment_fields_data( $form_data ); } if ( $is_summary_enabled ) { /** * Allow filtering form data before displaying the order summary table. * * @since 1.9.3 * * @param array $form_data Form data. * * @return array */ $form_data = apply_filters( 'wpforms_forms_fields_payment_total_field_display_form_data', $form_data ); // Summary preview. // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped echo wpforms_render( 'fields/total/summary-preview', [ 'items' => $items, 'foot' => $foot, 'total_width' => $total_width, ], true ); } $amount = wpforms_format_amount( 0, true ); // If we are on the editor page, we need to get the total amount from the last item in the foot. if ( ! empty( $foot ) && wpforms_is_editor_page() ) { $foot_item = end( $foot ); $amount = $foot_item['amount'] ?? 0; } // Always print total to cover a case when a field is embedded into a Layout column with 25% width. $hidden_style = $is_summary_enabled ? 'display:none' : ''; // This displays the total the user sees. printf( '<div class="wpforms-payment-total" style="%1$s">%2$s</div>', esc_attr( $hidden_style ), esc_html( $amount ) ); // Hidden input for processing. printf( '<input type="%s" %s>', esc_attr( $type ), wpforms_html_attributes( $primary['id'], $primary['class'], $primary['data'], $attrs ) // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ); } /** * Validate field on form submitting. * * @since 1.8.2 * * @param int $field_id Field ID. * @param string $field_submit Submitted field value (raw data). * @param array $form_data Form data and settings. */ public function validate( $field_id, $field_submit, $form_data ) { // Basic required check - If a field is marked as required, check for entry data. if ( ! empty( $form_data['fields'][ $field_id ]['required'] ) && ( empty( $field_submit ) || wpforms_sanitize_amount( $field_submit ) <= 0 ) ) { wpforms()->obj( 'process' )->errors[ $form_data['id'] ][ $field_id ] = esc_html__( 'Payment is required.', 'wpforms-lite' ); } } /** * Format and sanitize field. * * @since 1.8.2 * * @param int $field_id Field ID. * @param string $field_submit Field value submitted by a user. * @param array $form_data Form data and settings. */ public function format( $field_id, $field_submit, $form_data ) { // Define data. $name = ! empty( $form_data['fields'][ $field_id ]['label'] ) ? $form_data['fields'][ $field_id ]['label'] : ''; $amount = wpforms_sanitize_amount( $field_submit ); // Set final field details. wpforms()->obj( 'process' )->fields[ $field_id ] = [ 'name' => sanitize_text_field( $name ), 'value' => wpforms_format_amount( $amount, true ), 'amount' => wpforms_format_amount( $amount ), 'amount_raw' => $amount, 'id' => absint( $field_id ), 'type' => sanitize_key( $this->type ), ]; } /** * Summary option. * * @since 1.8.7 * * @param array $field Field data and settings. */ private function summary_option( array $field ): void { $is_allowed = RequirementsAlerts::is_order_summary_allowed(); $toggle_data = [ 'slug' => 'summary', 'value' => $this->is_summary_enabled( $field ), 'desc' => esc_html__( 'Enable Summary', 'wpforms-lite' ), 'tooltip' => esc_html__( 'Enable order summary for this field.', 'wpforms-lite' ), ]; if ( ! $is_allowed ) { $toggle_data['attrs'] = [ 'disabled' => 'disabled' ]; $toggle_data['control-class'] = 'wpforms-toggle-control-disabled'; } $output = $this->field_element( 'toggle', $field, $toggle_data, false ); $this->field_element( 'row', $field, [ 'slug' => 'summary', 'content' => $output, ] ); if ( ! $is_allowed ) { $this->field_element( 'row', $field, [ 'slug' => 'summary_alert', 'content' => RequirementsAlerts::get_order_summary_alert(), ] ); } } /** * Summary notice on the options' tab. * * @since 1.8.7 * * @param array $field Field data and settings. */ private function summary_option_notice( array $field ): void { $notice = __( 'Example data is shown in the form editor. Actual products and totals will be displayed when you preview or embed your form.', 'wpforms-lite' ); $is_notice_hidden = ! $this->is_summary_enabled( $field ) ? 'wpforms-hidden' : ''; printf( '<div class="wpforms-alert-info wpforms-alert wpforms-total-summary-alert %1$s"> <p>%2$s</p> </div>', esc_attr( $is_notice_hidden ), esc_html( $notice ) ); } /** * Determine if a summary option is enabled. * * @since 1.8.7 * * @param array $field Field data and settings. */ private function is_summary_enabled( array $field ) { return ! empty( $field['summary'] ); } /** * Prepare fake fields data for builder preview. * * @since 1.8.7 * * @return array */ private function prepare_builder_preview_data(): array { $items = [ [ 'label' => __( 'Example Product 1', 'wpforms-lite' ), 'quantity' => 3, 'amount' => wpforms_format_amount( 30, true ), 'is_hidden' => false, ], [ 'label' => __( 'Example Product 2', 'wpforms-lite' ), 'quantity' => 2, 'amount' => wpforms_format_amount( 20, true ), 'is_hidden' => false, ], [ 'label' => __( 'Example Product 3', 'wpforms-lite' ), 'quantity' => 1, 'amount' => wpforms_format_amount( 10, true ), 'is_hidden' => false, ], ]; $total = 60; /** * Allow filtering items in the footer on the order summary table (builder screen). * * @since 1.8.7 * * @param array $fields Order summary footer. * @param int $total Fields total. */ $foot = (array) apply_filters( 'wpforms_forms_fields_payment_total_field_builder_order_summary_preview_foot', [], $total ); /** * Allow filtering builder order summary fields total. * * @since 1.8.7 * * @param string $total Fields total. */ $total = apply_filters( 'wpforms_forms_fields_payment_total_field_builder_order_summary_preview_total', $total ); $total = wpforms_format_amount( $total, true ); $foot[] = [ 'label' => __( 'Total', 'wpforms-lite' ), 'quantity' => '', 'amount' => $total, 'class' => 'wpforms-order-summary-preview-total', ]; $total_width = strlen( html_entity_decode( $total, ENT_COMPAT, 'UTF-8' ) ) + 4; /** * Allow filtering builder order summary total column width. * * @since 1.8.7 * * @param int $total_width Total column width. */ $total_width = (int) apply_filters( 'wpforms_forms_fields_payment_total_field_builder_order_summary_preview_total_width', $total_width ); return [ $items, $foot, $total_width ]; } /** * Prepare payment fields data for summary preview. * * @since 1.8.7 * * @param array $form_data Form data. * * @return array */ private function prepare_payment_fields_data( array $form_data ): array { $payment_fields = wpforms_payment_fields(); $fields = []; $foot = []; $total = 0; foreach ( $form_data['fields'] as $field ) { if ( ( ! isset( $field['price'] ) && empty( $field['choices'] ) ) || ! in_array( $field['type'], $payment_fields, true ) ) { continue; } $this->prepare_payment_field_choices( $field, $fields, $total ); $this->prepare_payment_field_single( $field, $fields, $total ); } /** * Allow filtering items in the order summary footer. * * @since 1.8.7 * * @param array $fields Fields. */ $foot = (array) apply_filters( 'wpforms_forms_fields_payment_total_field_order_summary_preview_foot', $foot ); $total = wpforms_format_amount( $total, true ); $foot[] = [ 'label' => __( 'Total', 'wpforms-lite' ), 'quantity' => '', 'amount' => $total, 'class' => 'wpforms-order-summary-preview-total', ]; return [ $fields, $foot, strlen( html_entity_decode( $total, ENT_COMPAT, 'UTF-8' ) ) + 3 ]; } /** * Prepare payment single data for summary preview. * * @since 1.8.7 * * @param array $field Field data. * @param array $fields Fields data. * @param float $total Fields total. */ private function prepare_payment_field_single( array $field, array &$fields, float &$total ): void { if ( ! empty( $field['choices'] ) ) { return; } $quantity = $this->get_payment_field_min_quantity( $field ); $field_amount = $this->get_payment_field_single_amount( $field, $quantity ); $classes = [ 'wpforms-order-summary-field' ]; $format = $field['format'] ?? ''; $is_conditionally_hidden = $this->is_conditionally_hidden( $field ); if ( $format === 'hidden' ) { $classes[] = 'wpforms-hidden'; } $fields[] = [ 'label' => ! empty( $field['label_hide'] ) ? '' : $field['label'], 'quantity' => $quantity, 'amount' => wpforms_format_amount( $field_amount, true ), 'is_hidden' => ! $quantity || $is_conditionally_hidden, 'class' => $classes, 'data' => [ 'field' => $field['id'], ], ]; if ( ! $is_conditionally_hidden ) { $total += $field_amount; } } /** * Prepare payment field choices data for summary preview. * * @since 1.8.7 * * @param array $field Field data. * @param array $fields Fields data. * @param float $total Fields total. */ private function prepare_payment_field_choices( array $field, array &$fields, float &$total ): void { if ( empty( $field['choices'] ) ) { return; } $quantity = $this->get_payment_field_min_quantity( $field ); $default_choice_key = $this->get_classic_dropdown_default_choice_key( $field ); $is_conditionally_hidden = $this->is_conditionally_hidden( $field ); foreach ( $field['choices'] as $key => $choice ) { $choice_amount = ! empty( $choice['value'] ) ? wpforms_sanitize_amount( $choice['value'] ) * $quantity : 0; $is_default = ! empty( $choice['default'] ) || ( isset( $default_choice_key ) && (int) $key === $default_choice_key ); /* translators: %s - item number. */ $choice_label = ! empty( $choice['label'] ) ? $choice['label'] : sprintf( esc_html__( 'Item %s', 'wpforms-lite' ), $key ); $fields[] = [ 'label' => ! empty( $field['label_hide'] ) ? $choice_label : $field['label'] . ' - ' . $choice_label, 'quantity' => $quantity, 'amount' => wpforms_format_amount( $choice_amount, true ), 'is_hidden' => ! $is_default || ! $quantity || $is_conditionally_hidden, 'class' => 'wpforms-order-summary-field', 'data' => [ 'field' => $field['id'], 'choice' => $key, ], ]; if ( $is_default && ! $is_conditionally_hidden ) { $total += $choice_amount; } } } /** * The `array_key_first` polyfill. * * @since 1.9.3 * * @param array|mixed $arr Input array. * * @return int|string|null */ private function array_key_first( $arr ) { $array = (array) $arr; return empty( $array ) ? null : array_keys( $array )[0]; } /** * Get the classic dropdown default choice key. * * @since 1.8.7 * * @param array $field Field Settings. * * @return int|null */ private function get_classic_dropdown_default_choice_key( array $field ) { if ( $field['type'] !== 'payment-select' || $field['style'] !== 'classic' || ! empty( $field['placeholder'] ) ) { return null; } foreach ( $field['choices'] as $key => $choice ) { if ( ! isset( $choice['default'] ) ) { continue; } return (int) $key; } return $this->array_key_first( $field['choices'] ); } /** * Get payment field minimum quantity. * * @since 1.8.7 * * @param array $field Field data. * * @return int */ private function get_payment_field_min_quantity( array $field ): int { if ( ! wpforms_payment_has_quantity( $field, $this->form_data ) || ! isset( $field['min_quantity'] ) ) { return 1; } // Ensure non-negative quantity. return max( 0, (int) $field['min_quantity'] ); } /** * Add a class to the builder field preview. * * @since 1.8.7 * * @param string $css Class names. * @param array $field Field properties. * * @return string */ public function preview_field_class( $css, $field ) { if ( $field['type'] !== $this->type ) { return $css; } if ( $this->is_summary_enabled( $field ) ) { $css .= ' wpforms-summary-enabled'; } return $css; } /** * Add an order summary to the confirmation settings. * * @since 1.8.7 * * @param WPForms_Builder_Panel_Settings $settings Settings. * @param int $field_id Field ID. */ public function add_confirmation_setting( $settings, int $field_id ): void { wpforms_panel_field( 'toggle', 'confirmations', 'message_order_summary', $settings->form_data, esc_html__( 'Show order summary after confirmation message', 'wpforms-lite' ), [ 'input_id' => 'wpforms-panel-field-confirmations-message_order_summary-' . $field_id, 'input_class' => 'wpforms-panel-field-confirmations-message_order_summary', 'parent' => 'settings', 'subsection' => $field_id, ] ); } /** * Show the order summary on the confirmation page. * * @since 1.8.7 * * @param array $confirmation Current confirmation data. * @param array $form_data Form data and settings. * @param array $fields Sanitized field data. * @param int $entry_id Entry id. */ public function order_summary_confirmation( array $confirmation, array $form_data, array $fields, int $entry_id ): void { if ( empty( $confirmation['message_order_summary'] ) ) { return; } $total_exists = false; foreach ( $fields as $field ) { if ( $field['type'] !== $this->type ) { continue; } $total_exists = true; break; } // Check if the total field exists on the form. if ( ! $total_exists ) { return; } echo '<div class="wpforms-confirmation-container-order-summary">'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped echo wpforms_process_smart_tags( '{order_summary}', $form_data, $fields, $entry_id, 'payment-total-order-summary-confirmation' ); echo '</div>'; } /** * Calculates the total amount for a single payment field based on its price and quantity. * * @since 1.9.5 * * @param array $field The payment field data containing the price. * @param int $quantity The quantity of the field specified. * * @return float|int The calculated total amount for the payment field. */ private function get_payment_field_single_amount( array $field, int $quantity ) { if ( empty( $field['price'] ) ) { return 0; } return wpforms_sanitize_amount( $field['price'] ) * $quantity; } /** * Determines if a field is conditionally hidden based on its settings and conditions. * * Note: This is a simplified implementation that assumes fields with 'show' conditional * logic are hidden by default, without evaluating the actual conditions. This approach * was chosen to avoid complex condition evaluation during form rendering. * * @since 1.9.5 * * @param array $field Field data, including conditional logic settings. * * @return bool True if the field is conditionally hidden, false otherwise. */ private function is_conditionally_hidden( array $field ): bool { return wpforms()->is_pro() && wpforms_conditional_logic_fields()->field_is_conditional( $field ) && ( $field['conditional_type'] ?? '' ) === 'show'; } } Fields/FileUpload/Field.php 0000644 00000030464 15252506741 0011550 0 ustar 00 <?php namespace WPForms\Forms\Fields\FileUpload; use WPForms\Forms\Fields\Traits\ProField as ProFieldTrait; use WPForms\Forms\Fields\Traits\CameraTrait; use WPForms\Forms\Fields\Traits\AccessRestrictionsTrait; use WPForms_Field; /** * File upload field. * * @since 1.9.4 */ class Field extends WPForms_Field { use ProFieldTrait; use CameraTrait; use AccessRestrictionsTrait; /** * Classic (old) style of the file uploader field. * * @since 1.9.4 * * @var string */ public const STYLE_CLASSIC = 'classic'; /** * Modern style of the file uploader field. * * @since 1.9.4 * * @var string */ public const STYLE_MODERN = 'modern'; /** * Maximum file number. * * @since 1.9.4 * * @var int */ private const MAX_FILE_NUM = 100; /** * Replaceable (either in PHP or JS) template for a maximum file number. * * @since 1.9.4 * * @var string */ protected const TEMPLATE_MAXFILENUM = '{maxFileNumber}'; /** * Primary class constructor. * * @since 1.9.4 */ public function init() { // Define field type information. $this->name = esc_html__( 'File Upload', 'wpforms-lite' ); $this->type = 'file-upload'; $this->icon = 'fa-upload'; $this->order = 100; $this->group = 'fancy'; $this->default_settings = [ 'style' => self::STYLE_MODERN, ]; $this->init_pro_field(); } /** * Field options panel inside the builder. * * @since 1.9.4 * * @param array $field Field data and settings. * * @noinspection HtmlUnknownTarget */ public function field_options( $field ) { $style = ! empty( $field['style'] ) ? $field['style'] : self::STYLE_MODERN; /* * Basic field options. */ // Options open markup. $this->field_option( 'basic-options', $field, [ 'markup' => 'open', 'after_title' => $this->get_field_options_notice(), ] ); // Label. $this->field_option( 'label', $field ); // Description. $this->field_option( 'description', $field ); // Allowed extensions. $lbl = $this->field_element( 'label', $field, [ 'slug' => 'extensions', 'value' => esc_html__( 'Allowed File Extensions', 'wpforms-lite' ), 'tooltip' => esc_html__( 'Enter the extensions you would like to allow, comma separated.', 'wpforms-lite' ), 'after_tooltip' => sprintf( '<a href="%1$s" class="after-label-description" target="_blank" rel="noopener noreferrer">%2$s</a>', esc_url( wpforms_utm_link( 'https://wpforms.com/docs/a-complete-guide-to-the-file-upload-field/#file-types', 'Field Options', 'File Upload Extensions Documentation' ) ), esc_html__( 'See More Details', 'wpforms-lite' ) ), ], false ); $fld = $this->field_element( 'text', $field, [ 'slug' => 'extensions', 'value' => ! empty( $field['extensions'] ) ? $field['extensions'] : '', ], false ); $this->field_element( 'row', $field, [ 'slug' => 'extensions', 'content' => $lbl . $fld, ] ); // Max file size. $lbl = $this->field_element( 'label', $field, [ 'slug' => 'max_size', 'value' => esc_html__( 'Max File Size', 'wpforms-lite' ), 'tooltip' => sprintf( /* translators: %s - max upload size. */ esc_html__( 'Enter the max size of each file, in megabytes, to allow. If left blank, the value defaults to the maximum size the server allows which is %s.', 'wpforms-lite' ), wpforms_max_upload() ), ], false ); $fld = $this->field_element( 'text', $field, [ 'slug' => 'max_size', 'type' => 'number', 'attrs' => [ 'min' => 1, 'max' => 512, 'step' => 1, 'pattern' => '[0-9]', ], 'value' => ! empty( $field['max_size'] ) ? abs( $field['max_size'] ) : '', ], false ); $this->field_element( 'row', $field, [ 'slug' => 'max_size', 'content' => $lbl . $fld, ] ); // Max file number. $lbl = $this->field_element( 'label', $field, [ 'slug' => 'max_file_number', 'value' => esc_html__( 'Max File Uploads', 'wpforms-lite' ), 'tooltip' => esc_html__( 'Enter the max number of files to allow. If left blank, the value defaults to 1.', 'wpforms-lite' ), ], false ); $fld = $this->field_element( 'text', $field, [ 'slug' => 'max_file_number', 'type' => 'number', 'attrs' => [ 'min' => 1, 'max' => self::MAX_FILE_NUM, 'step' => 1, 'pattern' => '[0-9]', ], 'value' => $this->get_max_file_number( $field ), ], false ); $this->field_element( 'row', $field, [ 'slug' => 'max_file_number', 'content' => $lbl . $fld, 'class' => $style === self::STYLE_CLASSIC ? 'wpforms-hidden' : '', ] ); // Required toggle. $this->field_option( 'required', $field ); // Options close markup. $this->field_option( 'basic-options', $field, [ 'markup' => 'close' ] ); /* * Advanced field options. */ // Options open markup. $this->field_option( 'advanced-options', $field, [ 'markup' => 'open' ] ); // Style. $lbl = $this->field_element( 'label', $field, [ 'slug' => 'style', 'value' => esc_html__( 'Style', 'wpforms-lite' ), 'tooltip' => esc_html__( 'Modern Style supports multiple file uploads, displays a drag-and-drop upload box, and uses AJAX. Classic Style supports single file upload and displays a traditional upload button.', 'wpforms-lite' ), ], false ); $fld = $this->field_element( 'select', $field, [ 'slug' => 'style', 'value' => $style, 'options' => [ self::STYLE_MODERN => esc_html__( 'Modern', 'wpforms-lite' ), self::STYLE_CLASSIC => esc_html__( 'Classic', 'wpforms-lite' ), ], ], false ); $this->field_element( 'row', $field, [ 'slug' => 'style', 'content' => $lbl . $fld, ] ); // Custom CSS classes. $this->field_option( 'css', $field ); // Media Library toggle. $fld = $this->field_element( 'toggle', $field, [ 'slug' => 'media_library', 'value' => ! empty( $field['media_library'] ) ? 1 : '', 'desc' => esc_html__( 'Store Files in WordPress Media Library', 'wpforms-lite' ), 'tooltip' => esc_html__( 'Check this option to store the final uploaded file in the WordPress Media Library', 'wpforms-lite' ), 'class' => 'wpforms-file-upload-media-library', ], false ); $this->field_element( 'row', $field, [ 'slug' => 'media_library', 'content' => $fld, ] ); // Access Restrictions. $this->access_restrictions_options( $field ); // Camera. $this->camera_options( $field ); // Hide Label. $this->field_option( 'label_hide', $field ); // Options close markup. $this->field_option( 'advanced-options', $field, [ 'markup' => 'close', ] ); } /** * Field preview panel inside the builder. * * @since 1.9.4 * * @param array $field Field data. */ public function field_preview( $field ) { // Label. $this->field_preview_option( 'label', $field, [ 'label_badge' => $this->get_field_preview_badge(), ] ); $modern_classes = [ 'wpforms-file-upload-builder-modern' ]; $classic_classes = [ 'wpforms-file-upload-builder-classic' ]; if ( empty( $field['style'] ) || $field['style'] !== self::STYLE_CLASSIC ) { $classic_classes[] = 'wpforms-hide'; } else { $modern_classes[] = 'wpforms-hide'; } $strings = $this->get_strings(); $max_file_number = $this->get_max_file_number( $field ); /** * Filter the classic camera text. * * @since 1.9.8 * * @param string $classic_camera The classic camera text. */ $classic_camera_text = (string) apply_filters( 'wpforms_forms_fields_file_upload_field_classic_camera_text', esc_html__( 'Capture With Your Camera', 'wpforms-lite' ) ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped echo wpforms_render( 'fields/file-upload/file-upload-backend', [ 'max_file_number' => $max_file_number, 'preview_hint' => str_replace( self::TEMPLATE_MAXFILENUM, $max_file_number, $strings['preview_hint'] ), 'modern_classes' => implode( ' ', $modern_classes ), 'classic_classes' => implode( ' ', $classic_classes ), 'is_camera' => ! empty( $field['camera_enabled'] ) ? 1 : '', 'classic_camera' => $classic_camera_text, ], true ); // Description. $this->field_preview_option( 'description', $field ); } /** * File Uploads specific strings. * * @since 1.9.4 * * @return array Field-specific strings. */ public function get_strings(): array { return [ 'preview_title_single' => sprintf( /* translators: %1$s: Choose File to Upload opening tag, %2$s: Choose File to Upload closing tag. */ esc_html__( 'Drag & Drop File or %1$sChoose File to Upload%2$s', 'wpforms-lite' ), '<span class="wpforms-file-upload-choose-file">', '</span>' ), 'preview_title_plural' => sprintf( /* translators: %1$s: Choose Files to Upload opening tag, %2$s: Choose Files to Upload closing tag. */ esc_html__( 'Drag & Drop Files or %1$sChoose Files to Upload%2$s', 'wpforms-lite' ), '<span class="wpforms-file-upload-choose-file">', '</span>' ), 'preview_title_single_camera' => sprintf( /* translators: %1$s: Choose File to Upload opening tag, %2$s: Closing tag, %3$s: Capture With Camera opening tag. */ esc_html__( 'Drag & Drop File, %1$sChoose File to Upload%2$s, or %3$sCapture With Camera%2$s', 'wpforms-lite' ), '<span class="wpforms-file-upload-choose-file">', '</span>', '<span class="wpforms-file-upload-capture-camera">' ), 'preview_title_plural_camera' => sprintf( /* translators: %1$s: Choose Files to Upload opening tag, %2$s: Closing tag, %3$s: Capture With Camera opening tag. */ esc_html__( 'Drag & Drop Files, %1$sChoose Files to Upload%2$s, or %3$sCapture With Camera%2$s', 'wpforms-lite' ), '<span class="wpforms-file-upload-choose-file">', '</span>', '<span class="wpforms-file-upload-capture-camera">' ), 'preview_hint' => sprintf( /* translators: % - max number of files as a template string (not a number), replaced by a number later. */ esc_html__( 'You can upload up to %s files.', 'wpforms-lite' ), self::TEMPLATE_MAXFILENUM ), 'password_match_error_title' => esc_html__( 'Passwords Do Not Match', 'wpforms-lite' ), 'password_match_error_text' => esc_html__( 'Please check the password for the following fields: {fields}', 'wpforms-lite' ), 'password_empty_error_title' => esc_html__( 'Passwords Are Empty', 'wpforms-lite' ), 'password_empty_error_text' => esc_html__( 'Please enter a password for the following fields: {fields}', 'wpforms-lite' ), 'notification_warning_title' => esc_html__( 'Cannot Enable Restrictions', 'wpforms-lite' ), 'notification_warning_text' => esc_html__( 'This field is attached to Notifications. In order to enable restrictions, please first remove it from File Upload Attachments in Notifications.', 'wpforms-lite' ), 'notification_error_title' => esc_html__( 'Cannot Enable Attachments', 'wpforms-lite' ), 'notification_error_text' => esc_html__( 'The following fields ({fields}) cannot be attached to notifications because restrictions are enabled for them.', 'wpforms-lite' ), 'all_user_roles_selected' => esc_html__( 'All User Roles already selected', 'wpforms-lite' ), 'incompatible_addon_text' => esc_html__( 'File Upload Restrictions can\'t be enabled because the current version of the Post Submissions addon is incompatible.', 'wpforms-lite' ), ]; } /** * Getting max file number. * * @since 1.9.4 * * @param array $field Field data. * * @return int * @noinspection PhpMissingParamTypeInspection */ protected function get_max_file_number( $field ): int { if ( empty( $field['max_file_number'] ) ) { return 1; } $max_file_number = absint( $field['max_file_number'] ); if ( $max_file_number < 1 ) { return 1; } if ( $max_file_number > self::MAX_FILE_NUM ) { return self::MAX_FILE_NUM; } return $max_file_number; } /** * Field display on the form front-end. * * @since 1.9.4 * * @param array $field Field data and settings. * @param array $deprecated Deprecated field attributes. Use field properties. * @param array $form_data Form data and settings. */ public function field_display( $field, $deprecated, $form_data ) { } } Fields/Phone/Field.php 0000644 00000012106 15252506741 0010566 0 ustar 00 <?php namespace WPForms\Forms\Fields\Phone; use WPForms\Forms\Fields\Traits\ProField as ProFieldTrait; use WPForms_Field; /** * Phone number field. * * @since 1.9.4 */ class Field extends WPForms_Field { use ProFieldTrait; /** * International Telephone Input library CSS. * * @since 1.9.4 */ public const INTL_VERSION = '28.0.4'; /** * Primary class constructor. * * @since 1.9.4 */ public function init() { // Define field type information. $this->name = esc_html__( 'Phone', 'wpforms-lite' ); $this->keywords = esc_html__( 'telephone, mobile, cell', 'wpforms-lite' ); $this->type = 'phone'; $this->icon = 'fa-phone'; $this->order = 50; $this->group = 'fancy'; $this->default_settings = [ 'format' => 'smart', ]; $this->init_pro_field(); $this->hooks(); } /** * Hooks. * * @since 1.9.4 */ protected function hooks() { } /** * Field options panel inside the builder. * * @since 1.9.4 * * @param array $field Field data. */ public function field_options( $field ) { /** * Basic field options. */ // Options open markup. $this->field_option( 'basic-options', $field, [ 'markup' => 'open', 'after_title' => $this->get_field_options_notice(), ] ); // Label. $this->field_option( 'label', $field ); // Format. $lbl = $this->field_element( 'label', $field, [ 'slug' => 'format', 'value' => esc_html__( 'Format', 'wpforms-lite' ), 'tooltip' => esc_html__( 'Select format for the phone form field', 'wpforms-lite' ), ], false ); $fld = $this->field_element( 'select', $field, [ 'slug' => 'format', 'value' => ! empty( $field['format'] ) ? esc_attr( $field['format'] ) : 'smart', 'options' => [ 'smart' => esc_html__( 'Smart', 'wpforms-lite' ), 'us' => esc_html__( 'US', 'wpforms-lite' ), 'international' => esc_html__( 'International', 'wpforms-lite' ), ], ], false ); $args = [ 'slug' => 'format', 'content' => $lbl . $fld, ]; $this->field_element( 'row', $field, $args ); // Description. $this->field_option( 'description', $field ); // Required toggle. $this->field_option( 'required', $field ); // Options close markup. $args = [ 'markup' => 'close', ]; $this->field_option( 'basic-options', $field, $args ); /* * Advanced field options. */ // Options open markup. $args = [ 'markup' => 'open', ]; $this->field_option( 'advanced-options', $field, $args ); // Size. $this->field_option( 'size', $field ); // Placeholder. $this->field_option( 'placeholder', $field ); // Default value. $this->field_option( 'default_value', $field ); // Custom CSS classes. $this->field_option( 'css', $field ); // Hide Label. $this->field_option( 'label_hide', $field ); // Options close markup. $args = [ 'markup' => 'close', ]; $this->field_option( 'advanced-options', $field, $args ); } /** * Field preview inside the builder. * * @since 1.9.4 * * @param array $field Field data. */ public function field_preview( $field ) { // Define data. $placeholder = ! empty( $field['placeholder'] ) ? $field['placeholder'] : ''; $default_value = ! empty( $field['default_value'] ) ? $field['default_value'] : ''; $format = ! empty( $field['format'] ) ? $field['format'] : 'smart'; $size = ! empty( $field['size'] ) ? $field['size'] : 'medium'; // Label. $this->field_preview_option( 'label', $field, [ 'label_badge' => $this->get_field_preview_badge(), ] ); // Primary input inside container for Smart format preview. printf( '<div class="wpforms-field-phone-input-container" data-format="%1$s"> <input type="text" placeholder="%2$s" value="%3$s" class="primary-input wpforms-field-%4$s" readonly> <div class="wpforms-field-phone-country-container"> <div class="wpforms-field-phone-flag"></div> <div class="wpforms-field-phone-arrow"></div> </div> </div>', esc_attr( $format ), esc_attr( $placeholder ), esc_attr( $default_value ), esc_attr( $size ) ); // Description. $this->field_preview_option( 'description', $field ); } /** * Get a preview option. * * @since 1.9.4 * * @param string $option Option name. * @param array $field Field data. * @param array $args Additional arguments. * @param bool $do_echo Echo or return. */ public function field_preview_option( $option, $field, $args = [], $do_echo = true ) { // phpcs:ignore Universal.NamingConventions.NoReservedKeywordParameterNames.echoFound // Skip preview option for the editor. if ( wpforms_is_editor_page() ) { return; } parent::field_preview_option( $option, $field, $args, $do_echo ); } /** * Field display on the form front-end. * * @since 1.9.4 * * @param array $field Field data and settings. * @param array $deprecated Deprecated field attributes. Use field properties. * @param array $form_data Form data and settings. */ public function field_display( $field, $deprecated, $form_data ) { } } Fields/Content/Field.php 0000644 00000005453 15252506741 0011136 0 ustar 00 <?php namespace WPForms\Forms\Fields\Content; use WPForms\Forms\Fields\Traits\ProField as ProFieldTrait; use WPForms\Forms\Fields\Traits\ContentInput; use WPForms_Field; /** * The Content Field Class. * * @since 1.9.4 */ class Field extends WPForms_Field { use ProFieldTrait; use ContentInput; /** * Class initialization method. * * @since 1.9.4 */ public function init() { // Define field type information. $this->name = esc_html__( 'Content', 'wpforms-lite' ); $this->keywords = esc_html__( 'image, text, table, list, heading, wysiwyg, visual', 'wpforms-lite' ); $this->type = 'content'; $this->icon = 'fa-file-image-o'; $this->order = 180; $this->group = 'fancy'; $this->allow_read_only = false; $this->default_settings = [ 'label_disable' => '1', ]; $this->init_pro_field(); $this->hooks(); } /** * Register WP hooks. * * @since 1.9.4 */ protected function hooks() { } /** * Show field options in the builder left panel. * * @since 1.9.4 * * @param array $field Field data. */ public function field_options( $field ) { // Options open markup. $this->field_option( 'basic-options', $field, [ 'markup' => 'open', 'after_title' => $this->get_field_options_notice(), ] ); $this->field_option_content( $field ); // Set label to the disabled. $args = [ 'type' => 'hidden', 'slug' => 'label_disable', 'value' => '1', ]; $this->field_element( 'text', $field, $args ); // Options close markup. $this->field_option( 'basic-options', $field, [ 'markup' => 'close' ] ); // Options open markup. $this->field_option( 'advanced-options', $field, [ 'markup' => 'open' ] ); // Size. $this->field_option( 'size', $field ); // Custom CSS classes. $this->field_option( 'css', $field ); // Options close markup. $this->field_option( 'advanced-options', $field, [ 'markup' => 'close' ] ); } /** * Show the field preview in the builder right panel. * * @since 1.9.4 * * @param array $field Field data. */ public function field_preview( $field ) { if ( ! empty( $this->is_disabled_field ) ) { // Label. $field['label'] = empty( $field['label'] ) ? esc_html__( 'Content', 'wpforms-lite' ) : $field['label']; $this->field_preview_option( 'label', $field, [ 'label_badge' => $this->get_field_preview_badge(), ] ); } $this->content_input_preview( $field ); } /** * Field display on the form front-end. * * @since 1.9.4 * * @param array $field Field data and settings. * @param array $deprecated Deprecated field attributes. Use field properties instead. * @param array $form_data Form data and settings. */ public function field_display( $field, $deprecated, $form_data ) { } } Fields/Camera/Field.php 0000644 00000030266 15252506741 0010714 0 ustar 00 <?php namespace WPForms\Forms\Fields\Camera; use WPForms_Field; use WPForms\Forms\Fields\Traits\CameraTrait; use WPForms\Forms\Fields\Traits\ProField as ProFieldTrait; use WPForms\Forms\Fields\Traits\AccessRestrictionsTrait; /** * Camera field. * * @since 1.9.8 */ class Field extends WPForms_Field { use ProFieldTrait; use CameraTrait; use AccessRestrictionsTrait; protected const STYLE_BUTTON = 'button'; protected const STYLE_LINK = 'link'; public const STYLE_CLASSIC = 'classic'; public const STYLE_MODERN = 'modern'; /** * Primary class constructor. * * @since 1.9.8 */ public function init() { // Define field type information. $this->name = esc_html__( 'Camera', 'wpforms-lite' ); $this->keywords = esc_html__( 'photo, image, capture, webcam', 'wpforms-lite' ); $this->type = 'camera'; $this->icon = 'fa-camera'; $this->order = 105; $this->group = 'fancy'; $this->default_settings = [ 'style' => 'button', ]; $this->init_pro_field(); $this->hooks(); } /** * Add hooks. * * @since 1.9.8 */ private function hooks(): void { add_action( 'wpforms_builder_enqueues', [ $this, 'builder_enqueues' ] ); } /** * Enqueue script for the admin form builder. * * @since 1.9.8 */ public function builder_enqueues(): void { $min = wpforms_get_min_suffix(); if ( ! wpforms_is_pro() ) { return; } wp_enqueue_script( 'wpforms-builder-file-upload-field', WPFORMS_PLUGIN_URL . "assets/pro/js/admin/builder/fields/file-upload{$min}.js", [ 'jquery', 'wpforms-builder' ], WPFORMS_VERSION, false ); wp_enqueue_script( 'wpforms-builder-camera', WPFORMS_PLUGIN_URL . "assets/pro/js/admin/builder/fields/camera{$min}.js", [ 'jquery', 'wpforms-builder' ], WPFORMS_VERSION, false ); // Localize strings for the camera field. wp_localize_script( 'wpforms-builder-camera', 'wpforms_camera_builder', [ 'button_link_text_label' => esc_html__( 'Button Link Text', 'wpforms-lite' ), 'link_text_label' => esc_html__( 'Link Text', 'wpforms-lite' ), 'button_link_text_tooltip' => esc_html__( 'Enter the text for the button link.', 'wpforms-lite' ), 'link_text_tooltip' => esc_html__( 'Enter the text for the link.', 'wpforms-lite' ), 'error_message' => esc_html__( 'Camera field with Link style cannot have empty Link Text. Please enter text or change style to Button.', 'wpforms-lite' ), 'error_title' => esc_html__( 'Missing Link Text', 'wpforms-lite' ), 'error_ok' => esc_html__( 'OK', 'wpforms-lite' ), ] ); } /** * Field options panel inside the builder. * * @since 1.9.8 * * @param array $field Field data. */ public function field_options( $field ) { /* * Basic field options. */ // Options open markup. $this->field_option( 'basic-options', $field, [ 'markup' => 'open', 'after_title' => $this->get_field_options_notice(), ] ); // Label. $this->field_option( 'label', $field ); // Description. $this->field_option( 'description', $field ); // Camera options. $this->add_camera_enabled_toggle( $field ); $this->add_camera_format_options( $field ); $this->add_camera_aspect_ratio_options( $field ); $this->add_camera_custom_ratio_options( $field ); $this->add_camera_time_limit_options( $field ); // Max file size. $this->add_max_file_size_options( $field ); // Required toggle. $this->field_option( 'required', $field ); // Options close markup. $this->field_option( 'basic-options', $field, [ 'markup' => 'close' ] ); // Advanced field options. $this->field_option( 'advanced-options', $field, [ 'markup' => 'open' ] ); // Style (Button or Link). $this->add_style_options( $field ); // Button link text. $this->add_button_link_text_options( $field ); // Custom CSS classes. $this->field_option( 'css', $field ); // Media Library toggle. $fld = $this->field_element( 'toggle', $field, [ 'slug' => 'media_library', 'value' => ! empty( $field['media_library'] ) ? 1 : '', 'desc' => esc_html__( 'Store Files in WordPress Media Library', 'wpforms-lite' ), 'tooltip' => esc_html__( 'Check this option to store the final uploaded file in the WordPress Media Library', 'wpforms-lite' ), 'class' => 'wpforms-camera-media-library', ], false ); $this->field_element( 'row', $field, [ 'slug' => 'media_library', 'content' => $fld, ] ); // Access Restrictions. $this->access_restrictions_options( $field ); // Hide label. $this->field_option( 'label_hide', $field ); // Options close markup. $this->field_option( 'advanced-options', $field, [ 'markup' => 'close' ] ); } /** * Field preview inside the builder. * * @since 1.9.8 * * @param array $field Field data. */ public function field_preview( $field ) { // Label. $this->field_preview_option( 'label', $field, [ 'label_badge' => $this->get_field_preview_badge(), ] ); $style = ! empty( $field['style'] ) ? $field['style'] : self::STYLE_BUTTON; $field_id = absint( $field['id'] ); $text = $field['button_link_text'] ?? esc_html__( 'Capture With Your Camera', 'wpforms-lite' ); // Always render both button and link, but hide/show based on the selected style. $button_class = $style === self::STYLE_BUTTON ? 'wpforms-camera-button wpforms-btn-secondary' : 'wpforms-camera-button wpforms-btn-secondary wpforms-hidden'; $link_class = $style === self::STYLE_LINK ? 'wpforms-camera-link' : 'wpforms-camera-link wpforms-hidden'; printf( '<button type="button" class="%s" id="%d">%s %s</button>', esc_attr( $button_class ), (int) $field_id, $this->get_camera_icon_svg(), // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped esc_html( $text ) ); printf( '<a href="#" class="%s" data-field-id="%d">%s</a>', esc_attr( $link_class ), (int) $field_id, esc_html( $text ) ); // Description. $this->field_preview_option( 'description', $field ); } /** * Field display on the form front-end. * * @since 1.9.8 * * @param array $field Field data and settings. * @param array $deprecated Deprecated field attributes. Use field properties. * @param array $form_data Form data and settings. */ public function field_display( $field, $deprecated, $form_data ) { // Implemented in Pro only. } /** * Add max file size options. * * @since 1.9.8 * * @param array $field Field data. */ private function add_max_file_size_options( array $field ): void { $lbl = $this->field_element( 'label', $field, [ 'slug' => 'max_size', 'value' => esc_html__( 'Max File Size', 'wpforms-lite' ), 'tooltip' => sprintf( /* translators: %s - max upload size. */ esc_html__( 'Enter the max size of each file, in megabytes, to allow. If left blank, the value defaults to the maximum size the server allows which is %s.', 'wpforms-lite' ), wpforms_max_upload() ), ], false ); $fld = $this->field_element( 'text', $field, [ 'slug' => 'max_size', 'type' => 'number', 'attrs' => [ 'min' => 1, 'max' => 512, 'step' => 1, 'pattern' => '[0-9]', ], 'value' => ! empty( $field['max_size'] ) ? abs( $field['max_size'] ) : '', ], false ); $this->field_element( 'row', $field, [ 'slug' => 'max_size', 'content' => $lbl . $fld, ] ); } /** * Add style options, Button or Link. * * @since 1.9.8 * * @param array $field Field data. */ private function add_style_options( array $field ): void { // Style (Button or Link). $lbl = $this->field_element( 'label', $field, [ 'slug' => 'style', 'value' => esc_html__( 'Style', 'wpforms-lite' ), 'tooltip' => esc_html__( 'Choose the style of the camera button.', 'wpforms-lite' ), ], false ); $fld = $this->field_element( 'select', $field, [ 'slug' => 'style', 'value' => ! empty( $field['style'] ) ? $field['style'] : self::STYLE_BUTTON, 'options' => [ self::STYLE_BUTTON => esc_html__( 'Button', 'wpforms-lite' ), self::STYLE_LINK => esc_html__( 'Link', 'wpforms-lite' ), ], ], false ); $this->field_element( 'row', $field, [ 'slug' => 'style', 'content' => $lbl . $fld, 'class' => 'wpforms-camera-style', ] ); } /** * Add button link text options. * * @since 1.9.8 * * @param array $field Field data. */ private function add_button_link_text_options( array $field ): void { $style = ! empty( $field['style'] ) ? $field['style'] : self::STYLE_BUTTON; // Button link text. $lbl = $this->field_element( 'label', $field, [ 'slug' => 'button_link_text', 'value' => $style === self::STYLE_BUTTON ? esc_html__( 'Button Link Text', 'wpforms-lite' ) : esc_html__( 'Link Text', 'wpforms-lite' ), 'tooltip' => $style === self::STYLE_BUTTON ? esc_html__( 'Enter the text for the button link.', 'wpforms-lite' ) : esc_html__( 'Enter the text for the link.', 'wpforms-lite' ), ], false ); $fld = $this->field_element( 'text', $field, [ 'slug' => 'button_link_text', 'value' => $field['button_link_text'] ?? esc_html__( 'Capture With Your Camera', 'wpforms-lite' ), ], false ); $this->field_element( 'row', $field, [ 'slug' => 'button_link_text', 'content' => $lbl . $fld, ] ); } /** * Get camera icon SVG. * * @since 1.9.8 * * @return string Camera icon SVG code. * @noinspection HtmlDeprecatedAttribute */ protected function get_camera_icon_svg(): string { return '<svg width="16" height="14" viewBox="0 0 16 14" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M4.65625 1.03125C4.875 0.40625 5.4375 0 6.09375 0H9.90625C10.5625 0 11.125 0.40625 11.3438 1.03125L11.6562 2H14C15.0938 2 16 2.90625 16 4V12C16 13.0938 15.0938 14 14 14H2C0.90625 14 0 13.0938 0 12V4C0 2.90625 0.90625 2 2 2H4.34375L4.65625 1.03125ZM8 5C6.34375 5 5 6.34375 5 8C5 9.65625 6.34375 11 8 11C9.65625 11 11 9.65625 11 8C11 6.34375 9.65625 5 8 5Z"/></svg>'; } /** * Get remove selected file icon SVG. * * @since 1.9.8 * * @return string Remove icon SVG code. * @noinspection HtmlDeprecatedAttribute */ protected function get_camera_remove_file_icon(): string { return '<svg width="13" height="15" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M4.121.914a.853.853 0 0 1 .82-.602H8.06c.382 0 .71.247.82.602l.246.711h2.625c.492 0 .875.383.875.875a.864.864 0 0 1-.875.875H1.25A.864.864 0 0 1 .375 2.5c0-.492.383-.875.875-.875h2.625l.246-.71Zm7.629 3.774-.574 8.832c-.055.683-.63 1.23-1.313 1.23H3.137c-.684 0-1.258-.547-1.313-1.23L1.25 4.688h10.5Z"/></svg>'; } /** * Check if the field is modern upload style. * * @since 1.9.8 * * @param array $field_data Field data. * * @return bool */ public static function is_modern_upload( $field_data ): bool { return isset( $field_data['style'] ) && $field_data['style'] === self::STYLE_MODERN; } /** * Format field value for display in Entries. * * @since 1.9.8 * * @param int $field_id Field ID. * @param mixed $field_submit Field value that was submitted. * @param array $form_data Form data and settings. */ public function format( $field_id, $field_submit, $form_data ) { $field_id = absint( $field_id ); $field_label = ! empty( $form_data['fields'][ $field_id ]['label'] ) ? sanitize_text_field( $form_data['fields'][ $field_id ]['label'] ) : ''; $style = ! empty( $form_data['fields'][ $field_id ]['style'] ) && $form_data['fields'][ $field_id ]['style'] === self::STYLE_MODERN ? self::STYLE_MODERN : self::STYLE_CLASSIC; if ( $style === self::STYLE_CLASSIC ) { wpforms()->obj( 'process' )->fields[ $field_id ] = [ 'name' => $field_label, 'value' => '', 'file' => '', 'file_original' => '', 'ext' => '', 'id' => $field_id, 'type' => $this->type, ]; return; } wpforms()->obj( 'process' )->fields[ $field_id ] = [ 'name' => $field_label, 'value' => '', 'value_raw' => '', 'id' => $field_id, 'type' => $this->type, 'style' => self::STYLE_MODERN, ]; } } Fields/Base/Frontend.php 0000644 00000001273 15252506741 0011126 0 ustar 00 <?php namespace WPForms\Forms\Fields\Base; use WPForms_Field; /** * Field's Frontend base class. * * @since 1.8.1 */ class Frontend { /** * Instance of the main WPForms_Field_{something} class. * * @since 1.8.1 * * @var WPForms_Field */ protected $field_obj; /** * Class constructor. * * @since 1.8.1 * * @param WPForms_Field $field_obj Instance of the WPForms_Field_{something} class. */ public function __construct( $field_obj ) { $this->field_obj = $field_obj; $this->init(); } /** * Initialize. * * @since 1.8.1 */ public function init() { $this->hooks(); } /** * Hooks. * * @since 1.8.1 */ protected function hooks() { } } Fields/Password/Field.php 0000644 00000023766 15252506741 0011335 0 ustar 00 <?php namespace WPForms\Forms\Fields\Password; use WPForms\Forms\Fields\Traits\ProField as ProFieldTrait; use WPForms_Field; /** * Password field. * * @since 1.9.4 */ class Field extends WPForms_Field { use ProFieldTrait; /** * Primary class constructor. * * @since 1.9.4 */ public function init() { // Define field type information. $this->name = esc_html__( 'Password', 'wpforms-lite' ); $this->keywords = esc_html__( 'user', 'wpforms-lite' ); $this->type = 'password'; $this->icon = 'fa-lock'; $this->order = 95; $this->group = 'fancy'; $this->init_pro_field(); $this->hooks(); } /** * Hooks. * * @since 1.9.4 */ protected function hooks() { } /** * Field options panel inside the builder. * * @since 1.9.4 * * @param array $field Field data. * * @noinspection PackedHashtableOptimizationInspection */ public function field_options( $field ) { /* * Basic field options. */ // Options open markup. $this->field_option( 'basic-options', $field, [ 'markup' => 'open', 'after_title' => $this->get_field_options_notice(), ] ); // Label. $this->field_option( 'label', $field ); // Description. $this->field_option( 'description', $field ); // Required toggle. $this->field_option( 'required', $field ); // Confirmation toggle. $fld = $this->field_element( 'toggle', $field, [ 'slug' => 'confirmation', 'value' => isset( $field['confirmation'] ) ? '1' : '0', 'desc' => esc_html__( 'Enable Password Confirmation', 'wpforms-lite' ), 'tooltip' => esc_html__( 'Check this option to ask users to provide their password twice.', 'wpforms-lite' ), ], false ); $args = [ 'slug' => 'confirmation', 'content' => $fld, ]; $this->field_element( 'row', $field, $args ); // Password strength. $meter = $this->field_element( 'toggle', $field, [ 'slug' => 'password-strength', 'value' => isset( $field['password-strength'] ) ? '1' : '0', 'desc' => esc_html__( 'Enable Password Strength', 'wpforms-lite' ), 'tooltip' => esc_html__( 'Check this option to set minimum password strength.', 'wpforms-lite' ), ], false ); $args = [ 'slug' => 'password-strength', 'content' => $meter, ]; $this->field_element( 'row', $field, $args ); $strength_label = $this->field_element( 'label', $field, [ 'value' => esc_html__( 'Minimum Strength', 'wpforms-lite' ), 'tooltip' => esc_html__( 'Select minimum password strength level.', 'wpforms-lite' ), ], false ); $strength = $this->field_element( 'select', $field, [ 'slug' => 'password-strength-level', 'options' => [ '2' => esc_html__( 'Weak', 'wpforms-lite' ), '3' => esc_html__( 'Medium', 'wpforms-lite' ), '4' => esc_html__( 'Strong', 'wpforms-lite' ), ], 'value' => $field['password-strength-level'] ?? '3', ], false ); $args = [ 'slug' => 'password-strength-level', 'class' => ! isset( $field['password-strength'] ) ? 'wpforms-hidden' : '', 'content' => $strength_label . $strength, ]; $this->field_element( 'row', $field, $args ); $visibility = $this->field_element( 'toggle', $field, [ 'slug' => 'password-visibility', 'value' => isset( $field['password-visibility'] ) ? '1' : '0', 'desc' => esc_html__( 'Enable Password Visibility', 'wpforms-lite' ), 'tooltip' => esc_html__( 'Check this option to add a toggle for showing and hiding the password.', 'wpforms-lite' ), ], false ); $args = [ 'slug' => 'password-visibility', 'content' => $visibility, ]; $this->field_element( 'row', $field, $args ); // Options close markup. $args = [ 'markup' => 'close', ]; $this->field_option( 'basic-options', $field, $args ); /* * Advanced field options. */ // Options open markup. $args = [ 'markup' => 'open', ]; $this->field_option( 'advanced-options', $field, $args ); // Size. $this->field_option( 'size', $field ); // Placeholder. $this->field_option( 'placeholder', $field ); // Confirmation Placeholder. $lbl = $this->field_element( 'label', $field, [ 'slug' => 'confirmation_placeholder', 'value' => esc_html__( 'Confirmation Placeholder Text', 'wpforms-lite' ), 'tooltip' => esc_html__( 'Enter text for the confirmation field placeholder.', 'wpforms-lite' ), ], false ); $fld = $this->field_element( 'text', $field, [ 'slug' => 'confirmation_placeholder', 'value' => ! empty( $field['confirmation_placeholder'] ) ? esc_attr( $field['confirmation_placeholder'] ) : '', ], false ); $args = [ 'slug' => 'confirmation_placeholder', 'content' => $lbl . $fld, ]; $this->field_element( 'row', $field, $args ); // Default value. $this->field_option( 'default_value', $field ); // Custom CSS classes. $this->field_option( 'css', $field ); // Hide Label. $this->field_option( 'label_hide', $field ); // Hide sublabels. $this->field_option( 'sublabel_hide', $field ); // Options close markup. $args = [ 'markup' => 'close', ]; $this->field_option( 'advanced-options', $field, $args ); } /** * Field preview inside the builder. * * @since 1.9.4 * * @param array $field Current field specific data. * * @noinspection HtmlUnknownAttribute */ public function field_preview( $field ) { $placeholder = ! empty( $field['placeholder'] ) ? $field['placeholder'] : ''; $confirm_placeholder = ! empty( $field['confirmation_placeholder'] ) ? $field['confirmation_placeholder'] : ''; $default_value = ! empty( $field['default_value'] ) ? $field['default_value'] : ''; $confirm = ! empty( $field['confirmation'] ) ? 'enabled' : 'disabled'; $field_classes = [ 'wpforms-confirm', 'wpforms-confirm-' . $confirm, ]; if ( ! empty( $field['password-visibility'] ) ) { $field_classes[] = 'wpforms-field-password-visibility-enabled'; } // Label. $this->field_preview_option( 'label', $field, [ 'label_badge' => $this->get_field_preview_badge(), ] ); $icons = wpforms()->is_pro() ? ' <div class="wpforms-field-password-input-icon"> <svg class="wpforms-field-password-input-icon-invisible" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><!--!Font Awesome Free 6.7.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.--><path d="M288 32c-80.8 0-145.5 36.8-192.6 80.6C48.6 156 17.3 208 2.5 243.7c-3.3 7.9-3.3 16.7 0 24.6C17.3 304 48.6 356 95.4 399.4C142.5 443.2 207.2 480 288 480s145.5-36.8 192.6-80.6c46.8-43.5 78.1-95.4 93-131.1c3.3-7.9 3.3-16.7 0-24.6c-14.9-35.7-46.2-87.7-93-131.1C433.5 68.8 368.8 32 288 32zM144 256a144 144 0 1 1 288 0 144 144 0 1 1 -288 0zm144-64c0 35.3-28.7 64-64 64c-7.1 0-13.9-1.2-20.3-3.3c-5.5-1.8-11.9 1.6-11.7 7.4c.3 6.9 1.3 13.8 3.2 20.7c13.7 51.2 66.4 81.6 117.6 67.9s81.6-66.4 67.9-117.6c-11.1-41.5-47.8-69.4-88.6-71.1c-5.8-.2-9.2 6.1-7.4 11.7c2.1 6.4 3.3 13.2 3.3 20.3z"/></svg> <svg class="wpforms-field-password-input-icon-visible" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><!--!Font Awesome Free 6.7.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.--><path d="M38.8 5.1C28.4-3.1 13.3-1.2 5.1 9.2S-1.2 34.7 9.2 42.9l592 464c10.4 8.2 25.5 6.3 33.7-4.1s6.3-25.5-4.1-33.7L525.6 386.7c39.6-40.6 66.4-86.1 79.9-118.4c3.3-7.9 3.3-16.7 0-24.6c-14.9-35.7-46.2-87.7-93-131.1C465.5 68.8 400.8 32 320 32c-68.2 0-125 26.3-169.3 60.8L38.8 5.1zM223.1 149.5C248.6 126.2 282.7 112 320 112c79.5 0 144 64.5 144 144c0 24.9-6.3 48.3-17.4 68.7L408 294.5c8.4-19.3 10.6-41.4 4.8-63.3c-11.1-41.5-47.8-69.4-88.6-71.1c-5.8-.2-9.2 6.1-7.4 11.7c2.1 6.4 3.3 13.2 3.3 20.3c0 10.2-2.4 19.8-6.6 28.3l-90.3-70.8zM373 389.9c-16.4 6.5-34.3 10.1-53 10.1c-79.5 0-144-64.5-144-144c0-6.9 .5-13.6 1.4-20.2L83.1 161.5C60.3 191.2 44 220.8 34.5 243.7c-3.3 7.9-3.3 16.7 0 24.6c14.9 35.7 46.2 87.7 93 131.1C174.5 443.2 239.2 480 320 480c47.8 0 89.9-12.9 126.2-32.5L373 389.9z"/></svg> </div>' : ''; $field_markup = ' <div class="wpforms-field-password-input"> <input type="password" %1$s> %2$s </div>'; ?> <div class="<?php echo wpforms_sanitize_classes( $field_classes, true ); ?>"> <div class="wpforms-confirm-primary"> <?php printf( // The `$field_markup` variable is escaped above, we should escape only passed variables to placeholders. $field_markup, // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped wpforms_html_attributes( '', [ 'primary-input' ], [], [ 'readonly' => 'readonly', 'placeholder' => $placeholder, 'value' => $default_value, ] ), $icons // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ); ?> <label class="wpforms-sub-label"><?php esc_html_e( 'Password', 'wpforms-lite' ); ?></label> </div> <div class="wpforms-confirm-confirmation"> <?php printf( // The `$field_markup` variable is escaped above, we should escape only passed variables to placeholders. $field_markup, // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped wpforms_html_attributes( '', [ 'secondary-input' ], [], [ 'readonly' => 'readonly', 'placeholder' => $confirm_placeholder, ] ), $icons // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ); ?> <label class="wpforms-sub-label"><?php esc_html_e( 'Confirm Password', 'wpforms-lite' ); ?></label> </div> </div> <?php // Description. $this->field_preview_option( 'description', $field ); } /** * Field display on the form front-end. * * @since 1.9.4 * * @param array $field Field data and settings. * @param array $deprecated Deprecated field attributes. Use field properties. * @param array $form_data Form data and settings. */ public function field_display( $field, $deprecated, $form_data ) { } } Fields/PaymentSelect/Field.php 0000644 00000041656 15252506741 0012306 0 ustar 00 <?php namespace WPForms\Forms\Fields\PaymentSelect; use WPForms_Field; /** * Dropdown payment field. * * @since 1.8.2 */ class Field extends WPForms_Field { /** * Classic (old) style. * * @since 1.8.2 * * @var string */ public const STYLE_CLASSIC = 'classic'; /** * Modern style. * * @since 1.8.2 * * @var string */ public const STYLE_MODERN = 'modern'; /** * Primary class constructor. * * @since 1.8.2 */ public function init() { // Define field type information. $this->name = esc_html__( 'Dropdown Items', 'wpforms-lite' ); $this->keywords = esc_html__( 'product, store, ecommerce, pay, payment', 'wpforms-lite' ); $this->type = 'payment-select'; $this->icon = 'fa-caret-square-o-down'; $this->order = 70; $this->group = 'payment'; $this->defaults = [ 1 => [ 'label' => esc_html__( 'First Item', 'wpforms-lite' ), 'value' => '10', 'default' => '', ], 2 => [ 'label' => esc_html__( 'Second Item', 'wpforms-lite' ), 'value' => '25', 'default' => '', ], 3 => [ 'label' => esc_html__( 'Third Item', 'wpforms-lite' ), 'value' => '50', 'default' => '', ], ]; $this->default_settings = [ 'choices' => $this->defaults, ]; $this->hooks(); } /** * Register hooks. * * @since 1.8.2 */ private function hooks() { // Define additional field properties. add_filter( "wpforms_field_properties_{$this->type}", [ $this, 'field_properties' ], 5, 3 ); // Form frontend CSS enqueues. add_action( 'wpforms_frontend_css', [ $this, 'enqueue_frontend_css' ] ); // Form frontend JS enqueues. add_action( 'wpforms_frontend_js', [ $this, 'enqueue_frontend_js' ] ); // Customize HTML field value. add_filter( 'wpforms_html_field_value', [ $this, 'field_html_value' ], 10, 4 ); } /** * Define additional field properties. * * @since 1.8.2 * * @param array $properties Field properties. * @param array $field Field settings. * @param array $form_data Form data and settings. * * @return array */ public function field_properties( $properties, $field, $form_data ) { // Remove primary input. unset( $properties['inputs']['primary'] ); // Define data. $form_id = absint( $form_data['id'] ); $field_id = absint( $field['id'] ); $choices = $field['choices']; // Set options container (<select>) properties. $properties['input_container'] = [ 'class' => [ 'wpforms-payment-price' ], 'data' => [], 'id' => "wpforms-{$form_id}-field_{$field_id}", 'attr' => [ 'name' => "wpforms[fields][{$field_id}]", ], ]; // Set properties. foreach ( $choices as $key => $choice ) { $properties['inputs'][ $key ] = [ 'container' => [ 'attr' => [], 'class' => [ "choice-{$key}" ], 'data' => [], 'id' => '', ], 'label' => [ 'attr' => [ 'for' => "wpforms-{$form_id}-field_{$field_id}_{$key}", ], 'class' => [ 'wpforms-field-label-inline' ], 'data' => [], 'id' => '', 'text' => $this->get_choices_label( $choice['label'] ?? '', $key, $field ), ], 'attr' => [ 'value' => $choice['value'] ?? '', 'data' => [ 'amount' => wpforms_format_amount( wpforms_sanitize_amount( $choice['value'] ?? '' ) ), ], ], 'class' => [], 'data' => [], 'id' => "wpforms-{$form_id}-field_{$field_id}_{$key}", 'required' => ! empty( $field['required'] ) ? 'required' : '', 'default' => isset( $choice['default'] ), ]; } // Add a class that changes the field size. if ( ! empty( $field['size'] ) ) { $properties['input_container']['class'][] = 'wpforms-field-' . esc_attr( $field['size'] ); } // Required class for pagebreak validation. if ( ! empty( $field['required'] ) ) { $properties['input_container']['class'][] = 'wpforms-field-required'; } // Add additional class for container. if ( ! empty( $field['style'] ) && in_array( $field['style'], [ self::STYLE_CLASSIC, self::STYLE_MODERN ], true ) ) { $properties['container']['class'][] = "wpforms-field-select-style-{$field['style']}"; } if ( $this->is_payment_quantities_enabled( $field ) ) { $properties['container']['class'][] = ' wpforms-payment-quantities-enabled'; } return $properties; } /** * Get the value, that is used to prefill via dynamic or fallback population. * Based on field data and current properties. * * @since 1.8.2 * * @param string $raw_value Value from a GET param, always a string. * @param string $input Represent a subfield inside the field. May be empty. * @param array $properties Field properties. * @param array $field Current field specific data. * * @return array Modified field properties. */ protected function get_field_populated_single_property_value( $raw_value, $input, $properties, $field ) { /* * When the form is submitted, we get from Fallback only values (choice ID). * As payment-dropdown field doesn't support 'show_values' option - * we should transform value into label to check against using general logic in parent method. */ if ( ! is_string( $raw_value ) || empty( $field['choices'] ) || ! is_array( $field['choices'] ) ) { return $properties; } // The form submits only the choice ID, so shortcut for Dynamic when we have a label there. if ( ! is_numeric( $raw_value ) ) { return parent::get_field_populated_single_property_value( $raw_value, $input, $properties, $field ); } if ( ! empty( $field['choices'][ $raw_value ]['label'] ) && ! empty( $field['choices'][ $raw_value ]['value'] ) ) { return parent::get_field_populated_single_property_value( $field['choices'][ $raw_value ]['label'], $input, $properties, $field ); } return $properties; } /** * Field options panel inside the builder. * * @since 1.8.2 * * @param array $field Field settings. */ public function field_options( $field ) { /* * Basic field options. */ // Options open markup. $this->field_option( 'basic-options', $field, [ 'markup' => 'open' ] ); // Label. $this->field_option( 'label', $field ); // Choices option. $this->field_option( 'choices_payments', $field ); // Show price after item labels. $fld = $this->field_element( 'toggle', $field, [ 'slug' => 'show_price_after_labels', 'value' => isset( $field['show_price_after_labels'] ) ? '1' : '0', 'desc' => esc_html__( 'Show Price After Item Labels', 'wpforms-lite' ), 'tooltip' => esc_html__( 'Check this option to show price of the item after the label.', 'wpforms-lite' ), ], false ); $args = [ 'slug' => 'show_price_after_labels', 'content' => $fld, ]; $this->field_element( 'row', $field, $args ); // Quantity. $this->field_option( 'quantity', $field ); // Description. $this->field_option( 'description', $field ); // Required toggle. $this->field_option( 'required', $field ); // Options close markup. $this->field_option( 'basic-options', $field, [ 'markup' => 'close' ] ); /* * Advanced field options. */ // Options open markup. $this->field_option( 'advanced-options', $field, [ 'markup' => 'open' ] ); // Style. $lbl = $this->field_element( 'label', $field, [ 'slug' => 'style', 'value' => esc_html__( 'Style', 'wpforms-lite' ), 'tooltip' => esc_html__( 'Classic style is the default one generated by your browser. Modern has a fresh look and displays all selected options in a single row.', 'wpforms-lite' ), ], false ); $fld = $this->field_element( 'select', $field, [ 'slug' => 'style', 'value' => ! empty( $field['style'] ) ? $field['style'] : self::STYLE_CLASSIC, 'options' => [ self::STYLE_CLASSIC => esc_html__( 'Classic', 'wpforms-lite' ), self::STYLE_MODERN => esc_html__( 'Modern', 'wpforms-lite' ), ], ], false ); $this->field_element( 'row', $field, [ 'slug' => 'style', 'content' => $lbl . $fld, ] ); // Size. $this->field_option( 'size', $field ); // Placeholder. $this->field_option( 'placeholder', $field ); // Custom CSS classes. $this->field_option( 'css', $field ); // Hide label. $this->field_option( 'label_hide', $field ); // Options close markup. $this->field_option( 'advanced-options', $field, [ 'markup' => 'close' ] ); } /** * Field preview inside the builder. * * @since 1.8.2 * * @param array $field Field settings. */ public function field_preview( $field ) { // Label. $this->field_preview_option( 'label', $field ); // Prepare arguments. $args['modern'] = false; if ( ! empty( $field['style'] ) && $field['style'] === self::STYLE_MODERN ) { $args['modern'] = true; $args['class'] = 'choicesjs-select'; } // Choices. $this->field_preview_option( 'choices', $field, $args ); // Quantity. $this->field_preview_option( 'quantity', $field ); // Description. $this->field_preview_option( 'description', $field ); } /** * Field display on the form front-end. * * @since 1.8.2 * * @param array $field Field data and settings. * @param array $deprecated Deprecated array of field attributes. * @param array $form_data Form data and settings. * * @noinspection HtmlUnknownAttribute*/ public function field_display( $field, $deprecated, $form_data ) { // phpcs:ignore Generic.Metrics.CyclomaticComplexity.TooHigh $container = $field['properties']['input_container']; $field_placeholder = ! empty( $field['placeholder'] ) ? $field['placeholder'] : ''; $is_modern = ! empty( $field['style'] ) && $field['style'] === self::STYLE_MODERN; $choices = $field['properties']['inputs']; if ( ! empty( $field['required'] ) ) { $container['attr']['required'] = 'required'; } // Add a class for Choices.js initialization. if ( $is_modern ) { $container['class'][] = 'choicesjs-select'; // Add a size-class to data attribute - it is used when Choices.js is initialized. if ( ! empty( $field['size'] ) ) { $container['data']['size-class'] = 'wpforms-field-row wpforms-field-' . sanitize_html_class( $field['size'] ); } $container['data']['search-enabled'] = $this->is_choicesjs_search_enabled( count( $choices ) ); } $has_default = false; // Check to see if any of the options were selected by default. foreach ( $choices as $choice ) { if ( ! empty( $choice['default'] ) ) { $has_default = true; break; } } // Preselect default if no other choices were marked as default. printf( '<select %s>', wpforms_html_attributes( $container['id'], $container['class'], $container['data'], $container['attr'] ) // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ); // Optional placeholder. if ( ! empty( $field_placeholder ) || $is_modern ) { printf( '<option value="" class="placeholder" disabled %s>%s</option>', selected( false, $has_default, false ), esc_html( $field_placeholder ) ); } // Format string for option. if ( $is_modern ) { // The `data-custom-properties` is a Choices.js attribute, and it stores a copy of `data-amount` attribute. $option_format = '<option value="%1$s" data-amount="%2$s" data-custom-properties="%2$s" %3$s>%4$s</option>'; } else { $option_format = '<option value="%1$s" data-amount="%2$s" %3$s>%4$s</option>'; } // Build the select options. foreach ( $choices as $key => $choice ) { $amount = wpforms_format_amount( wpforms_sanitize_amount( $choice['attr']['value'] ) ); $label = $choice['label']['text'] ?? ''; /* translators: %s - item number. */ $label = $label !== '' ? $label : sprintf( esc_html__( 'Item %s', 'wpforms-lite' ), $key ); $label .= ! empty( $field['show_price_after_labels'] ) && isset( $choice['attr']['value'] ) ? ' - ' . wpforms_format_amount( wpforms_sanitize_amount( $choice['attr']['value'] ), true ) : ''; printf( $option_format, // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped esc_attr( $key ), esc_attr( $amount ), selected( true, ! empty( $choice['default'] ), false ), esc_html( $label ) ); } echo '</select>'; $this->display_quantity_dropdown( $field ); } /** * Validate field on submitting the form. * * @since 1.8.2 * * @param int $field_id Field ID. * @param string $field_submit Submitted field value (raw data). * @param array $form_data Form data and settings. */ public function validate( $field_id, $field_submit, $form_data ) { // Basic required check - If field is marked as required, check for entry data. if ( ! empty( $form_data['fields'][ $field_id ]['required'] ) && empty( $field_submit ) ) { wpforms()->obj( 'process' )->errors[ $form_data['id'] ][ $field_id ] = wpforms_get_required_label(); } // Validate that the option selected is real. if ( ! empty( $field_submit ) && empty( $form_data['fields'][ $field_id ]['choices'][ $field_submit ] ) ) { wpforms()->obj( 'process' )->errors[ $form_data['id'] ][ $field_id ] = esc_html__( 'Invalid payment option', 'wpforms-lite' ); } } /** * Format and sanitize field. * * @since 1.8.2 * * @param int $field_id Field ID. * @param string $field_submit Submitted field value (selected option). * @param array $form_data Form data and settings. */ public function format( $field_id, $field_submit, $form_data ) { $choice_label = ''; $field = $form_data['fields'][ $field_id ]; $name = ! empty( $field['label'] ) ? sanitize_text_field( $field['label'] ) : ''; // Fetch the amount. if ( ! empty( $field['choices'][ $field_submit ]['value'] ) ) { $amount = wpforms_sanitize_amount( $field['choices'][ $field_submit ]['value'] ); } else { $amount = 0; } $value = wpforms_format_amount( $amount, true ); if ( empty( $field_submit ) ) { $value = ''; } elseif ( ! empty( $field['choices'][ $field_submit ]['label'] ) ) { $choice_label = sanitize_text_field( $field['choices'][ $field_submit ]['label'] ); $value = $choice_label . ' - ' . $value; } $field_data = [ 'name' => $name, 'value' => $value, 'value_choice' => $choice_label, 'value_raw' => sanitize_text_field( $field_submit ), 'amount' => wpforms_format_amount( $amount ), 'amount_raw' => $amount, 'currency' => wpforms_get_currency(), 'id' => absint( $field_id ), 'type' => sanitize_key( $this->type ), ]; if ( $this->is_payment_quantities_enabled( $field ) ) { $field_data['quantity'] = $this->get_submitted_field_quantity( $field, $form_data ); } wpforms()->obj( 'process' )->fields[ $field_id ] = $field_data; } /** * Form frontend CSS enqueues. * * @since 1.8.2 * * @param array $forms Forms on the current page. */ public function enqueue_frontend_css( $forms ) { $has_modern_select = false; foreach ( $forms as $form ) { if ( $this->is_field_style( $form, self::STYLE_MODERN ) ) { $has_modern_select = true; break; } } if ( $has_modern_select || wpforms()->obj( 'frontend' )->assets_global() ) { $min = wpforms_get_min_suffix(); wp_enqueue_style( 'wpforms-choicesjs', WPFORMS_PLUGIN_URL . "assets/css/choices{$min}.css", [], '10.2.0' ); } } /** * Form frontend JS enqueues. * * @since 1.8.2 * * @param array $forms Forms on the current page. */ public function enqueue_frontend_js( $forms ) { $has_modern_select = false; foreach ( $forms as $form ) { if ( $this->is_field_style( $form, self::STYLE_MODERN ) ) { $has_modern_select = true; break; } } if ( $has_modern_select || wpforms()->obj( 'frontend' )->assets_global() ) { $this->enqueue_choicesjs_once( $forms ); } } /** * Whether the provided form has a dropdown field with a specified style. * * @since 1.8.2 * * @param array $form Form data. * @param string $style Desired field style. * * @return bool */ protected function is_field_style( $form, $style ) { $is_field_style = false; if ( empty( $form['fields'] ) ) { return false; } foreach ( (array) $form['fields'] as $field ) { if ( ! empty( $field['type'] ) && $field['type'] === $this->type && ! empty( $field['style'] ) && sanitize_key( $style ) === $field['style'] ) { $is_field_style = true; break; } } return $is_field_style; } /** * Get field name for an ajax error message. * * @since 1.8.2 * * @param string|mixed $name Field name for error triggered. * @param array $field Field settings. * @param array $props List of properties. * @param string|string[] $error Error message. * * @return string * @noinspection PhpMissingReturnTypeInspection * @noinspection ReturnTypeCanBeDeclaredInspection */ public function ajax_error_field_name( $name, $field, $props, $error ) { $name = (string) $name; if ( ! isset( $field['type'] ) || $field['type'] !== $this->type ) { return $name; } return $props['input_container']['attr']['name'] ?? ''; } } Fields/Richtext/Field.php 0000644 00000011665 15252506741 0011320 0 ustar 00 <?php namespace WPForms\Forms\Fields\Richtext; use WPForms\Forms\Fields\Traits\ProField as ProFieldTrait; use WPForms_Field; /** * Rich Text field. * * @since 1.9.4 */ class Field extends WPForms_Field { use ProFieldTrait; /** * Primary class constructor. * * @since 1.9.4 */ public function init() { // Define field type information. $this->name = esc_html__( 'Rich Text', 'wpforms-lite' ); $this->keywords = esc_html__( 'image, text, table, list, heading, wysiwyg, visual', 'wpforms-lite' ); $this->type = 'richtext'; $this->icon = 'fa-pencil-square-o'; $this->order = 170; $this->group = 'fancy'; $this->init_pro_field(); $this->hooks(); } /** * Hooks. * * @since 1.9.4 */ protected function hooks() { } /** * Field options panel inside the builder. * * @since 1.9.4 * * @param array $field Field data and settings. */ public function field_options( $field ) { // Options open markup. $this->field_option( 'basic-options', $field, [ 'markup' => 'open', 'after_title' => $this->get_field_options_notice(), ] ); $this->field_option( 'label', $field ); $this->field_option( 'description', $field ); $this->field_element( 'row', $field, [ 'slug' => 'media_enabled', 'content' => $this->field_element( 'toggle', $field, [ 'slug' => 'media_enabled', 'value' => isset( $field['media_enabled'] ) ? '1' : '0', 'desc' => esc_html__( 'Allow Media Uploads', 'wpforms-lite' ), 'tooltip' => esc_html__( 'Check this option to allow uploading and embedding files.', 'wpforms-lite' ), ], false ), ] ); $media_library = $this->field_element( 'toggle', $field, [ 'slug' => 'media_library', 'value' => isset( $field['media_library'] ) ? '1' : '0', 'desc' => esc_html__( 'Store files in WordPress Media Library', 'wpforms-lite' ), 'tooltip' => esc_html__( 'Check this option to store files in the WordPress Media Library.', 'wpforms-lite' ), ], false ); $this->field_element( 'row', $field, [ 'slug' => 'media_controls', 'class' => ! isset( $field['media_enabled'] ) ? 'wpforms-hide' : '', 'content' => $media_library, ] ); $this->field_option( 'required', $field ); $this->field_option( 'basic-options', $field, [ 'markup' => 'close' ] ); $this->field_option( 'advanced-options', $field, [ 'markup' => 'open' ] ); $output_style = $this->field_element( 'label', $field, [ 'slug' => 'style', 'value' => esc_html__( 'Field Style', 'wpforms-lite' ), ], false ); $output_style .= $this->field_element( 'select', $field, [ 'slug' => 'style', 'value' => ! empty( $field['style'] ) ? esc_attr( $field['style'] ) : 'full', 'options' => [ 'full' => esc_html__( 'Full', 'wpforms-lite' ), 'basic' => esc_html__( 'Basic', 'wpforms-lite' ), ], ], false ); $this->field_element( 'row', $field, [ 'slug' => 'style', 'content' => $output_style, ] ); $this->field_option( 'size', $field ); $this->field_option( 'css', $field ); $this->field_option( 'label_hide', $field ); $this->field_option( 'advanced-options', $field, [ 'markup' => 'close' ] ); } /** * The field preview inside the builder. * * @since 1.9.4 * * @param array $field Field data and settings. */ public function field_preview( $field ) { // Label. $this->field_preview_option( 'label', $field, [ 'label_badge' => $this->get_field_preview_badge(), ] ); $style = ! empty( $field['style'] ) && $field['style'] === 'basic' ? 'wpforms-field-richtext-toolbar-basic' : ''; $media_enabled = ! empty( $field['media_enabled'] ) ? 'wpforms-field-richtext-media-enabled' : ''; ?> <div class="wpforms-richtext-wrap tmce-active"> <div class="wp-editor-tabs"> <button type="button" class="wp-switch-editor switch-tmce"><?php esc_html_e( 'Visual', 'wpforms-lite' ); ?></button> <button type="button" class="wp-switch-editor"><?php esc_html_e( 'Text', 'wpforms-lite' ); ?></button> </div> <div class="wp-editor-container "> <div class="mce-container-body"> <div class="mce-toolbar-grp <?php echo esc_attr( $style ); ?> <?php echo esc_attr( $media_enabled ); ?>"></div> </div> <textarea id="wpforms-richtext-<?php echo wpforms_validate_field_id( $field['id'] ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>"></textarea> <div class="mce-statusbar"> <i class="mce-ico mce-i-resize"></i> </div> </div> </div> <?php $this->field_preview_option( 'description', $field ); } /** * The field display on the form front-end. * * @since 1.9.4 * * @param array $field Field data and settings. * @param array $deprecated Field attributes. * @param array $form_data Form data and settings. */ public function field_display( $field, $deprecated, $form_data ) { } } Fields/Traits/FileDisplayTrait.php 0000644 00000027506 15252506741 0013163 0 ustar 00 <?php namespace WPForms\Forms\Fields\Traits; /** * File Entry Preview Trait. * * Contains common methods for displaying file uploads in entries and emails. * * @since 1.9.8 */ trait FileDisplayTrait { /** * Format field value for display in Entries. * * @since 1.9.8 * * @param string|mixed $val Field value. * @param array $field Field data. * @param array $form_data Form data. * @param string $context Display context. * * @return string */ public function html_field_value( $val, array $field, array $form_data = [], string $context = '' ): string { $val = (string) $val; if ( $field['type'] !== $this->type ) { return $val; } $field = $this->entry_preview_prepare_field_value( $field, $form_data, $context ); // Return early if there is no value at all. if ( empty( $field['value'] ) && empty( $field['value_raw'] ) ) { return $val; } // Process modern uploader. if ( ! empty( $field['value_raw'] ) ) { return $this->process_modern_uploader( $field, $context ); } // Process classic uploader. if ( $this->is_entry_preview( $context ) ) { return $this->entry_preview_file_link_html( $field, $this->get_file_url( $field ) ); } return $this->get_file_link_html( $field, $context ); } /** * Get file link HTML. * * @since 1.9.8 * * @param array $file File data. * @param string $context Value display context. * * @return string * @noinspection HtmlUnknownTarget */ private function get_file_link_html( array $file, string $context ): string { $html = in_array( $context, [ 'email-html', 'entry-single' ], true ) ? $this->file_icon_html( $file ) : ''; $html .= sprintf( '<a href="%s" rel="noopener noreferrer" target="_blank" style="%s">%s</a>', esc_url( $this->get_file_url( $file ) ), $context === 'email-html' ? 'padding-left:10px;' : '', esc_html( $this->get_file_name( $file ) ) ); return $html; } /** * Get the URL of a file. * * @since 1.9.8 * * @param array $file File data. * @param array $args Additional query arguments. * * @return string */ public function get_file_url( array $file, array $args = [] ): string { $file_url = $file['value'] ?? ''; if ( ! empty( $file['protection_hash'] ) ) { $args = wp_parse_args( $args, [ 'wpforms_uploaded_file' => $file['protection_hash'], ] ); $file_url = add_query_arg( $args, home_url() ); } /** * Allow modifying the URL of a file. * * @since 1.9.8 * * @param string $file_url File URL. * @param array $file File data. */ return (string) apply_filters( 'wpforms_pro_fields_file_upload_get_file_url', $file_url, $file ); } /** * Get the name of a file. * * @since 1.9.8 * * @param array $file File data. * * @return string */ public function get_file_name( array $file ): string { if ( ! $this->is_file_protected( $file ) ) { return $file['file_original']; } $ext = $file['ext'] ?? ''; return sprintf( '%s.%s', hash( 'crc32b', $file['file_original'] ), $ext ); } /** * Check if the file is protected. * * @since 1.9.8 * * @param array $file_data File data. * * @return bool True if the file is protected, false otherwise. */ private function is_file_protected( array $file_data ): bool { return ! empty( $file_data['protection_hash'] ); } /** * Get file icon HTML. * * @since 1.9.8 * * @param array $file_data File data. * * @return string * @noinspection HtmlUnknownTarget */ public function file_icon_html( array $file_data ): string { $src = esc_url( $file_data['value'] ); $ext_types = wp_get_ext_types(); if ( $this->is_file_protected( $file_data ) || ! in_array( $file_data['ext'], $ext_types['image'], true ) ) { $src = wp_mime_type_icon( wp_ext2type( $file_data['ext'] ) ?? '' ); } elseif ( ! empty( $file_data['attachment_id'] ) ) { $image = wp_get_attachment_image_src( $file_data['attachment_id'], [ 16, 16 ], true ); $src = $image ? $image[0] : $src; } return sprintf( '<span class="file-icon"><img width="16" height="16" src="%s" alt="" /></span>', esc_url( $src ) ); } /** * Prepare field value for entry preview. * * @since 1.9.9 * * @param array $field Field data. * @param array $form_data Form data. * @param string $context Display context. * * @return array */ private function entry_preview_prepare_field_value( array $field, array $form_data, string $context ): array { if ( ! empty( $field['value'] ) || ! empty( $field['value_raw'] ) || ! $this->is_entry_preview( $context ) ) { return $field; } $this->form_id = absint( $form_data['id'] ); $this->field_id = absint( $field['id'] ); $input_name = $this->get_input_name(); // Modern uploader: data (JSON) in $_POST. $raw_json = isset( $_POST[ $input_name ] ) ? sanitize_text_field( wp_unslash( $_POST[ $input_name ] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Missing if ( $raw_json !== '' && wpforms_is_json( $raw_json ) ) { $files = json_decode( $raw_json, true ); if ( ! empty( $files ) ) { $field['value_raw'] = array_map( static function ( $file ) { $name = $file['name'] ?? ''; return [ 'value' => $file['url'] ?? '', 'file_original' => $name, 'ext' => strtolower( pathinfo( $name, PATHINFO_EXTENSION ) ), ]; }, $files ); } return $field; } // Classic uploader: data in $_FILES. $files = $_FILES[ $input_name ]['name'] ?? []; // phpcs:ignore WordPress.Security.NonceVerification.Missing, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized $files = is_array( $files ) ? $files : [ $files ]; $files = array_filter( array_map( 'sanitize_file_name', $files ) ); if ( ! empty( $files ) ) { $value_raw = []; foreach ( $files as $index => $file ) { $ext = strtolower( pathinfo( $file, PATHINFO_EXTENSION ) ); $value_raw[] = [ 'value' => $this->get_entry_preview_classic_file_src( $input_name, $index, $ext ), 'file_original' => $file, 'ext' => $ext, ]; } $field['value_raw'] = $value_raw; } return $field; } /** * Get the inline image source for a classic-uploaded file in the entry preview. * * Classic uploads have no public URL before submission, so the base * implementation returns an empty string and the file renders as text. * Field types that can safely inline a preview ( Camera ) override this. * * @since 2.0.0 * * @param string $input_name Field input name. * @param int|string $index File index within the upload. * @param string $ext Lowercased file extension. * * @return string * @noinspection PhpUnusedParameterInspection */ protected function get_entry_preview_classic_file_src( string $input_name, $index, string $ext ): string { // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundAfterLastUsed return ''; } /** * Process modern uploader. * * @since 1.9.9 * * @param array $field Field data. * @param string $context Value display context. * * @return string */ private function process_modern_uploader( array $field, string $context ): string { $values = $context === 'entry-table' ? array_slice( $field['value_raw'], 0, 3, true ) : $field['value_raw']; $html = ''; $submitted_fields = ! empty( $_POST['wpforms'] ) ? stripslashes_deep( $_POST['wpforms'] ) : []; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.NonceVerification.Missing $is_entry_preview = $this->is_entry_preview( $context ); foreach ( $values as $key => $file ) { $src = $this->get_file_url( $file ); // If the temp file doesn't exist, fallback to submitted field data if set. if ( $is_entry_preview && ! file_exists( $src ) && isset( $submitted_fields['complete'][ $field['id'] ]['value_raw'][ $key ] ) ) { $file = $submitted_fields['complete'][ $field['id'] ]['value_raw'][ $key ]; $src = $this->get_file_url( $file ); } // Normalize structure ( pre-submit uses url/name; post-submit uses value/file_original ). if ( empty( $file['value'] ) && ! empty( $file['url'] ) ) { $file['value'] = $file['url']; } if ( empty( $file['file_original'] ) && ! empty( $file['name'] ) ) { $file['file_original'] = $file['name']; } if ( empty( $file['ext'] ) ) { $source = $file['file_original'] ?? ( $file['name'] ?? '' ); $file['ext'] = strtolower( pathinfo( $source, PATHINFO_EXTENSION ) ); } if ( empty( $file['file_original'] ) ) { continue; } if ( $is_entry_preview ) { $html .= $this->entry_preview_file_link_html( $file, $src ); continue; } $html .= $this->get_file_link_html( $file, $context ) . '<br/>'; } if ( count( $values ) < count( $field['value_raw'] ) ) { $html .= '…'; } return $html; } /** * Get file link HTML for entry preview. * Show image previews, non-images as plain text. * * @since 1.9.9 * * @param array $file File data. * @param string $src File source. * * @return string */ private function entry_preview_file_link_html( array $file, string $src ): string { $filename = esc_html( $file['file_original'] ?? $this->get_file_name( $file ) ); $is_image = in_array( $file['ext'] ?? '', wp_get_ext_types()['image'], true ); // Render an inline thumbnail only for non-protected images. if ( $is_image && ! empty( $src ) && ! $this->is_file_protected( $file ) ) { return sprintf( '<span class="wpforms-entry-preview-file is-image"><img src="%1$s" alt="%2$s"/><span class="wpforms-entry-preview-filename">%2$s</span></span>', esc_url( $src ), esc_html( $filename ) ); } // Show the file name otherwise. return sprintf( '<span class="wpforms-entry-preview-file">%1$s</span>', $filename ); } /** * Check if the context is an entry preview. * * @since 1.9.9 * * @param string $context Value display context. * * @return bool True if the context is entry preview, false otherwise. */ private function is_entry_preview( string $context ): bool { return $context === 'entry-preview'; } /** * Get the input name for the field. * * @since 1.9.9 * * @return string */ public function get_input_name(): string { return sprintf( 'wpforms_%d_%d', $this->form_id, $this->field_id ); } /** * Format the field value for smart tags. * * @since 1.10.0 * * @param string $value The field value. * @param int $field_id The field ID. * @param array $fields The form fields. * @param string $field_key The field key. * * @return string * @noinspection PhpMissingParamTypeInspection * @noinspection PhpUnusedParameterInspection */ public function smart_tags_formatted_field_value( $value, $field_id, $fields, $field_key ): string { // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundAfterLastUsed $value = (string) $value; $field = $fields[ $field_id ] ?? []; return $this->get_formatted_value( $value, $field ); } /** * Get file URLs. * * @since 1.10.0 * * @param array $values Field values. * * @return array */ private function get_file_urls( array $values ): array { $urls = []; foreach ( $values as $file ) { $urls[] = $this->get_file_url( $file ); } return $urls; } /** * Get formatted value. * * @since 1.10.0 * * @param string $value Field value. * @param array $field Field settings. * * @return string */ private function get_formatted_value( string $value, array $field ): string { $type = $field['type'] ?? ''; if ( $type !== $this->type ) { return $value; } if ( empty( $field['style'] ) ) { return $this->get_file_url( $field ); } $values = (array) $field['value_raw']; $values = array_filter( $values ); $urls = $this->get_file_urls( $values ); return empty( $urls ) ? $value : implode( "\n", $urls ); } } Fields/Traits/IndicatorRendererTrait.php 0000644 00000007773 15252506741 0014365 0 ustar 00 <?php namespace WPForms\Forms\Fields\Traits; /** * Trait for rendering page indicators. * * Provides shared methods for rendering circles and connector indicators * to avoid code duplication between Lite (preview) and Pro (frontend) implementations. * * @since 1.10.0 */ trait IndicatorRendererTrait { /** * Render a single circles indicator item. * * @since 1.10.0 * * @param array $page Page data with title. * @param int $page_num Current page number. * @param string $color Indicator color. * @param bool $is_interactive Whether to add accessibility attributes for interactive elements. */ public function render_circles_indicator_item( array $page, int $page_num, string $color, bool $is_interactive = false ): void { $is_first = $page_num === 1; $class = $is_first ? 'active' : ''; $background_color = ! empty( $color ) ? $color : ''; // Build wrapper div attributes. $wrapper_attrs = sprintf( 'class="wpforms-page-indicator-page %1$s wpforms-page-indicator-page-%2$d" data-page="%2$d"', sanitize_html_class( $class ), absint( $page_num ) ); // Add accessibility attributes for interactive elements (frontend). if ( $is_interactive ) { $wrapper_attrs .= sprintf( ' role="button" tabindex="0" aria-label="%s"', /* translators: %d - page number. */ esc_attr( sprintf( __( 'Go to page %d', 'wpforms-lite' ), $page_num ) ) ); } printf( '<div %s>', $wrapper_attrs ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped // Render page number circle. printf( '<span class="wpforms-page-indicator-page-number" %s data-page="%d">%d</span>', $is_first && ! empty( $background_color ) ? 'style="background-color:' . sanitize_hex_color( $background_color ) . '"' : '', absint( $page_num ), absint( $page_num ) ); // Render page title if present. if ( ! empty( $page['title'] ) ) { printf( '<span class="wpforms-page-indicator-page-title">%s</span>', esc_html( $page['title'] ) ); } echo '</div>'; } /** * Render a single connector indicator item. * * @since 1.10.0 * * @param array $page Page data with title. * @param int $page_num Current page number. * @param string $color Indicator color. * @param string $width Width percentage for the connector item. * @param bool $is_interactive Whether to add accessibility attributes for interactive elements. */ public function render_connector_indicator_item( array $page, int $page_num, string $color, string $width, bool $is_interactive = false ): void { $is_first = $page_num === 1; $class = $is_first ? 'active ' : ''; // Build wrapper div attributes. $wrapper_attrs = sprintf( 'class="wpforms-page-indicator-page %s wpforms-page-indicator-page-%d" style="min-width:%s;" data-page="%d"', sanitize_html_class( $class ), absint( $page_num ), esc_attr( $width ), absint( $page_num ) ); // Add accessibility attributes for interactive elements (frontend). if ( $is_interactive ) { $wrapper_attrs .= sprintf( ' role="button" tabindex="0" aria-label="%s"', /* translators: %d - page number. */ esc_attr( sprintf( __( 'Go to page %d', 'wpforms-lite' ), $page_num ) ) ); } printf( '<div %s>', $wrapper_attrs ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped // Render page number with triangle. printf( '<span class="wpforms-page-indicator-page-number" %s data-page="%d">%d', $is_first && ! empty( $color ) ? 'style="background-color:' . sanitize_hex_color( $color ) . '"' : '', absint( $page_num ), absint( $page_num ) ); printf( '<span class="wpforms-page-indicator-page-triangle" %s></span></span>', $is_first && ! empty( $color ) ? 'style="border-top-color:' . sanitize_hex_color( $color ) . '"' : '' ); // Render page title if present. if ( ! empty( $page['title'] ) ) { printf( '<span class="wpforms-page-indicator-page-title">%s</span>', esc_html( $page['title'] ) ); } echo '</div>'; } } Fields/Traits/ReadOnlyField.php 0000644 00000007176 15252506741 0012434 0 ustar 00 <?php namespace WPForms\Forms\Fields\Traits; /** * Trait ReadOnlyField. * * Methods for read-only fields. * * @since 1.9.8 */ trait ReadOnlyField { /** * Whether the Read-Only option is allowed. * * @since 1.9.8 * * @var bool */ protected $allow_read_only = true; /** * Init Read-Only field functionality. * * @since 1.9.8 */ public function read_only_init(): void { // Read-only field hooks. add_action( 'wpforms_field_options_bottom_advanced-options', [ $this, 'field_option_read_only_toggle' ], -10 ); add_filter( 'wpforms_field_properties', [ $this, 'read_only_field_properties' ], 100, 3 ); add_filter( "wpforms_admin_builder_ajax_save_form_field_{$this->type}", [ $this, 'read_only_save_form_field' ], 100, 3 ); add_filter( 'wpforms_frontend_strings', [ $this, 'read_only_frontend_strings' ] ); } /** * Display the Read-Only toggle on the Advanced Options tab. * * @since 1.9.8 * * @param array $field Field data. * * @return void */ public function field_option_read_only_toggle( array $field ): void { if ( $field['type'] !== $this->type || ! $this->allow_read_only ) { return; } $value = $field['read_only'] ?? '0'; $tooltip = esc_html__( 'Check this option to show the field’s value without allowing changes. It will still be submitted.', 'wpforms-lite' ); $output = $this->field_element( 'toggle', $field, [ 'slug' => 'read_only', 'value' => $value, 'desc' => esc_html__( 'Read-Only', 'wpforms-lite' ), 'tooltip' => $tooltip, ], false ); $this->field_element( 'row', $field, [ 'slug' => 'read_only', 'content' => $output, ] ); } /** * Add a Read-Only field CSS class. * * @since 1.9.8 * * @param array|mixed $properties Field properties. * @param array $field Field data and settings. * @param array $form_data Form data and settings. * * @return array * @noinspection PhpUnusedParameterInspection */ public function read_only_field_properties( $properties, array $field, array $form_data ): array { // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundAfterLastUsed $properties = (array) $properties; if ( $field['type'] !== $this->type || ! $this->allow_read_only || empty( $field['read_only'] ) ) { return $properties; } $properties['container']['class'][] = 'wpforms-field-readonly'; return $properties; } /** * Filter field data before saving the form. * * @since 1.9.8 * * @param array $field_data Field data. * @param array $form_data Forms data. * @param array $saved_form_data Saved form data. * * @noinspection PhpMissingParamTypeInspection * @noinspection PhpUnusedParameterInspection */ public function read_only_save_form_field( $field_data, array $form_data, array $saved_form_data ): array { // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundAfterLastUsed $field_data = (array) $field_data; // Unset the `required` field option if the field is Read-Only. if ( ! empty( $field_data['read_only'] ) ) { unset( $field_data['required'] ); } return $field_data; } /** * Add read-only related strings to the frontend. * * @since 1.9.8 * * @param array|mixed $strings Frontend strings. * * @return array Frontend strings. */ public function read_only_frontend_strings( $strings ): array { $strings = (array) $strings; $strings['readOnlyDisallowedFields'] = $strings['readOnlyDisallowedFields'] ?? []; if ( $this->allow_read_only ) { return $strings; } $strings['readOnlyDisallowedFields'][] = $this->type; return $strings; } } Fields/Traits/MultiFieldMenu.php 0000644 00000002504 15252506741 0012624 0 ustar 00 <?php namespace WPForms\Forms\Fields\Traits; /** * Trait MultiFieldMenu. * * Methods for multi-field menu functionality. * * @since 1.9.9 */ trait MultiFieldMenu { /** * Generate multi-field actions menu HTML. * * @since 1.9.9 * * @return string Multi-field menu HTML. */ public function get_multi_field_menu_html(): string { $items = [ 'duplicate-multi' => [ 'icon' => 'fa-files-o', 'label' => __( 'Duplicate Fields', 'wpforms-lite' ), ], 'delete-multi' => [ 'icon' => 'fa-trash-o', 'label' => __( 'Delete Fields', 'wpforms-lite' ), 'last' => true, ], ]; $divider = '<li class="wpforms-context-menu-list-divider"></li>'; $html = '<div class="wpforms-field-multi-field-menu">'; $html .= '<ul class="wpforms-context-menu-list">'; foreach ( $items as $action => $item ) { $html .= sprintf( '<li class="wpforms-context-menu-list-item" data-action="%1$s"> <span class="wpforms-context-menu-list-item-icon"> <i class="fa %2$s" aria-hidden="true"></i> </span> <span class="wpforms-context-menu-list-item-text">%3$s</span> </li> %4$s', esc_attr( $action ), esc_attr( $item['icon'] ), esc_html( $item['label'] ), empty( $item['last'] ) ? $divider : '' ); } $html .= '</ul>'; $html .= '</div>'; return $html; } } Fields/Traits/AccessRestrictionsTrait.php 0000644 00000033066 15252506741 0014566 0 ustar 00 <?php namespace WPForms\Forms\Fields\Traits; /** * Access restrictions trait. * * @since 1.9.8 */ trait AccessRestrictionsTrait { /** * User roles. * * @since 1.9.8 * * @var array */ private $user_roles = []; /** * Add access restrictions options to the field. * * @since 1.9.8 * * @param array $field Field data and settings. */ private function access_restrictions_options( array $field ) { $access_restrictions = $this->field_element( 'toggle', $field, [ 'slug' => 'is_restricted', 'value' => ! empty( $field['is_restricted'] ) ? 1 : '', 'desc' => esc_html__( 'Enable File Access Restrictions', 'wpforms-lite' ), 'tooltip' => esc_html__( 'Choose who can access the uploaded files.', 'wpforms-lite' ), 'class' => $this->get_access_restrictions_toggle_class(), ], false ); $this->field_element( 'row', $field, [ 'slug' => 'access_restrictions', 'attrs' => $this->get_access_restrictions_options_attrs(), 'content' => $access_restrictions, ] ); // User Restriction. $this->user_restriction_options( $field ); // Password Protection. $this->password_protection_options( $field ); } /** * Get access restrictions toggle class. * * @since 1.9.8 * * @return string */ protected function get_access_restrictions_toggle_class(): string { return 'wpforms-file-upload-access-restrictions'; } /** * Get access restrictions options attributes. * * @since 1.9.8 * * @return array */ protected function get_access_restrictions_options_attrs(): array { return []; } /** * Add user restrictions options to the field. * * @since 1.9.8 * * @param array $field Field data and settings. */ private function user_restriction_options( array $field ) { $user_restrictions_value = $this->get_user_restrictions_value( $field ); $this->add_user_restrictions_select( $field, $user_restrictions_value ); $hide_user_restrictions = $this->should_hide_user_restrictions( $user_restrictions_value, $field ); $this->add_user_roles_restrictions( $field, $hide_user_restrictions ); $this->add_user_names_restrictions( $field, $hide_user_restrictions ); } /** * Get user restrictions value. * * @since 1.9.8 * * @param array $field Field data and settings. * * @return string */ private function get_user_restrictions_value( array $field ): string { return ! empty( $field['user_restrictions'] ) ? $field['user_restrictions'] : 'none'; } /** * Add user restrictions select to the field. * * @since 1.9.8 * * @param array $field Field data and settings. * @param string $user_restrictions_value User restrictions value. */ private function add_user_restrictions_select( array $field, string $user_restrictions_value ) { $label = $this->field_element( 'label', $field, [ 'slug' => 'user_restrictions', 'value' => esc_html__( 'User Restriction', 'wpforms-lite' ), ], false ); $select = $this->field_element( 'select', $field, [ 'slug' => 'user_restrictions', 'value' => $user_restrictions_value, 'options' => [ 'none' => esc_html__( 'None', 'wpforms-lite' ), 'logged' => esc_html__( 'Logged-in Users', 'wpforms-lite' ), ], 'class' => 'wpforms-file-upload-user-restrictions', ], false ); $this->field_element( 'row', $field, [ 'slug' => 'user_restrictions', 'content' => $label . $select, 'class' => $this->is_restricted( $field ) ? '' : 'wpforms-hidden', ] ); } /** * Check if user restrictions should be hidden. * * @since 1.9.8 * * @param string $user_restrictions_value User restrictions value. * @param array $field Field data and settings. * * @return bool */ private function should_hide_user_restrictions( string $user_restrictions_value, array $field ): bool { return $user_restrictions_value === 'none' || ! $this->is_restricted( $field ); } /** * Add user roles restrictions to the field. * * @since 1.9.8 * * @param array $field Field data and settings. * @param bool $hide_user_restrictions Should user restrictions be hidden. */ private function add_user_roles_restrictions( array $field, bool $hide_user_restrictions ) { $label = $this->field_element( 'label', $field, [ 'slug' => 'user_roles_restrictions', 'value' => esc_html__( 'User Roles', 'wpforms-lite' ), 'tooltip' => esc_html__( 'Select the user roles that can access the uploaded files.', 'wpforms-lite' ), ], false ); $select = $this->field_element( 'select-multiple', $field, [ 'slug' => 'user_roles_restrictions', 'value' => $this->get_selected_roles( $field ), 'desc' => esc_html__( 'All users with selected roles will be able to access the uploaded files.', 'wpforms-lite' ), 'options' => $this->get_user_roles(), 'choicesjs' => false, 'class' => 'wpforms-file-upload-user-roles-select', ], false ); $this->field_element( 'row', $field, [ 'slug' => 'user_roles_restrictions', 'content' => $label . $select, 'class' => $hide_user_restrictions ? 'wpforms-hidden' : '', ] ); } /** * Get selected roles. * * @since 1.9.8 * * @param array $field Field data and settings. * * @return array */ private function get_selected_roles( array $field ): array { $selected_roles = ! empty( $field['user_roles_restrictions'] ) ? json_decode( $field['user_roles_restrictions'], true ) : []; array_unshift( $selected_roles, 'administrator' ); return array_unique( $selected_roles ); } /** * Get user roles. * * @since 1.9.8 * * @return array */ private function get_user_roles(): array { if ( empty( $this->user_roles ) ) { $roles = get_editable_roles(); $this->user_roles = array_map( static function ( $item ) { return $item['name']; }, $roles ); } return $this->user_roles; } /** * Add user names restrictions to the field. * * @since 1.9.8 * * @param array $field Field data and settings. * @param bool $hide_user_restrictions Should user restrictions be hidden. */ private function add_user_names_restrictions( array $field, bool $hide_user_restrictions ) { $label = $this->field_element( 'label', $field, [ 'slug' => 'user_names_restrictions', 'value' => esc_html__( 'Users', 'wpforms-lite' ), 'tooltip' => esc_html__( 'Select the users that can access the uploaded files.', 'wpforms-lite' ), ], false ); $select = $this->field_element( 'select-multiple', $field, [ 'slug' => 'user_names_restrictions', 'value' => array_map( 'intval', $this->get_user_ids( $field ) ), 'options' => $this->get_user_list( $field ), 'choicesjs' => false, 'class' => 'wpforms-file-upload-user-names-select', ], false ); $this->field_element( 'row', $field, [ 'slug' => 'user_names_restrictions', 'content' => $label . $select, 'class' => $hide_user_restrictions ? 'wpforms-hidden' : '', ] ); } /** * Get user ids. * * @since 1.9.8 * * @param array $field Field data and settings. * * @return array */ private function get_user_ids( array $field ): array { return ! empty( $field['user_names_restrictions'] ) ? json_decode( $field['user_names_restrictions'], true ) : []; } /** * Get user list. * * @since 1.9.8 * * @param array $field Field data and settings. * * @return array */ private function get_user_list( array $field ): array { $user_ids = $this->get_user_ids( $field ); return $this->get_selected_users( $user_ids ); } /** * Get selected users. * * @since 1.9.8 * * @param array $user_ids User IDs. * * @return array */ private function get_selected_users( array $user_ids ): array { $selected_users = []; if ( ! empty( $user_ids ) ) { $users = get_users( [ 'include' => $user_ids, 'fields' => [ 'ID', 'display_name' ], 'orderby' => 'include', ] ); $selected_users = wp_list_pluck( $users, 'display_name', 'ID' ); } return $selected_users; } /** * Add password protection options to the field. * * @since 1.9.8 * * @param array $field Field data and settings. */ private function password_protection_options( array $field ) { $this->add_password_toggle( $field ); $this->add_password_label( $field ); $this->add_password_fields( $field ); } /** * Add password toggle to the field. * * @since 1.9.8 * * @param array $field Field data and settings. */ private function add_password_toggle( array $field ) { $password = $this->field_element( 'toggle', $field, [ 'slug' => 'is_protected', 'value' => ! empty( $field['is_protected'] ) ? 1 : '', 'desc' => esc_html__( 'Password Protection', 'wpforms-lite' ), 'tooltip' => esc_html__( 'Check this option to password protect the uploaded files.', 'wpforms-lite' ), 'class' => 'wpforms-file-upload-password-restrictions', ], false ); $this->field_element( 'row', $field, [ 'slug' => 'password_restrictions', 'content' => $password, 'class' => $this->is_restricted( $field ) ? '' : 'wpforms-hidden', ] ); } /** * Add password label to the field. * * @since 1.9.8 * * @param array $field Field data and settings. */ private function add_password_label( array $field ) { $password_label = $this->field_element( 'label', $field, [ 'slug' => 'protection_password_label', 'value' => esc_html__( 'Password', 'wpforms-lite' ), 'tooltip' => esc_html__( 'Set a password to protect the uploaded files.', 'wpforms-lite' ), ], false ); $this->field_element( 'row', $field, [ 'slug' => 'protection_password_label', 'content' => $password_label, 'class' => $this->is_protected( $field ) ? '' : 'wpforms-hidden', ] ); } /** * Add password fields to the field. * * @since 1.9.8 * * @param array $field Field data and settings. */ private function add_password_fields( array $field ) { $password_field_row = $this->get_password_field( $field ); $password_confirm_field_row = $this->get_password_confirm_field( $field ); $password_columns = $this->field_element( 'row', $field, [ 'content' => $password_field_row . $password_confirm_field_row, 'class' => [ 'wpforms-field-options-columns', ], ], false ); $this->field_element( 'row', $field, [ 'slug' => 'protection_password_columns', 'content' => $password_columns, 'class' => $this->is_protected( $field ) ? '' : 'wpforms-hidden', ] ); } /** * Add password field to the field. * * @since 1.9.8 * * @param array $field Field data and settings. */ private function get_password_field( array $field ): string { $clean_button = $this->field_element( 'button', $field, [ 'slug' => 'password_restrictions_clean_button', 'value' => '<i class="fa fa-times-circle fa-lg"></i>', 'class' => [ 'wpforms-file-upload-password-clean', 'wpforms-hidden', ], 'data' => [ 'field-id' => $field['id'], ], 'attrs' => [ 'tabindex' => '-1', ], ], false ); $password_field = $this->field_element( 'text', $field, [ 'slug' => 'protection_password', 'value' => ! empty( $field['protection_password'] ) ? $field['protection_password'] : '', 'after' => esc_html__( 'Enter Password', 'wpforms-lite' ), 'type' => 'password', 'class' => 'wpforms-file-upload-password', 'attrs' => [ 'autocomplete' => 'new-password', ], ], false ); return $this->field_element( 'row', $field, [ 'slug' => 'protection_password', 'content' => $password_field . $clean_button, ], false ); } /** * Add password confirm field to the field. * * @since 1.9.8 * * @param array $field Field data and settings. */ private function get_password_confirm_field( array $field ): string { $password_confirm_field = $this->field_element( 'text', $field, [ 'slug' => 'protection_password_confirm', 'value' => ! empty( $field['protection_password_confirm'] ) ? $field['protection_password_confirm'] : '', 'after' => esc_html__( 'Confirm Password', 'wpforms-lite' ), 'type' => 'password', 'class' => 'wpforms-file-upload-password-confirm', ], false ); $password_confirm_field_error = $this->field_element( 'row', $field, [ 'slug' => 'protection_password_confirm_error', 'content' => esc_html__( 'Passwords do not match', 'wpforms-lite' ), 'class' => [ 'wpforms-hidden', 'wpforms-error', 'wpforms-error-message', ], ], false ); return $this->field_element( 'row', $field, [ 'slug' => 'protection_password_confirm', 'content' => $password_confirm_field . $password_confirm_field_error, ], false ); } /** * Check if the field has access restrictions enabled. * * @since 1.9.8 * * @param array $field Field data and settings. * * @return bool True if the field has access restrictions enabled, false otherwise. */ private function is_restricted( array $field ): bool { return ! empty( $field['is_restricted'] ); } /** * Check if the field has password protection enabled. * * @since 1.9.8 * * @param array $field Field data and settings. * * @return bool True if the field has password protection enabled, false otherwise. */ private function is_protected( array $field ): bool { return ! empty( $field['is_protected'] ); } } Fields/Traits/CameraTrait.php 0000644 00000025711 15252506741 0012142 0 ustar 00 <?php namespace WPForms\Forms\Fields\Traits; trait CameraTrait { /** * Add camera options to the field. * * @since 1.9.8 * * @param array $field Field data and settings. */ public function camera_options( array $field ): void { $this->add_camera_enabled_toggle( $field ); $this->add_camera_format_options( $field ); $this->add_camera_aspect_ratio_options( $field ); $this->add_camera_custom_ratio_options( $field ); $this->add_camera_time_limit_options( $field ); } /** * Add camera-enabled toggle. * * @since 1.9.8 * * @param array $field Field data and settings. */ private function add_camera_enabled_toggle( array $field ): void { // Check if this is a Camera field (not FileUpload with camera options). $is_camera_field = $this->type === 'camera'; $camera_enabled = $this->field_element( 'toggle', $field, [ 'slug' => 'camera_enabled', 'value' => $this->is_camera_enabled_for_field( $field ) ? 1 : '', 'desc' => esc_html__( 'Enable Camera', 'wpforms-lite' ), 'tooltip' => esc_html__( 'Check this option to enable the camera field.', 'wpforms-lite' ), 'class' => 'wpforms-file-upload-camera-enabled-toggle', ], false ); // Hide the toggle for the Camera field, show for FileUpload field. $row_class = [ 'wpforms-file-upload-camera-enabled-row' ]; if ( $is_camera_field ) { $row_class[] = 'wpforms-hidden'; } $this->field_element( 'row', $field, [ 'slug' => 'camera', 'content' => $camera_enabled, 'class' => $row_class, ] ); } /** * Add camera format options. * * @since 1.9.8 * * @param array $field Field data and settings. */ private function add_camera_format_options( array $field ): void { $format_label = $this->field_element( 'label', $field, [ 'slug' => 'camera_format', 'value' => esc_html__( 'Format', 'wpforms-lite' ), 'tooltip' => esc_html__( 'Select the camera format.', 'wpforms-lite' ), 'class' => 'wpforms-file-upload-camera-format-label', ], false ); $format_select = $this->field_element( 'select', $field, [ 'slug' => 'camera_format', 'value' => ! empty( $field['camera_format'] ) ? $field['camera_format'] : 'photo', 'options' => [ 'photo' => esc_html__( 'Photo', 'wpforms-lite' ), 'video' => esc_html__( 'Video', 'wpforms-lite' ), ], 'class' => 'wpforms-file-upload-camera-format-select', ], false ); // Check if the camera is enabled to determine visibility. $hidden_class = $this->is_camera_enabled_for_field( $field ) ? [] : [ 'wpforms-hidden' ]; $this->field_element( 'row', $field, [ 'slug' => 'camera_format', 'content' => $format_label . $format_select, 'class' => array_merge( [ 'wpforms-file-upload-camera-format' ], $hidden_class ), ] ); } /** * Add camera aspect ratio options. * * @since 1.9.8 * * @param array $field Field data and settings. */ private function add_camera_aspect_ratio_options( array $field ): void { $aspect_ratio_label = $this->field_element( 'label', $field, [ 'slug' => 'camera_aspect_ratio', 'value' => esc_html__( 'Aspect Ratio', 'wpforms-lite' ), 'tooltip' => esc_html__( 'Select the camera aspect ratio.', 'wpforms-lite' ), 'class' => 'wpforms-file-upload-camera-aspect-ratio-label', ], false ); // Build aspect ratio options - always include freeform. $aspect_ratio_options = [ 'original' => esc_html__( 'Original', 'wpforms-lite' ), 'custom' => esc_html__( 'Custom', 'wpforms-lite' ), 'freeform' => esc_html__( 'Freeform', 'wpforms-lite' ), 'landscape' => [ 'optgroup' => esc_html__( 'Landscape orientation', 'wpforms-lite' ), '16:9' => esc_html__( '16:9', 'wpforms-lite' ), '5:4' => esc_html__( '5:4', 'wpforms-lite' ), '3:2' => esc_html__( '3:2', 'wpforms-lite' ), ], 'portrait' => [ 'optgroup' => esc_html__( 'Portrait orientation', 'wpforms-lite' ), '9:16' => esc_html__( '9:16', 'wpforms-lite' ), '4:5' => esc_html__( '4:5', 'wpforms-lite' ), '2:3' => esc_html__( '2:3', 'wpforms-lite' ), ], ]; // Add class to hide freeform if a format is not a photo. $camera_format = ! empty( $field['camera_format'] ) ? $field['camera_format'] : 'photo'; $aspect_ratio_class = [ 'wpforms-file-upload-camera-aspect-ratio-select' ]; if ( $camera_format !== 'photo' ) { $aspect_ratio_class[] = 'wpforms-file-upload-camera-aspect-ratio-no-freeform'; } $aspect_ratio_select = $this->field_element( 'select', $field, [ 'slug' => 'camera_aspect_ratio', 'value' => ! empty( $field['camera_aspect_ratio'] ) ? $field['camera_aspect_ratio'] : 'original', 'options' => $aspect_ratio_options, 'class' => $aspect_ratio_class, ], false ); // Check if the camera is enabled to determine visibility. $hidden_class = $this->is_camera_enabled_for_field( $field ) ? [] : [ 'wpforms-hidden' ]; $this->field_element( 'row', $field, [ 'slug' => 'camera_aspect_ratio', 'content' => $aspect_ratio_label . $aspect_ratio_select, 'class' => array_merge( [ 'wpforms-file-upload-camera-aspect-ratio' ], $hidden_class ), ] ); } /** * Add camera custom ratio options. * * @since 1.9.8 * * @param array $field Field data and settings. */ private function add_camera_custom_ratio_options( array $field ): void { // Check if an aspect ratio is custom to determine visibility. $camera_aspect_ratio = ! empty( $field['camera_aspect_ratio'] ) ? $field['camera_aspect_ratio'] : 'original'; $custom_ratio_hidden_class = ( $this->is_camera_enabled_for_field( $field ) && $camera_aspect_ratio === 'custom' ) ? [] : [ 'wpforms-hidden' ]; // Ratio Width field. $ratio_width_field = '<div class="wpforms-file-upload-camera-ratio-width">' . $this->field_element( 'text', $field, [ 'slug' => 'camera_ratio_width', 'type' => 'number', 'value' => ! empty( $field['camera_ratio_width'] ) && $field['camera_ratio_width'] >= 1 ? $field['camera_ratio_width'] : '4', 'attrs' => [ 'min' => 1, 'step' => 1, ], 'after' => esc_html__( 'Ratio Width', 'wpforms-lite' ), 'class' => 'wpforms-file-upload-camera-ratio-width-input', ], false ) . '</div>'; // Ratio Height field. $ratio_height_field = '<div class="wpforms-file-upload-camera-ratio-height">' . $this->field_element( 'text', $field, [ 'slug' => 'camera_ratio_height', 'type' => 'number', 'value' => ! empty( $field['camera_ratio_height'] ) && $field['camera_ratio_height'] >= 1 ? $field['camera_ratio_height'] : '3', 'attrs' => [ 'min' => 1, 'step' => 1, ], 'after' => esc_html__( 'Ratio Height', 'wpforms-lite' ), 'class' => 'wpforms-file-upload-camera-ratio-height-input', ], false ) . '</div>'; $this->field_element( 'row', $field, [ 'slug' => 'camera_custom_ratio', 'content' => '<div class="wpforms-field-option-row-columns wpforms-field-option-row-columns-2 wpforms-file-upload-camera-ratio-columns">' . $ratio_width_field . $ratio_height_field . '</div>', 'class' => array_merge( [ 'wpforms-file-upload-camera-custom-ratio' ], $custom_ratio_hidden_class ), ] ); } /** * Add camera time limit options. * * @since 1.9.8 * * @param array $field Field data and settings. */ private function add_camera_time_limit_options( array $field ): void { $time_limit_label = $this->field_element( 'label', $field, [ 'slug' => 'camera_time_limit', 'value' => esc_html__( 'Time Limit', 'wpforms-lite' ), 'tooltip' => esc_html__( 'Set the time limit for camera recording.', 'wpforms-lite' ), 'class' => 'wpforms-file-upload-camera-time-limit-label', ], false ); // Minutes field. $minutes_field = '<div class="wpforms-file-upload-camera-time-limit-minutes">' . $this->field_element( 'text', $field, [ 'slug' => 'camera_time_limit_minutes', 'type' => 'number', 'value' => ! empty( $field['camera_time_limit_minutes'] ) && $field['camera_time_limit_minutes'] >= 0 ? $field['camera_time_limit_minutes'] : '1', 'attrs' => [ 'min' => 0, 'step' => 1, ], 'after' => esc_html__( 'Minutes', 'wpforms-lite' ), 'class' => 'wpforms-file-upload-camera-time-limit-minutes-input', ], false ) . '</div>'; // Seconds field. $seconds_field = '<div class="wpforms-file-upload-camera-time-limit-seconds">' . $this->field_element( 'text', $field, [ 'slug' => 'camera_time_limit_seconds', 'type' => 'number', 'value' => ! empty( $field['camera_time_limit_seconds'] ) && $field['camera_time_limit_seconds'] >= 0 && $field['camera_time_limit_seconds'] <= 59 ? $field['camera_time_limit_seconds'] : '30', 'attrs' => [ 'min' => 0, 'max' => 59, 'step' => 1, ], 'after' => esc_html__( 'Seconds', 'wpforms-lite' ), 'class' => 'wpforms-file-upload-camera-time-limit-seconds-input', ], false ) . '</div>'; // Check if a format is video to determine time limit visibility. $camera_format = ! empty( $field['camera_format'] ) ? $field['camera_format'] : 'photo'; $time_limit_hidden_class = ( $this->is_camera_enabled_for_field( $field ) && $camera_format === 'video' ) ? [] : [ 'wpforms-hidden' ]; $this->field_element( 'row', $field, [ 'slug' => 'camera_time_limit', 'content' => $time_limit_label . '<div class="wpforms-field-option-row-columns wpforms-field-option-row-columns-2 wpforms-file-upload-camera-time-limit-columns">' . $minutes_field . $seconds_field . '</div>', 'class' => array_merge( [ 'wpforms-file-upload-camera-time-limit' ], $time_limit_hidden_class ), ] ); } /** * Whether the provided form has a camera field. * * @since 1.9.8 * * @param array|mixed $form Form data. */ protected function is_camera_enabled( $form ): bool { if ( empty( $form['fields'] ) ) { return false; } foreach ( $form['fields'] as $field ) { if ( ! empty( $field['camera_enabled'] ) ) { return true; } } return false; } /** * Whether the field is a camera field or has camera enabled. * * @since 1.9.8 * * @param array $field Field data and settings. */ private function is_camera_enabled_for_field( array $field ): bool { return $this->type === 'camera' || ! empty( $field['camera_enabled'] ); } /** * Get the camera time limit in seconds. * * @since 1.9.8 * * @param array $field Field data. * * @return int Camera time limit in seconds. */ public function get_camera_time_limit( array $field ): int { $field = wp_parse_args( $field, [ 'camera_enabled' => false, 'camera_format' => '', 'camera_time_limit_minutes' => 0, 'camera_time_limit_seconds' => 0, ] ); if ( empty( $field['camera_enabled'] ) || $field['camera_format'] !== 'video' ) { return 0; } return absint( $field['camera_time_limit_minutes'] ) * 60 + absint( $field['camera_time_limit_seconds'] ); } } Fields/Traits/ContentInput.php 0000644 00000037015 15252506741 0012400 0 ustar 00 <?php namespace WPForms\Forms\Fields\Traits; use WP_Post; /** * Trait ContentInput. * * @since 1.9.4 */ trait ContentInput { /** * Translatable strings. * * @since 1.9.4 * * @var null|array Translatable strings. */ private static $translatable_strings; /** * Constructor overloader to register trait-specific hooks. * * @since 1.9.4 * * @param bool $init Pass false to allow shortcutting the whole initialization, if needed. */ public function __construct( $init = true ) { if ( ! $init ) { return; } $this->content_input_hooks(); parent::__construct( $init ); } /** * Register hooks. * * @since 1.9.4 */ private function content_input_hooks(): void { add_action( 'wpforms_builder_enqueues', [ $this, 'builder_enqueues' ] ); add_action( 'wpforms_builder_print_footer_scripts', [ $this, 'content_editor_tools_template' ] ); add_filter( 'wpforms_builder_field_option_class', [ $this, 'builder_field_option_class' ], 10, 2 ); add_filter( 'wpforms_builder_strings', [ $this, 'content_builder_strings' ], 10, 2 ); add_filter( 'editor_stylesheets', [ $this, 'editor_stylesheets' ] ); add_filter( 'media_view_strings', [ $this, 'edit_media_view_strings' ], 10, 2 ); add_filter( 'teeny_mce_buttons', [ $this, 'teeny_mce_buttons' ], 10, 2 ); } /** * Content field option. * * @since 1.9.4 * * @param array $field Field data and settings. */ private function field_option_content( array $field ): void { $value = ( isset( $field['content'] ) && ! wpforms_is_empty_string( $field['content'] ) ) ? wp_kses( $field['content'], $this->get_allowed_html_tags() ) : ''; $output = $this->field_element( 'row', $field, [ 'slug' => 'content', 'content' => $this->get_content_editor( $value, $field ), ], false ); $output .= wpforms_render( 'fields/content/action-buttons', [ 'id' => $field['id'], 'preview' => $this->get_input_string( 'preview' ), 'expand' => $this->get_input_string( 'expand' ), ], true ); printf( '<div class="wpforms-expandable-editor">%s</div><div class="wpforms-expandable-editor-clear"></div>', $output ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped } /** * Add class name to the field option top element. * * @since 1.9.4 * * @param string|mixed $css_class CSS classes. * @param array $field Field data. * * @return string */ public function builder_field_option_class( $css_class, $field ): string { $css_class = (string) $css_class; return $this->type === $field['type'] ? $css_class . ' wpforms-field-has-tinymce' : $css_class; } /** * Localized strings for `content-field` JS script. * * @since 1.9.4 * * @param array|mixed $strings Localized strings. * @param array $form The form element. * * @return array * @noinspection PhpUnusedParameterInspection */ public function content_builder_strings( $strings, $form ): array { // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundAfterLastUsed $strings = (array) $strings; $strings['content_field'] = [ 'collapse' => wp_strip_all_tags( $this->get_input_string( 'collapse' ) ), 'expand' => wp_strip_all_tags( $this->get_input_string( 'expand' ) ), 'editor_default_value' => wp_kses( $this->get_input_string( 'editor_default_value' ), $this->get_allowed_html_tags() ), 'content_editor_plugins' => $this->content_editor_plugins(), 'content_editor_toolbar' => $this->content_editor_toolbar(), 'content_editor_css_url' => $this->content_css_url(), 'editor_height' => $this->get_editor_height(), 'allowed_html' => array_keys( $this->get_allowed_html_tags() ), 'invalid_elements' => $this->get_invalid_elements(), 'quicktags_buttons' => $this->get_quicktags_buttons(), 'body_class' => $this->get_editor_body_class(), ]; return $this->add_supported_field_type( $strings, $this->type ); } /** * Add editor stylesheet. * * @since 1.9.4 * * @param array|mixed $stylesheets Editor stylesheets. * * @return array */ public function editor_stylesheets( $stylesheets ): array { $stylesheets = (array) $stylesheets; if ( wpforms_is_admin_page( 'builder' ) ) { $stylesheets[] = $this->content_css_url(); } return $stylesheets; } /** * Edit some media view strings to reference a form instead of a page/post. * * @since 1.9.4 * * @param array|mixed $strings List of media view strings. * @param WP_Post $post Post object. * * @return array Modified media view strings. * @noinspection SqlResolve * @noinspection PhpMissingParamTypeInspection * @noinspection PhpUnusedParameterInspection */ public function edit_media_view_strings( $strings, $post ): array { // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundAfterLastUsed $strings = (array) $strings; if ( wpforms_is_admin_page( 'builder' ) ) { $strings['insertIntoPost'] = esc_html__( /** @lang text */ 'Insert into form', 'wpforms-lite' ); $strings['uploadedToThisPost'] = esc_html__( 'Uploaded to this form', 'wpforms-lite' ); } return $strings; } /** * Remove fullscreen button if this is other tinymce editor instance than content field editor. * * @since 1.9.4 * * @param array|mixed $buttons Array of editor buttons. * @param string $editor_id Editor textarea ID. * * @return array */ public function teeny_mce_buttons( $buttons, $editor_id ): array { $buttons = (array) $buttons; $is_other_editor = strpos( $editor_id, 'wpforms_panel_' ) === 0 || $editor_id === 'entry_note'; $key = array_search( 'fullscreen', $buttons, true ); if ( $is_other_editor && $key !== false ) { unset( $buttons[ $key ] ); } return $buttons; } /** * Get default content editor plugins. * * @since 1.9.4 * * @return array Plugins array. */ private function content_editor_plugins(): array { $plugins = [ 'charmap', 'colorpicker', 'hr', 'link', 'image', 'lists', 'paste', 'tabfocus', 'textcolor', 'wordpress', 'wpemoji', 'wptextpattern', 'wpeditimage', ]; /** * Get content editor plugins filter. * * @since 1.7.8 * * @param array $plugins Plugins array. */ return (array) apply_filters( 'wpforms_builder_content_input_get_content_editor_plugins', $plugins ); } /** * Get default content editor toolbar. * * @since 1.9.4 * * @return array Toolbar buttons array. */ private function content_editor_toolbar(): array { $toolbar = [ 'formatselect', 'bold', 'italic', 'underline', 'strikethrough', 'forecolor', 'link', 'bullist', 'numlist', 'blockquote', 'alignleft', 'aligncenter', 'alignright', ]; /** * Get content editor toolbar buttons filter. * * @since 1.7.8 * * @param array $toolbar Toolbar buttons array. */ return (array) apply_filters( 'wpforms_builder_content_input_get_content_editor_toolbar', $toolbar ); } /** * Enqueue wpforms-content-field script. * * @since 1.9.4 * * @param string $view Current view. * * @noinspection PhpUnusedParameterInspection */ public function builder_enqueues( $view ): void { // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.Found $wp_min = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min'; // Enqueue editor styles explicitly. Hack for broken styles when the Content field is deleted and Settings > Confirmation editor get broken. // phpcs:ignore WordPress.WP.EnqueuedResourceParameters.MissingVersion wp_enqueue_style( 'wpforms-editor-styles', includes_url( "css/editor$wp_min.css" ) ); } /** * Content editor tools template. * * @since 1.9.4 */ public function content_editor_tools_template(): void { ?> <script type="text/html" id="tmpl-wpforms-content-editor-tools"> <div id="wp-wpforms-field-{{data.optionId}}-content-editor-tools" class="wp-editor-tools hide-if-no-js"> <div id="wp-wpforms-field-{{data.optionId}}-content-media-buttons" class="wp-media-buttons"> <button type="button" id="insert-media-button" class="button insert-media add_media" data-editor="wpforms-field-{{data.optionId}}-content"> <span class="wp-media-buttons-icon"></span> <?php esc_html_e( 'Add Media', 'wpforms-lite' ); ?> </button> </div> <div class="wp-editor-tabs"> <button type="button" id="wpforms-field-{{data.optionId}}-content-tmce" class="wp-switch-editor switch-tmce" data-wp-editor-id="wpforms-field-{{data.optionId}}-content"> <?php esc_html_e( 'Visual', 'wpforms-lite' ); ?> </button> <button type="button" id="wpforms-field-{{data.optionId}}-content-html" class="wp-switch-editor switch-html" data-wp-editor-id="wpforms-field-{{data.optionId}}-content"> <?php esc_html_e( 'Text', 'wpforms-lite' ); ?> </button> </div> </div> </script> <?php } /** * Register types in JS localization to use in WPFormsContentField. * * @since 1.9.4 * * @param array $strings Localized strings. * @param string $type Field type. * * @return array */ private function add_supported_field_type( $strings, $type ): array { $other_supported_field_types = $strings['content_input']['supported_field_types'] ?? []; $strings['content_input'] = [ 'supported_field_types' => array_merge( $other_supported_field_types, [ $type ] ), ]; return $strings; } /** * Get translatable string. * * @since 1.9.4 * * @param string $key String key. * * @return string */ private function get_input_string( $key ): string { if ( ! self::$translatable_strings ) { self::$translatable_strings = [ 'editor_default_value' => __( '<h4>Add Text and Images to Your Form With Ease</h4> <p>To get started, replace this text with your own.</p>', 'wpforms-lite' ), 'expand' => __( 'Expand Editor', 'wpforms-lite' ), 'collapse' => __( 'Collapse Editor', 'wpforms-lite' ), 'preview' => __( 'Update Preview', 'wpforms-lite' ), ]; } return self::$translatable_strings[ $key ] ?? ''; } /** * Show field preview in the right builder panel. * * @since 1.9.4 * * @param array $field Field data. */ private function content_input_preview( $field ): void { $content = $field['content'] ?? $this->get_input_string( 'editor_default_value' ); ?> <div class="wpforms-field-content-preview"> <?php echo wp_kses( $this->do_caption_shortcode( wpautop( $content ) ), $this->get_allowed_html_tags() ); ?> <div class="wpforms-field-content-preview-end"></div> </div> <?php } /** * Check if shortcode is [caption] and if not, return processed content string. * * @since 1.9.4 * * @param false|string $value Short-circuit return value. Either false or the value to replace the shortcode with. * @param string $tag Shortcode name. * @param array|string $attr Shortcode attributes array or empty string. * @param array $m Regular expression match array. * * @return false|string * @noinspection PhpUnusedParameterInspection * @noinspection PhpMissingParamTypeInspection */ public function short_circuit_shortcodes( $value, $tag, $attr, $m ) { return $tag !== 'caption' ? $m[0] : false; } /** * Check if shortcode is [caption] and if not, short-circuit processing the shortcode. * * @since 1.9.4 * * @param string $content Editor content. * * @return string */ protected function do_caption_shortcode( $content ): string { /** * Check if user allowed executing all shortcodes on content field value. * * @since 1.7.8 * * @param bool $bool Boolean if shortcodes should be executed. */ if ( apply_filters( 'wpforms_content_input_value_do_shortcode', false ) && ! wpforms_is_admin_page( 'builder' ) ) { return do_shortcode( $content ); } add_filter( 'pre_do_shortcode_tag', [ $this, 'short_circuit_shortcodes' ], 10, 4 ); $content = do_shortcode( $content ); remove_filter( 'pre_do_shortcode_tag', [ $this, 'short_circuit_shortcodes' ] ); return $content; } /** * Get TinyMCE editor for content field. * * @since 1.9.4 * * @param string $value Field value. * @param array $field Field data. * * @return string */ private function get_content_editor( $value, $field ): string { /* Heads up, if you are going to edit editor settings, bear in mind editor is instantiated in two places: - PHP instance in \WPForms\Admin\Builder\Traits\ContentInput::get_content_editor - JS instance in WPForms.Admin.Builder.ContentField.initTinyMCE */ $settings = [ 'media_buttons' => true, 'drag_drop_upload' => true, 'textarea_name' => "fields[{$field['id']}][content]", 'editor_height' => $this->get_editor_height(), 'editor_class' => ! empty( $field['required'] ) ? 'wpforms-field-required' : '', 'tinymce' => [ 'init_instance_callback' => $this->is_disabled_field ? '' : 'wpformsContentFieldTinyMCECallback', 'plugins' => implode( ',', $this->content_editor_plugins() ), 'toolbar1' => implode( ',', $this->content_editor_toolbar() ), 'invalid_elements' => $this->get_invalid_elements(), 'relative_urls' => false, 'remove_script_host' => false, 'object_resizing' => false, 'body_class' => $this->get_editor_body_class(), ], 'quicktags' => [ 'buttons' => $this->get_quicktags_buttons(), ], ]; ob_start(); wp_editor( $value, 'wpforms-field-option-' . $field['id'] . '-content', $settings ); return ob_get_clean(); } /** * Get invalid HTML in content editor. * * @since 1.9.4 * * @return string Invalid HTML elements. */ private function get_invalid_elements(): string { return 'form,input,textarea,select,option,script,embed,iframe'; } /** * Get the list of the `quicktags` buttons. * * @since 1.9.4 * * @return string Quicktags buttons. */ private function get_quicktags_buttons(): string { $quicktag_buttons = [ 'strong', 'em', 'block', 'del', 'ins', 'img', 'ul', 'ol', 'li', 'code', 'link', 'close', ]; /** * Get the list of the `quicktags` buttons filter. * * @since 1.7.8 * * @param string $quicktags_buttons Comma separated list of quicktags buttons. */ return implode( ',', apply_filters( 'wpforms_builder_content_input_get_quicktags_buttons', $quicktag_buttons ) ); } /** * Get content CSS url. * * @since 1.9.4 * * @return string */ private function content_css_url(): string { $min = wpforms_get_min_suffix(); return WPFORMS_PLUGIN_URL . "assets/css/builder/content-editor{$min}.css"; } /** * Get content editor height. * * @since 1.9.4 * * @retun int Editor textarea height. */ private function get_editor_height(): int { /** * Get content editor height filter. * * @since 1.7.8 * * @param int $height Editor textarea height. */ return (int) apply_filters( 'wpforms_builder_content_input_get_editor_height', 204 ); } /** * Get allowed HTML tags for Content Input Field. * * @since 1.9.4 * * @return array */ protected function get_allowed_html_tags(): array { /** * Filter allowed HTML tags in the content field input. * * @since 1.7.8 * * @param array $allowed_tags Allowed tags. */ return (array) apply_filters( 'wpforms_builder_content_input_get_allowed_html_tags', wpforms_get_allowed_html_tags_for_richtext_field() ); } /** * Get editor body class. * * @since 1.9.4 * * @return string */ private function get_editor_body_class(): string { return 'wpforms-content-field-editor-body'; } } Fields/Traits/FileMethodsTrait.php 0000644 00000004674 15252506741 0013162 0 ustar 00 <?php namespace WPForms\Forms\Fields\Traits; /** * File methods trait. * * @since 1.9.8 */ trait FileMethodsTrait { /** * File extensions that are not allowed. * * @since 1.9.8 * * @var array */ private $denylist = [ 'ade', 'adp', 'app', 'asp', 'bas', 'bat', 'cer', 'cgi', 'chm', 'cmd', 'com', 'cpl', 'crt', 'csh', 'csr', 'dll', 'drv', 'exe', 'fxp', 'flv', 'hlp', 'hta', 'htaccess', 'htm', 'html', 'htpasswd', 'inf', 'ins', 'isp', 'jar', 'js', 'jse', 'jsp', 'ksh', 'lnk', 'mdb', 'mde', 'mdt', 'mdw', 'msc', 'msi', 'msp', 'mst', 'ops', 'pcd', 'php', 'pif', 'pl', 'prg', 'ps1', 'ps2', 'py', 'rb', 'reg', 'scr', 'sct', 'sh', 'shb', 'shs', 'sys', 'swf', 'tmp', 'torrent', 'url', 'vb', 'vbe', 'vbs', 'vbscript', 'wsc', 'wsf', 'wsf', 'wsh', 'dfxp', 'onetmp', ]; /** * Get all allowed extensions. * Check against user-entered extensions. * * @since 1.9.8 * * @return array */ protected function get_extensions(): array { // Allowed file extensions by default. $default_extensions = $this->get_default_extensions(); // Allowed file extensions. $extensions = ! empty( $this->field_data['extensions'] ) ? explode( ',', $this->field_data['extensions'] ) : $default_extensions; return wpforms_chain( $extensions ) ->map( static function ( $ext ) { return strtolower( preg_replace( '/[^A-Za-z0-9_-]/', '', $ext ) ); } ) ->array_filter() ->array_intersect( $default_extensions ) ->value(); } /** * Determine the max-allowed file size in bytes as per field options. * * @since 1.9.8 * * @return int Number of bytes allowed. */ public function max_file_size(): int { if ( ! empty( $this->field_data['max_size'] ) ) { // Strip any suffix provided (e.g., M, MB, etc.), which leaves us with the raw MB value. $max_size = preg_replace( '/[^0-9.]/', '', $this->field_data['max_size'] ); return wpforms_size_to_bytes( $max_size . 'M' ); } return (int) wpforms_max_upload( true ); } /** * Get default extensions supported by WordPress * without those that we manually denylist. * * @since 1.9.8 * * @return array */ protected function get_default_extensions(): array { return wpforms_chain( get_allowed_mime_types() ) ->array_keys() ->implode( '|' ) ->explode( '|' ) ->array_diff( $this->denylist ) ->value(); } } Fields/Traits/FileEntriesEditTrait.php 0000644 00000012275 15252506741 0013772 0 ustar 00 <?php namespace WPForms\Forms\Fields\Traits; /** * File Entries Edit Trait. * * Contains common methods for editing file upload entries. * * @since 1.9.8 */ trait FileEntriesEditTrait { /** * Enqueues for the Edit Entry page. * * @since 1.9.8 * * @noinspection ReturnTypeCanBeDeclaredInspection */ public function enqueues() { wp_enqueue_style( 'tooltipster', WPFORMS_PLUGIN_URL . 'assets/lib/jquery.tooltipster/jquery.tooltipster.min.css', null, '4.2.6' ); wp_enqueue_script( 'tooltipster', WPFORMS_PLUGIN_URL . 'assets/lib/jquery.tooltipster/jquery.tooltipster.min.js', [ 'jquery' ], '4.2.6', true ); } /** * Display the field on the Edit Entry page. * * @since 1.9.8 * * @param array $entry_field Entry field data. * @param array $field Field data and settings. * @param array $form_data Form data and settings. * * @noinspection ReturnTypeCanBeDeclaredInspection */ public function field_display( $entry_field, $field, $form_data ) { // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundAfterLastUsed $html = ''; $is_media_file = isset( $field['media_library'] ); if ( method_exists( $this->field_object, 'is_modern_upload' ) && $this->field_object::is_modern_upload( $entry_field ) ) { // Check if there are any files in value_raw. if ( ! empty( $entry_field['value_raw'] ) && is_array( $entry_field['value_raw'] ) ) { foreach ( $entry_field['value_raw'] as $key => $field_data ) { $html .= $this->get_file_item_html( $field_data, $is_media_file, $key ); } } } else { $html .= $this->get_file_item_html( $entry_field, $is_media_file ); } echo $html; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped } /** * Get HTML for the file item. * * @since 1.9.8 * * @param array $field_data Field data. * @param bool $is_media_file Is WP media. * @param int|string $key Key for multiple items. * * @return string * @noinspection HtmlUnknownTarget */ private function get_file_item_html( array $field_data, bool $is_media_file, $key = 0 ): string { $html = '<div class="file-entry">'; $html .= $this->field_object->file_icon_html( $field_data ); $html .= sprintf( '<a href="%s" target="_blank" rel="noopener noreferrer">%s</a>', esc_url( $this->field_object->get_file_url( $field_data ) ), esc_html( $this->field_object->get_file_name( $field_data ) ) ); $html .= sprintf( '<input type="hidden" name="wpforms[fields][%d][]" value="%s"/>', esc_attr( $field_data['id'] ), esc_attr( $key ) ); if ( $is_media_file ) { $title = sprintf( wp_kses( /* translators: %s - link to the Media Library. */ __( 'Please use the default <a href="%s">WordPress Media</a> interface to remove this file.', 'wpforms-lite' ), [ 'a' => [ 'href' => [], ], ] ), esc_url( admin_url( 'upload.php' ) ) ); $html .= sprintf( '<i class="fa fa-question-circle wpforms-help-tooltip" title="%s"></i>', esc_html( $title ) ); } else { $html .= $this->remove_button_html(); } $html .= '</div>'; return $html; } /** * Get the remove button HTML. * * @since 1.9.8 * * @return string */ private function remove_button_html(): string { return '<a class="delete button-link-delete" href=""><span class="dashicons dashicons-trash wpforms-trash-icon"></span></a>'; } /** * Format and sanitize a field while processing entry editing. * * @since 1.9.8 * * @param int $field_id Field ID. * @param mixed $field_submit Field value that was submitted. * @param mixed $field_data Existing field data. * @param array $form_data Form data and settings. * * @noinspection ReturnTypeCanBeDeclaredInspection*/ public function format( $field_id, $field_submit, $field_data, $form_data ) { // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundAfterLastUsed if ( method_exists( $this->field_object, 'is_modern_upload' ) && ! $this->field_object::is_modern_upload( $field_data ) ) { if ( ! is_array( $field_submit ) ) { $field_data['value'] = ''; $field_data['file_original'] = ''; $field_data['ext'] = ''; } wpforms()->obj( 'process' )->fields[ $field_id ] = $field_data; return; } if ( ! isset( $field_data['value_raw'] ) || ! is_array( $field_submit ) ) { $field_data['value_raw'] = ''; $field_data['value'] = ''; wpforms()->obj( 'process' )->fields[ $field_id ] = $field_data; return; } $field_data['value_raw'] = array_intersect_key( $field_data['value_raw'], array_combine( $field_submit, $field_submit ) ); $field_data['value'] = implode( "\n", array_column( $field_data['value_raw'], 'value' ) ); wpforms()->obj( 'process' )->fields[ $field_id ] = $field_data; } /** * Skip validation. * * @since 1.9.8 * * @param int $field_id Field ID. * @param mixed $field_submit Field value that was submitted. * @param mixed $field_data Existing field data. * @param array $form_data Form data and settings. * * @noinspection ReturnTypeCanBeDeclaredInspection*/ public function validate( $field_id, $field_submit, $field_data, $form_data ) { } } Fields/Traits/NumberField.php 0000644 00000016666 15252506741 0012153 0 ustar 00 <?php namespace WPForms\Forms\Fields\Traits; /** * Numbers and Number Slider Field trait, designed for use with `WPForms_Field`. * * @since 1.9.4 */ trait NumberField { /** * Enqueues required scripts for the form builder. * * @since 1.9.4 */ private function number_hooks() { add_action( 'wpforms_builder_enqueues', [ $this, 'number_builder_enqueues' ] ); } /** * Enqueue wpforms-number-field script. * * @since 1.9.4 * * @param string $view Current view. * * @noinspection PhpUnusedParameterInspection, PhpUnnecessaryCurlyVarSyntaxInspection */ public function number_builder_enqueues( $view ) { // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.Found $min = wpforms_get_min_suffix(); wp_enqueue_script( 'wpforms-number-field', WPFORMS_PLUGIN_URL . "assets/js/admin/builder/fields/numbers{$min}.js", [ 'wpforms-builder', 'wpforms-utils' ], WPFORMS_VERSION, false ); } /** * Helper function to create field option elements. * * Field option elements are pieces that help create a field option. * They are used to quickly build field options. * * This method is intended to be used within classes that implement or extend * the `WPForms_Field` functionality. * * @since 1.9.4 * * @param string $option Field option to render. * @param array $field Field data and settings. * @param array $args Field preview arguments. * @param bool $echo_output Print or return the value. Print by default. * * @return mixed echo or return string */ abstract public function field_element( $option, $field, $args = [], $echo_output = true ); /** * Helper function to create a number field option element. * * @since 1.9.4 * * @param array $field Field data and settings. * @param array $args Field preview arguments. * @param bool $echo_output Whether to print the generated output. Default true. * * @return string */ private function field_number_element( $field, $args = [], $echo_output = true ) { //phpcs:ignore Generic.Metrics.CyclomaticComplexity.MaxExceeded if ( ! isset( $args['slug'], $args['label'] ) ) { return ''; } $slug = $args['slug']; $label = $args['label']; $value = $field[ $slug ] ?? $args['value'] ?? ''; $attrs = []; if ( isset( $args['min'] ) && is_numeric( $args['min'] ) ) { $attrs['min'] = (float) $args['min']; } if ( isset( $args['max'] ) && is_numeric( $args['max'] ) ) { $attrs['max'] = (float) $args['max']; } if ( isset( $args['step'] ) && ( $args['step'] === 'any' || ( is_numeric( $args['step'] ) && $args['step'] > 0 ) ) ) { $attrs['step'] = (string) $args['step']; } $number_label_markup = $this->field_element( 'label', $field, [ 'slug' => $slug, 'value' => $label, 'tooltip' => $args['tooltip'] ?? '', ], false ); $number_input_markup = $this->field_element( 'text', $field, [ 'type' => 'number', 'slug' => $slug, 'value' => is_numeric( $value ) ? (float) $value : '', 'attrs' => $attrs, 'class' => $args['class'] ?? '', ], false ); $output = $this->field_element( 'row', $field, [ 'slug' => $slug, 'content' => $number_label_markup . $number_input_markup, ], false ); if ( ! $output ) { return ''; } if ( $echo_output ) { // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped echo $output; } return $output; } /** * Helper function to create `min_max` field option markup. * * @since 1.9.4 * * @param array $field Field data and settings. * @param array $args Field preview arguments. * @param bool $echo_output Print or return the value. Print by default. * * @return string */ private function field_number_option_min_max( $field, $args, $echo_output = true ) { $class = $args['class'] ?? 'number_min_max'; $range_label_markup = $this->field_element( 'label', $field, [ 'slug' => 'min', 'value' => $args['label'] ?? esc_html__( 'Range', 'wpforms-lite' ), 'tooltip' => $args['tooltip'] ?? esc_html__( 'Define the minimum and the maximum values for the field.', 'wpforms-lite' ), ], false ); $min_value = $field['min'] ?? null; $input_min_args = [ 'type' => 'number', 'slug' => 'min', 'value' => is_numeric( $min_value ) ? (float) $min_value : '', 'class' => $class . '-min', 'attrs' => [ 'step' => 'any', ], ]; $range_input_min_markup = $this->field_element( 'text', $field, $input_min_args, false ); $max_value = $field['max'] ?? null; $input_max_args = [ 'type' => 'number', 'slug' => 'max', 'value' => is_numeric( $max_value ) ? (float) $max_value : '', 'class' => $class . '-max', 'attrs' => [ 'step' => 'any', ], ]; $range_input_max_markup = $this->field_element( 'text', $field, $input_max_args, false ); return $this->field_element( 'row', $field, [ 'slug' => 'min_max', 'content' => $range_label_markup . sprintf( '<div class="wpforms-input-row"> <div class="minimum">%s<label for="wpforms-field-option-%d-min" class="sub-label">%s</label></div> <div class="maximum">%s<label for="wpforms-field-option-%d-max" class="sub-label">%s</label></div> </div>', $range_input_min_markup, (int) $field['id'], esc_html__( 'Minimum', 'wpforms-lite' ), $range_input_max_markup, (int) $field['id'], esc_html__( 'Maximum', 'wpforms-lite' ) ), ], $echo_output ); } /** * Helper function to create `default_value` field option markup. * * @since 1.9.4 * * @param array $field Field data and settings. * @param array $args Field preview arguments. * @param bool $echo_output Print or return the value. Print by default. * * @return string */ private function field_number_option_default_value( $field, $args, $echo_output = true ) { $default_value_args = [ 'slug' => 'default_value', 'label' => esc_html__( 'Default Value', 'wpforms-lite' ), 'tooltip' => esc_html__( 'Enter a default value for this field.', 'wpforms-lite' ), 'class' => $args['class'] ?? '', 'value' => $args['value'] ?? '', 'min' => $field['min'] ?? '', 'max' => $field['max'] ?? '', 'step' => $field['step'] ?? '', ]; return $this->field_number_element( $field, $default_value_args, $echo_output ); } /** * Helper function to create `step` field option markup. * * @since 1.9.4 * * @param array $field Field data and settings. * @param array $args Field preview arguments. * @param bool $echo_output Print or return the value. Print by default. * * @return string */ private function field_number_option_step( $field, $args, $echo_output = true ) { $step_args = [ 'slug' => 'step', 'label' => esc_html__( 'Increment', 'wpforms-lite' ), 'tooltip' => $args['tooltip'] ?? esc_html__( 'Determines the increment between selectable values on the field.', 'wpforms-lite' ), 'class' => $args['class'] ?? '', 'min' => 0, 'step' => 'any', 'value' => 1, ]; $min = is_numeric( $field['min'] ?? null ) ? (float) $field['min'] : null; $max = is_numeric( $field['max'] ?? null ) ? (float) $field['max'] : null; if ( ! is_null( $min ) && ! is_null( $max ) ) { $step_args['max'] = $max - $min; } return $this->field_number_element( $field, $step_args, $echo_output ); } } Fields/Traits/ProField.php 0000644 00000041212 15252506741 0011444 0 ustar 00 <?php namespace WPForms\Forms\Fields\Traits; use WPForms\Admin\Education\Helpers; /** * Trait ProField. * * Mostly educational things for the Pro field in the Lite plugin. * * @since 1.9.4 */ trait ProField { /** * Is it the Pro plugin? * * @since 1.9.4 * * @var boolean */ protected $is_pro = false; /** * Whether the field is a Pro field. * * @since 1.9.4 * * @var boolean */ protected $is_pro_field = true; /** * Addon slug. * * @since 1.9.4 * * @var string */ protected $addon_slug; /** * Whether the Addon is initialized. * * @since 1.9.4 * * @var boolean */ protected $is_addon_initialized = false; /** * Whether the field is disabled. * * @since 1.9.4 * * @var boolean */ protected $is_disabled_field = true; /** * Addon educational data. * * @since 1.9.4 * * @var array */ protected $addon_edu_data = []; /** * Init Pro Field. * * @since 1.9.4 */ private function init_pro_field(): void { $this->is_pro = wpforms()->is_pro(); $this->is_addon_initialized = ! empty( $this->addon_slug ) && wpforms_is_addon_initialized( $this->addon_slug ); $this->is_disabled_field = $this->is_disabled_field(); // Add hooks. add_filter( 'admin_init', [ $this, 'admin_init_pro_field' ] ); add_filter( 'wpforms_builder_field_option_class', [ $this, 'filter_field_option_class' ], 10, 2 ); add_filter( "wpforms_admin_builder_ajax_save_form_field_$this->type", [ $this, 'filter_save_form_field_data' ], 10, 3 ); add_filter( 'wpforms_field_data', [ $this, 'filter_frontend_field_data' ], PHP_INT_MAX, 2 ); add_filter( 'wpforms_helpers_form_pro_fields', [ $this, 'filter_form_pro_fields' ], PHP_INT_MAX, 2 ); add_filter( 'wpforms_helpers_form_addons_edu_data', [ $this, 'filter_form_addons_edu_data' ], PHP_INT_MAX, 2 ); add_filter( 'wpforms_field_preview_display_duplicate_button', [ $this, 'filter_field_preview_display_duplicate_button' ], 10, 2 ); add_filter( 'wpforms_field_preview_class', [ $this, 'filter_field_preview_class' ], 10, 2 ); add_filter( 'wpforms_entry_save_data', [ $this, 'filter_entry_save_data' ], 10, 3 ); add_filter( 'wpforms_pro_admin_entries_table_facades_columns_get_field_columns_forbidden_fields', [ $this, 'filter_field_columns_forbidden_fields' ], 10, 2 ); add_filter( 'wpforms_pro_admin_entries_export_configuration', [ $this, 'filter_entries_export_configuration' ] ); add_filter( "wpforms_pro_admin_entries_edit_is_field_displayable_$this->type", [ $this, 'filter_is_field_displayable' ], 10, 3 ); } /** * Init Pro field on the ` admin_init ` hook. * * @since 1.9.4 */ public function admin_init_pro_field(): void { $this->addon_edu_data = $this->get_field_addon_edu_data(); } /** * Get the Pro field options tab CSS class. * * @since 1.9.4 * * @param string|mixed $css_class CSS class. * @param array $field Field data. * * @return string * @noinspection PhpMissingParamTypeInspection */ public function filter_field_option_class( $css_class, $field ): string { $css_class = (string) $css_class; if ( $field['type'] !== $this->type ) { return $css_class; } $css_class .= empty( $this->is_disabled_field ) ? '' : ' wpforms-field-is-pro'; return trim( $css_class ); } /** * Filter field data before saving the form. * * @since 1.9.4 * * @param array $field_data Field data. * @param array $form_data Forms data. * @param array $saved_form_data Saved form data. * * @noinspection PhpMissingParamTypeInspection * @noinspection PhpUnusedParameterInspection */ public function filter_save_form_field_data( $field_data, $form_data, $saved_form_data ) { if ( empty( $this->is_disabled_field ) ) { return $field_data; } $field_id = $field_data['id'] ?? ''; // Prevent changes in the field data if it's a Pro field in Lite. // The settings are disabled in the Form Builder, but users can still hijack the data. // Therefore, return the saved field data if it exists. return $saved_form_data['fields'][ $field_id ] ?? $field_data; } /** * Filter form pro fields array. * * @since 1.9.4 * * @param array|mixed $pro_fields Pro fields array. * @param array $field Field data. */ public function filter_form_pro_fields( $pro_fields, array $field ): array { $pro_fields = is_array( $pro_fields ) ? $pro_fields : []; if ( isset( $field['type'] ) && $field['type'] === $this->type ) { $pro_fields[] = $field; } return $pro_fields; } /** * Filter the form addons educational data array. * * @since 1.9.4 * * @param array|mixed $addons_edu_data Addons educational data. * @param array $field Field data. */ public function filter_form_addons_edu_data( $addons_edu_data, array $field ): array { $addons_edu_data = is_array( $addons_edu_data ) ? $addons_edu_data : []; if ( ! isset( $field['type'] ) || $field['type'] !== $this->type || empty( $this->addon_edu_data ) ) { return $addons_edu_data; } $addon = $this->addon_edu_data['slug'] ?? ''; $addons_edu_data[ $addon ] = $this->addon_edu_data; return $addons_edu_data; } /** * Get the Pro field options notice. * * @since 1.9.4 * * @noinspection HtmlUnknownAttribute */ private function get_field_options_notice(): string { if ( empty( $this->is_disabled_field ) ) { return ''; } [ $name, $title, $content, $button_label, $button_utm ] = $this->get_field_options_notice_texts(); $action = $this->addon_edu_data['action'] ?? 'upgrade'; $button_class = 'education-action-button'; $button_attr = ''; if ( $action !== 'upgrade' ) { $button_class = 'education-modal'; $button_attr = sprintf( 'data-nonce="%1$s" data-path="%2$s" data-url="%3$s" data-message="%4$s" data-field-type="%5$s" data-name="%6$s"', esc_attr( wp_create_nonce( 'wpforms-admin' ) ), $this->addon_edu_data['path'] ?? '', $this->addon_edu_data['url'] ?? '', $action === 'incompatible' ? $this->addon_edu_data['message'] : '', esc_attr( $this->type ), esc_attr( $name ) ); } return sprintf( '<div class="wpforms-field-option-field-title-notice"> <div class="wpforms-alert-info wpforms-alert wpforms-educational-alert"> <h4>%1$s</h4> <p>%2$s</p> <button class="wpforms-btn wpforms-btn-sm wpforms-btn-blue %3$s" data-action="%4$s" %6$s data-license="%7$s" data-utm-content="%8$s">%5$s</button> </div> </div>', $title, esc_html( $content ), esc_attr( $button_class ), esc_attr( $action ), esc_html( $button_label ), $button_attr, esc_attr( $this->addon_edu_data['license_level'] ?? 'pro' ), esc_attr( $button_utm ) ); } /** * Get the Pro field options notice texts. * * @since 1.9.4 */ private function get_field_options_notice_texts(): array { $action = $this->addon_edu_data['action'] ?? 'upgrade'; $addon_name = $this->addon_edu_data['title'] ?? ''; $name = $this->name; $titles = [ 'upgrade' => sprintf( /* translators: %1$s - Field name. */ esc_html__( '%1$s is a Pro Feature', 'wpforms-lite' ), $name ), 'incompatible' => esc_html__( 'Incompatible Addon', 'wpforms-lite' ), ]; $contents = [ 'upgrade' => sprintf( /* translators: %1$s - Field name. */ esc_html__( 'Upgrade to gain access to the %1$s field and dozens of other powerful features to help you build smarter forms and grow your business.', 'wpforms-lite' ), $name ), 'install' => sprintf( /* translators: %1$s - Addon name. */ esc_html__( 'You have access to the %1$s, but it\'s not currently installed.', 'wpforms-lite' ), $addon_name ), 'activate' => sprintf( /* translators: %1$s - Addon name. */ esc_html__( 'You have access to the %1$s, but it\'s not currently activated.', 'wpforms-lite' ), $addon_name ), 'incompatible' => sprintf( /* translators: %1$s - Addon name. */ esc_html__( 'The %1$s is not compatible with this version of WPForms and requires an update.', 'wpforms-lite' ), $addon_name ), ]; $button_labels = [ 'upgrade' => esc_html__( 'Upgrade to Pro', 'wpforms-lite' ), 'install' => esc_html__( 'Install Addon', 'wpforms-lite' ), 'activate' => esc_html__( 'Activate Addon', 'wpforms-lite' ), 'incompatible' => esc_html__( 'Update Addon', 'wpforms-lite' ), ]; // If it's not an upgrade action, use the addon data. if ( $action !== 'upgrade' ) { $name = $addon_name; $utm_name = $this->addon_edu_data['utm_content']; } else { $edu_fields = wpforms()->obj( 'education_fields' ); $edu_field = $edu_fields ? $edu_fields->get_field( $this->type ) : null; $utm_name = $edu_field['name_en'] ?? $this->type; // Fallback to the field type. } $button_utm = sprintf( 'AI Form - %1$s notice', esc_html( $utm_name ) ); return [ $name, $titles[ $action ] ?? $titles['upgrade'], $contents[ $action ] ?? $contents['upgrade'], $button_labels[ $action ] ?? $button_labels['upgrade'], $button_utm, ]; } /** * Determine if the field is disabled. * * @since 1.9.4 * @since 1.10.0 The method access modifier is changed from private to protected. */ protected function is_disabled_field(): bool { // It is a Pro field in Lite OR the addon is not initialized. return ! ( $this->is_pro && ( empty( $this->addon_slug ) || $this->is_addon_initialized ) ); } /** * Get a preview option. * * @since 1.9.4 * * @param string $option Option name. * @param array $field Field data. * @param array $args Additional arguments. * @param bool $do_echo Echo or return. * * @noinspection ReturnTypeCanBeDeclaredInspection * @noinspection PhpMultipleClassDeclarationsInspection */ public function field_preview_option( $option, $field, $args = [], $do_echo = true ) { // Hide remaining elements, prevent incompatible addon field elements from being displayed. if ( $option === 'hide-remaining' && ! empty( $this->is_disabled_field ) ) { echo '<div class="wpforms-field-hide-remaining"></div>'; return; } parent::field_preview_option( $option, $field, $args, $do_echo ); } /** * Get the Pro field preview badge. * * @since 1.9.4 */ private function get_field_preview_badge(): string { if ( empty( $this->is_disabled_field ) ) { return ''; } $action = $this->addon_edu_data['action'] ?? ''; if ( $action === 'incompatible' ) { return Helpers::get_badge( esc_html__( 'Update required', 'wpforms-lite' ) , 'lg', 'inline', 'red' ); } // If it's an addon field in Pro AND the addon is not initialized, show the ADDON badge. if ( in_array( $action, [ 'install' ,'activate' ], true ) ) { return Helpers::get_badge( 'Addon', 'lg', 'inline', 'orange' ); } return Helpers::get_badge( 'Pro', 'lg', 'inline', 'green' ); } /** * Get the addon educational data of the field. * * @since 1.9.4 * * @return array */ private function get_field_addon_edu_data(): array { if ( empty( $this->addon_slug ) || ! empty( $this->is_addon_initialized ) || ! is_admin() ) { return []; } $addons = Helpers::get_edu_addons(); return $addons[ 'wpforms-' . $this->addon_slug ] ?? []; } /** * Filter frontend field data to prevent rendering Pro fields in Lite. * * @since 1.9.4 * * @param array|mixed $field Field data. * @param array $form_data Form data. * * @return array * @noinspection PhpMissingParamTypeInspection * @noinspection PhpUnusedParameterInspection */ public function filter_frontend_field_data( $field, $form_data ): array { // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundAfterLastUsed $field = (array) $field; $type = $field['type'] ?? ''; // If it's not a Pro field or the field type doesn't match, return the field data as is. if ( empty( $this->is_pro_field ) || $type !== $this->type ) { return $field; } // If it's a Pro field in Lite OR, // the addon is not initialized, // return an empty array to prevent rendering. if ( ! empty( $this->is_disabled_field ) ) { return []; } return $field; } /** * Disallow the field preview "Duplicate" button. * * @since 1.9.4 * * @param bool|mixed $display Display switch. * @param array $field Field settings. * * @return bool * @noinspection PhpMissingParamTypeInspection */ public function filter_field_preview_display_duplicate_button( $display, $field ): bool { if ( $field['type'] !== $this->type || empty( $this->is_disabled_field ) ) { return (bool) $display; } return false; } /** * Add a class to the field preview container. * * @since 1.9.4 * * @param string|mixed $css_class CSS class. * @param array $field Field settings. * * @return string * @noinspection PhpMissingParamTypeInspection */ public function filter_field_preview_class( $css_class, $field ): string { $css_class = (string) $css_class; if ( $field['type'] !== $this->type || empty( $this->is_disabled_field ) ) { return $css_class; } return trim( $css_class . ' wpforms-field-is-pro' ); } /** * Filter entry save data. * * @since 1.9.5 * * @param array|mixed $fields Entry fields data. * @param array $entry Entry data. * @param array $form_data Form data. * * @return array * @noinspection PhpUnusedParameterInspection */ public function filter_entry_save_data( $fields, array $entry, array $form_data ): array { // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundAfterLastUsed $fields = (array) $fields; // If it's not a disabled Pro field, return the fields as is. if ( empty( $this->is_disabled_field ) ) { return $fields; } // Remove disabled Pro fields from the entry fields. foreach ( $fields as $field_id => $field ) { if ( isset( $field['type'] ) && $field['type'] === $this->type ) { unset( $fields[ $field_id ] ); } } return $fields; } /** * Filter forbidden columns on the Form Entries page. * * @since 1.9.5 * * @param array|mixed $forbidden_fields Entry fields data. * @param int|string $form_id Form ID. * * @return array * @noinspection PhpUnusedParameterInspection * @noinspection PhpUnusedLocalVariableInspection */ public function filter_field_columns_forbidden_fields( $forbidden_fields, $form_id ): array { $forbidden_fields = (array) $forbidden_fields; if ( empty( $this->is_disabled_field ) ) { return $forbidden_fields; } $form_data = $this->get_form_data( (int) $form_id ); if ( ! $form_data ) { return $forbidden_fields; } $fields = $form_data['fields'] ?? []; foreach ( $fields as $field_id => $field ) { if ( isset( $field['type'] ) && $field['type'] === $this->type ) { $forbidden_fields[] = $field['type']; } } return $forbidden_fields; } /** * Get form data by form ID and cache it. * * @since 1.9.5 * * @param int $form_id Form ID. * * @return array */ private function get_form_data( int $form_id ): array { $form_obj = wpforms()->obj( 'form' ); if ( ! $form_obj ) { return []; } // Cache the form data into static variable. static $cached_form_data = []; if ( isset( $cached_form_data[ $form_id ] ) ) { return $cached_form_data[ $form_id ]; } $cached_form_data[ $form_id ] = (array) $form_obj->get( $form_id, [ 'content_only' => true ] ); return $cached_form_data[ $form_id ]; } /** * Filter entries export configuration. * * @since 1.9.5 * * @param array $config Export configuration. * * @return array * @noinspection PhpMissingParamTypeInspection */ public function filter_entries_export_configuration( $config ): array { $config = (array) $config; // If it's not a disabled Pro field, return the config as is. if ( empty( $this->is_disabled_field ) ) { return $config; } if ( empty( $this->type ) ) { return $config; } $config['disallowed_fields'] = ! empty( $config['disallowed_fields'] ) ? (array) $config['disallowed_fields'] : []; // Add the disabled Pro field type to `disallowed_fields` if not already there. if ( ! in_array( $this->type, $config['disallowed_fields'], true ) ) { $config['disallowed_fields'][] = $this->type; } return $config; } /** * Filter if the field is displayable in the Entry Edit page. * * @since 1.9.5 * * @param bool|mixed $displayable Whether the field is displayable. * @param array $field Field data. * @param array $form_data Form data. * * @return bool * @noinspection PhpUnusedParameterInspection */ public function filter_is_field_displayable( $displayable, array $field, array $form_data ): bool { // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundAfterLastUsed if ( ! $this->is_disabled_field ) { return (bool) $displayable; } return false; } } Fields/EntryPreview/Field.php 0000644 00000017124 15252506741 0012165 0 ustar 00 <?php namespace WPForms\Forms\Fields\EntryPreview; use WPForms\Forms\Fields\Traits\ProField as ProFieldTrait; use WPForms_Field; /** * Entry preview field. * * @since 1.9.4 */ class Field extends WPForms_Field { use ProFieldTrait; /** * Init. * * @since 1.9.4 */ public function init() { // Define field type information. $this->name = esc_html__( 'Entry Preview', 'wpforms-lite' ); $this->keywords = esc_html__( 'confirm', 'wpforms-lite' ); $this->type = 'entry-preview'; $this->icon = 'fa-file-text-o'; $this->order = 190; $this->group = 'fancy'; $this->allow_read_only = false; $this->init_pro_field(); $this->hooks(); } /** * Hooks. * * @since 1.9.4 */ protected function hooks() { add_filter( 'wpforms_builder_strings', [ $this, 'add_builder_strings' ], 10, 2 ); add_filter( 'wpforms_field_preview_display_duplicate_button', [ $this, 'field_display_duplicate_button' ], 10, 2 ); add_filter( 'wpforms_field_new_display_duplicate_button', [ $this, 'field_display_duplicate_button' ], 10, 2 ); } /** * Field options panel inside the builder. * * @since 1.9.4 * * @param array $field Field data. */ public function field_options( $field ) { // Options open markup. $this->field_option( 'basic-options', $field, [ 'markup' => 'open', 'after_title' => $this->get_field_options_notice(), ] ); if ( empty( $this->is_disabled_field ) ) { $this->field_element( 'row', $field, [ 'slug' => 'description', 'content' => sprintf( '<p class="note">%s</p>', esc_html__( 'Entry Preview must be displayed on its own page, without other fields. HTML fields are allowed.', 'wpforms-lite' ) ), ] ); } $this->field_element( 'row', $field, [ 'slug' => 'preview-notice-enable', 'content' => $this->field_element( 'toggle', $field, [ 'slug' => 'preview-notice-enable', // When we add the field to a form, it enabled by default. 'value' => ! empty( $field['preview-notice-enable'] ) || wp_doing_ajax(), 'desc' => esc_html__( 'Display Preview Notice', 'wpforms-lite' ), 'tooltip' => esc_html__( 'Check this option to show a message above the entry preview.', 'wpforms-lite' ), ], false ), ] ); $this->field_element( 'row', $field, [ 'slug' => 'preview-notice', 'content' => $this->field_element( 'label', $field, [ 'slug' => 'preview-notice', 'value' => esc_html__( 'Preview Notice', 'wpforms-lite' ), 'tooltip' => esc_html__( 'Fill in the message to show above the entry preview.', 'wpforms-lite' ), ], false ) . $this->field_element( 'textarea', $field, [ 'slug' => 'preview-notice', 'value' => $field['preview-notice'] ?? self::get_default_notice(), ], false ), ] ); $this->field_option( 'basic-options', $field, [ 'markup' => 'close' ] ); $this->field_option( 'advanced-options', $field, [ 'markup' => 'open' ] ); $this->field_element( 'row', $field, [ 'slug' => 'style', 'content' => $this->field_element( 'label', $field, [ 'slug' => 'style', 'value' => esc_html__( 'Style', 'wpforms-lite' ), 'tooltip' => esc_html__( 'Choose the entry preview display style.', 'wpforms-lite' ), ], false ) . $this->field_element( 'select', $field, [ 'slug' => 'style', 'value' => ! empty( $field['style'] ) ? $field['style'] : 'basic', 'options' => self::get_styles(), ], false ), ] ); $this->field_option( 'css', $field ); $this->field_option( 'advanced-options', $field, [ 'markup' => 'close' ] ); } /** * Create the field preview. * * @since 1.9.4 * * @param array $field Field data and settings. * * @noinspection HtmlUnknownAttribute*/ public function field_preview( $field ) { printf( '<label class="label-title"> <span class="text">%1$s</span>%2$s</label>', esc_html__( 'Entry Preview', 'wpforms-lite' ), $this->get_field_preview_badge() // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ); $is_new_field = wp_doing_ajax(); $notice = ! empty( $field['preview-notice-enable'] ) && isset( $field['preview-notice'] ) && ! wpforms_is_empty_string( $field['preview-notice'] ) ? force_balance_tags( $field['preview-notice'] ) : ''; $notice = $is_new_field || wpforms_is_empty_string( $notice ) ? self::get_default_notice() : $notice; $is_disabled = $is_new_field || ! empty( $field['preview-notice-enable'] ); printf( '<div class="wpforms-entry-preview-notice nl2br"%2$s>%1$s</div>', wp_kses_post( nl2br( $notice ) ), ! $is_disabled ? ' style="display: none"' : '' ); printf( '<div class="wpforms-alert wpforms-alert-info"%2$s> <p>%1$s</p> </div>', esc_html__( 'Entry preview will be displayed here and will contain all fields found on the previous page.', 'wpforms-lite' ), $is_disabled ? ' style="display: none"' : '' ); } /** * Display the field input elements on the frontend. * * @since 1.9.4 * * @param array $field Field data and settings. * @param array $deprecated Field attributes. * @param array $form_data Form data and settings. */ public function field_display( $field, $deprecated, $form_data ) { } /** * Add custom JS i18n strings for the builder. * * @since 1.9.4 * * @param array|mixed $strings List of strings. * @param array $form Current form. * * @return array * @noinspection PhpMissingParamTypeInspection * @noinspection PhpUnusedParameterInspection */ public function add_builder_strings( $strings, $form ): array { $strings = (array) $strings; $strings['entry_preview_require_page_break'] = esc_html__( 'Page breaks are required for entry previews to work. If you\'d like to remove page breaks, you\'ll have to first remove the entry preview field.', 'wpforms-lite' ); $strings['entry_preview_default_notice'] = self::get_default_notice(); $strings['entry_preview_require_previous_button'] = esc_html__( 'You can\'t hide the previous button because it is required for the entry preview field on this page.', 'wpforms-lite' ); return $strings; } /** * Get default notice. * * @since 1.9.4 * * @return string */ protected static function get_default_notice(): string { return sprintf( "<strong>%s</strong>\n%s", esc_html__( 'This is a preview of your submission. It has not been submitted yet!', 'wpforms-lite' ), esc_html__( 'Please take a moment to verify your information. You can also go back to make changes.', 'wpforms-lite' ) ); } /** * Get a list of available styles. * * @since 1.9.4 * * @return array */ protected static function get_styles(): array { return [ 'basic' => esc_html__( 'Basic', 'wpforms-lite' ), 'compact' => esc_html__( 'Compact', 'wpforms-lite' ), 'table' => esc_html__( 'Table', 'wpforms-lite' ), 'table_compact' => esc_html__( 'Table, Compact', 'wpforms-lite' ), ]; } /** * Disallow the field preview "Duplicate" button. * * @since 1.9.9 * * @param bool|mixed $display Display switch. * @param array $field Field settings. * * @return bool */ public function field_display_duplicate_button( $display, array $field ): bool { $type = $field['type'] ?? ''; if ( $type === $this->type ) { // Pagebreak fields cannot be duplicated. return false; } return (bool) $display; } } Fields/Helpers/RequirementsAlerts.php 0000644 00000015263 15252506741 0013741 0 ustar 00 <?php namespace WPForms\Forms\Fields\Helpers; /** * Helpers for Requirements Alerts. * * Can be used for notifying about new features that addons are not supported. * * @since 1.8.7 */ class RequirementsAlerts { /** * Determine if the Product Quantities feature is allowed to use. * * @since 1.8.7 * * @return bool */ public static function is_product_quantities_allowed(): bool { return empty( self::get_addons_require_for_product_quantities() ); } /** * Determine if the Order Summary feature is allowed to use. * * @since 1.8.7 * * @return bool */ public static function is_order_summary_allowed(): bool { return ! self::is_pro() || ! defined( 'WPFORMS_COUPONS_VERSION' ) || version_compare( WPFORMS_COUPONS_VERSION, '1.2.0', '>=' ); } /** * Product Quantities feature: get an update required alert HTML. * * @since 1.8.7 * * @return string */ public static function get_product_quantities_alert(): string { $addons_require_update = self::get_addons_require_for_product_quantities(); // Generate update link when only one addon needs to be updated. if ( count( $addons_require_update ) === 1 ) { $update_url = self::get_addon_update_url( key( $addons_require_update ) ); } else { // Redirect to the Plugins admin page if multiple addons require an update. $update_url = admin_url( 'plugins.php?plugin_status=upgrade' ); } return self::get_update_alert( sprintf( /* translators: %1$s - addons list. */ __( 'The following addons require an update to support product quantities: %1$s', 'wpforms-lite' ), implode( ', ', $addons_require_update ) ), $update_url ); } /** * Order Summary feature: get an update required alert HTML. * * @since 1.8.7 * * @return string */ public static function get_order_summary_alert(): string { return self::get_update_alert( __( 'You\'re using an older version of the Coupons addon that does not support order summary.', 'wpforms-lite' ), self::get_addon_update_url( 'wpforms-coupons' ) ); } /** * Repeater field: determine if addon is allowed to use inside the repeater field. * * @since 1.8.9 * * @param string $addon_slug Addon slug. * * @return bool */ public static function is_inside_repeater_allowed( string $addon_slug ): bool { $requirements = [ 'wpforms-geolocation' => '2.10.0', 'wpforms-signatures' => '1.11.0', 'wpforms-form-abandonment' => '1.12.0', 'wpforms-save-resume' => '1.11.0', 'wpforms-lead-forms' => '1000', // @todo: We should adjust this value when the Lead Forms get the Repeater field support. 'wpforms-google-sheets' => '2.1.0', ]; if ( ! isset( $requirements[ $addon_slug ] ) ) { return true; } $version_constant = strtoupper( str_replace( '-', '_', $addon_slug ) ) . '_VERSION'; return self::is_pro() && defined( $version_constant ) && version_compare( constant( $version_constant ), $requirements[ $addon_slug ], '>=' ); } /** * Repeater field: get an update required alert HTML. * * @since 1.8.9 * * @param string $addon_name Addon name. * @param string $addon_slug Addon slug. * * @return string */ public static function get_repeater_alert( string $addon_name, string $addon_slug ): string { return self::get_update_alert( self::get_repeater_alert_text( $addon_name ), self::get_addon_update_url( $addon_slug ) ); } /** * Repeater field: get alert text. * * @since 1.8.9 * * @param string $addon_name Addon name. * * @return string */ public static function get_repeater_alert_text( string $addon_name ): string { return sprintf( /* translators: %1$s - addon name. */ __( 'You\'re using an older version of the %1$s addon that does not support the Repeater field.', 'wpforms-lite' ), $addon_name ); } /** * Retrieve a list of addons that require updating to support the Product Quantities feature. * * @since 1.8.7 * * @return array */ private static function get_addons_require_for_product_quantities(): array { static $addons; if ( ! is_null( $addons ) ) { return $addons; } $addons = []; // All addons require Pro and Top level licenses. if ( ! self::is_pro() ) { return $addons; } if ( defined( 'WPFORMS_COUPONS_VERSION' ) && version_compare( WPFORMS_COUPONS_VERSION, '1.2.0', '<' ) ) { $addons['wpforms-coupons'] = __( 'Coupons', 'wpforms-lite' ); } if ( defined( 'WPFORMS_PAYPAL_COMMERCE_VERSION' ) && version_compare( WPFORMS_PAYPAL_COMMERCE_VERSION, '1.9.0', '<' ) ) { $addons['wpforms-paypal-commerce'] = __( 'PayPal Commerce', 'wpforms-lite' ); } if ( defined( 'WPFORMS_PAYPAL_STANDARD_VERSION' ) && version_compare( WPFORMS_PAYPAL_STANDARD_VERSION, '1.10.0', '<' ) ) { $addons['wpforms-paypal-standard'] = __( 'PayPal Standard', 'wpforms-lite' ); } if ( defined( 'WPFORMS_SQUARE_VERSION' ) && version_compare( WPFORMS_SQUARE_VERSION, '1.9.0', '<' ) ) { $addons['wpforms-square'] = __( 'Square', 'wpforms-lite' ); } if ( defined( 'WPFORMS_SAVE_RESUME_VERSION' ) && version_compare( WPFORMS_SAVE_RESUME_VERSION, '1.9.0', '<' ) ) { $addons['wpforms-save-resume'] = __( 'Save and Resume', 'wpforms-lite' ); } return $addons; } /** * Get an update alert HTML. * * @since 1.8.7 * * @param string $message Alert message. * @param string $update_url Update button URL. * * @return string */ private static function get_update_alert( string $message, string $update_url ): string { $alert = sprintf( '<div class="wpforms-alert-message"> <h4>%1$s</h4> <p>%2$s</p> </div> <div class="wpforms-alert-buttons"> <a href="%3$s" target="_blank" rel="noopener noreferrer" class="wpforms-btn wpforms-btn-sm wpforms-btn-blue">%4$s</a> </div>', esc_html__( 'Update Required', 'wpforms-lite' ), esc_html( $message ), esc_url( $update_url ), esc_html__( 'Update Now', 'wpforms-lite' ) ); return sprintf( '<div class="wpforms-alert wpforms-alert-danger wpforms-alert-field-requirements">%1$s</div>', $alert // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ); } /** * Get addon update URL. * * @since 1.8.7 * * @param string $addon_slug Addon slug. * * @return string */ private static function get_addon_update_url( string $addon_slug ): string { $addon_path = sprintf( '%1$s/%1$s.php', $addon_slug ); return wp_nonce_url( self_admin_url( 'update.php?action=upgrade-plugin&plugin=' . $addon_path ), 'upgrade-plugin_' . $addon_path ); } /** * Determine if Pro or Top level license is used. * * @since 1.8.7 * * @return bool */ private static function is_pro(): bool { return in_array( wpforms_get_license_type(), [ 'pro', 'elite', 'agency', 'ultimate' ], true ); } } Fields/Addons/Map/Field.php 0000644 00000054066 15252506741 0011455 0 ustar 00 <?php namespace WPForms\Forms\Fields\Addons\Map; use WPForms\Forms\Fields\Traits\ProField as ProFieldTrait; use WPForms_Field; use WPFormsGeolocation\Admin\Settings\Settings; /** * Map field. * * @since 1.10.0 */ class Field extends WPForms_Field { /** * Find Nearby Locations option key. * * @since 1.10.0 */ protected const NEARBY_LOCATIONS_KEY = 'wpforms_geolocation_find_nearby_locations'; /** * Search Radius option key. * * @since 1.10.0 */ protected const NEARBY_LOCATIONS_RADIUS_KEY = 'wpforms_geolocation_search_radius'; /** * Default search radius. * * @since 1.10.0 */ protected const DEFAULT_SEARCH_RADIUS = 25; use ProFieldTrait; /** * Whether the addon is active. * * @since 1.10.0 * * @var bool */ private $is_addon_active = false; /** * Determine if we should display the field options notice. * * @since 1.10.0 * * @var bool */ protected $display_field_options_notice = true; /** * Init class. * * @since 1.10.0 * * @noinspection ReturnTypeCanBeDeclaredInspection */ public function init() { // Define field type information. $this->name = esc_html__( 'Map', 'wpforms-lite' ); $this->keywords = esc_html__( 'map', 'wpforms-lite' ); $this->type = 'map'; $this->icon = 'fa-map-location-dot'; $this->order = 75; $this->group = 'fancy'; $this->addon_slug = 'geolocation'; $this->allow_read_only = false; $this->default_settings = [ 'hide_full_screen' => '1', 'hide_map_type' => '1', 'hide_location_info' => '1', 'hide_street_view' => '1', 'hide_camera_control' => '1', 'disable_mouse_zooming' => '1', 'show_in_entry' => '1', 'show_thumbnail_in_entry' => '1', 'search_radius' => self::DEFAULT_SEARCH_RADIUS, ]; $this->is_addon_active = function_exists( 'wpforms_' . $this->addon_slug ); $this->init_pro_field(); $this->hooks(); } /** * Define field hooks. * * @since 1.10.0 */ protected function hooks(): void {} /** * Define additional field options. * * @since 1.10.0 * * @param array $field Field data and settings. * * @noinspection ReturnTypeCanBeDeclaredInspection */ public function field_options( $field ) { $this->basic_field_options( (array) $field ); $this->advanced_field_options( (array) $field ); } /** * Basic field options. * * @since 1.10.0 * * @param array $field Field settings. * * @return void */ private function basic_field_options( array $field ): void { // Options open markup. $this->field_option( 'basic-options', $field, [ 'markup' => 'open', 'after_title' => $this->display_field_options_notice ? $this->get_field_options_notice() : '', ] ); $this->field_option( 'label', $field ); $this->field_option( 'description', $field ); $this->field_element( 'row', $field, [ 'slug' => 'choices', 'class' => 'wpforms-field-option-row-locations', 'content' => $this->get_location_options( $field ), ] ); $current_user_id = get_current_user_id(); $find_nearby_locations = (bool) get_user_meta( $current_user_id, self::NEARBY_LOCATIONS_KEY, true ); $nearby_locations_radius = (int) get_user_meta( $current_user_id, self::NEARBY_LOCATIONS_RADIUS_KEY, true ); $nearby_locations_radius = $nearby_locations_radius > 0 ? $nearby_locations_radius : self::DEFAULT_SEARCH_RADIUS; $this->field_element( 'row', $field, [ 'slug' => 'find_nearby_locations', 'content' => $this->field_element( 'toggle', $field, [ 'slug' => 'find_nearby_locations', 'value' => $find_nearby_locations ? '1' : '0', 'desc' => esc_html__( 'Find Nearby Locations', 'wpforms-lite' ), ], false ), ] ); $this->field_element( 'row', $field, [ 'slug' => 'search_radius', 'class' => ! $find_nearby_locations ? 'wpforms-hidden' : '', 'content' => $this->field_element( 'label', $field, [ 'slug' => 'search_radius', 'value' => esc_html__( 'Search Radius', 'wpforms-lite' ), ], false ) . $this->field_element( 'select', $field, [ 'slug' => 'search_radius', 'value' => $nearby_locations_radius, 'options' => $this->get_search_radius_km_options(), 'data' => [ 'miles-options' => wp_json_encode( $this->get_search_radius_miles_options() ), ], ], false ), ] ); $this->field_element( 'row', $field, [ 'slug' => 'show_locations_list', 'content' => $this->field_element( 'toggle', $field, [ 'slug' => 'show_locations_list', 'value' => isset( $field['show_locations_list'] ) ? '1' : '0', 'desc' => esc_html__( 'Show List of Locations', 'wpforms-lite' ), ], false ), ] ); $this->field_element( 'row', $field, [ 'slug' => 'allow_location_selection', 'content' => $this->field_element( 'toggle', $field, [ 'slug' => 'allow_location_selection', 'value' => isset( $field['allow_location_selection'] ) ? '1' : '0', 'desc' => esc_html__( 'Allow Location Selection', 'wpforms-lite' ), ], false ), ] ); $this->field_element( 'row', $field, [ 'slug' => 'zoom_level', 'content' => $this->field_element( 'label', $field, [ 'slug' => 'zoom_level', 'value' => esc_html__( 'Zoom Level', 'wpforms-lite' ), ], false ) . $this->field_element( 'select', $field, [ 'class' => 'wpforms-field-map-settings', 'data' => [ 'map-control' => 'zoom', ], 'slug' => 'zoom_level', 'value' => ! empty( $field['zoom_level'] ) && $field['zoom_level'] >= 0 && $field['zoom_level'] <= 22 ? (int) $field['zoom_level'] : 15, 'options' => range( 0, 22 ), ], false ), ] ); $this->field_option( 'basic-options', $field, [ 'markup' => 'close' ] ); } /** * Advanced field options. * * @since 1.10.0 * * @param array $field Field settings. * * @return void */ private function advanced_field_options( array $field ): void { // phpcs:ignore Generic.Metrics.CyclomaticComplexity.TooHigh $is_mapbox = $this->get_active_provider_slug() === 'mapbox-search'; $this->field_option( 'advanced-options', $field, [ 'markup' => 'open' ] ); $this->field_option( 'size', $field ); $this->field_option( 'css', $field ); printf( '<div class="wpforms-field-option-row-subtitle">%1$s</div>', esc_html__( 'Presentational Settings', 'wpforms-lite' ) ); $this->field_element( 'row', $field, [ 'slug' => 'hide_full_screen', 'content' => $this->field_element( 'toggle', $field, [ 'class' => 'wpforms-field-map-settings', 'data' => [ 'map-control' => 'fullscreenControl', ], 'slug' => 'hide_full_screen', 'value' => isset( $field['hide_full_screen'] ) ? '1' : '0', 'desc' => esc_html__( 'Hide Full Screen ', 'wpforms-lite' ), ], false ), ] ); if ( ! $is_mapbox ) { $this->field_element( 'row', $field, [ 'slug' => 'hide_map_type', 'content' => $this->field_element( 'toggle', $field, [ 'class' => 'wpforms-field-map-settings', 'data' => [ 'map-control' => 'mapTypeControl', ], 'slug' => 'hide_map_type', 'value' => isset( $field['hide_map_type'] ) ? '1' : '0', 'desc' => esc_html__( 'Hide Map Type ', 'wpforms-lite' ), ], false ), ] ); $this->field_element( 'row', $field, [ 'slug' => 'hide_location_info', 'content' => $this->field_element( 'toggle', $field, [ 'slug' => 'hide_location_info', 'value' => isset( $field['hide_location_info'] ) ? '1' : '0', 'desc' => esc_html__( 'Hide Location Info ', 'wpforms-lite' ), ], false ), ] ); $this->field_element( 'row', $field, [ 'slug' => 'hide_street_view', 'content' => $this->field_element( 'toggle', $field, [ 'class' => 'wpforms-field-map-settings', 'data' => [ 'map-control' => 'streetViewControl', ], 'slug' => 'hide_street_view', 'value' => isset( $field['hide_street_view'] ) ? '1' : '0', 'desc' => esc_html__( 'Hide Street View ', 'wpforms-lite' ), ], false ), ] ); printf( '<div class="wpforms-field-option-row-subtitle">%1$s</div>', esc_html__( 'Interactive Settings', 'wpforms-lite' ) ); $this->field_element( 'row', $field, [ 'slug' => 'hide_camera_control', 'content' => $this->field_element( 'toggle', $field, [ 'class' => 'wpforms-field-map-settings', 'data' => [ 'map-control' => 'cameraControl', ], 'slug' => 'hide_camera_control', 'value' => isset( $field['hide_camera_control'] ) ? '1' : '0', 'desc' => esc_html__( 'Hide Camera Control ', 'wpforms-lite' ), ], false ), ] ); } $this->field_element( 'row', $field, [ 'slug' => 'hide_zoom', 'content' => $this->field_element( 'toggle', $field, [ 'class' => 'wpforms-field-map-settings', 'data' => [ 'map-control' => 'zoomControl', ], 'slug' => 'hide_zoom', 'value' => isset( $field['hide_zoom'] ) ? '1' : '0', 'desc' => esc_html__( 'Hide Zoom ', 'wpforms-lite' ), ], false ), ] ); $this->field_element( 'row', $field, [ 'slug' => 'disable_dragging', 'content' => $this->field_element( 'toggle', $field, [ 'slug' => 'disable_dragging', 'value' => isset( $field['disable_dragging'] ) ? '1' : '0', 'desc' => esc_html__( 'Disable Dragging ', 'wpforms-lite' ), ], false ), ] ); $this->field_element( 'row', $field, [ 'slug' => 'disable_mouse_zooming', 'content' => $this->field_element( 'toggle', $field, [ 'slug' => 'disable_mouse_zooming', 'value' => isset( $field['disable_mouse_zooming'] ) ? '1' : '0', 'desc' => esc_html__( 'Disable Mouse Zooming ', 'wpforms-lite' ), ], false ), ] ); printf( '<div class="wpforms-field-option-row-subtitle">%1$s</div>', esc_html__( 'Other', 'wpforms-lite' ) ); $this->field_element( 'row', $field, [ 'slug' => 'show_in_entry', 'content' => $this->field_element( 'toggle', $field, [ 'slug' => 'show_in_entry', 'value' => isset( $field['show_in_entry'] ) ? '1' : '0', 'desc' => esc_html__( 'Show in Entry ', 'wpforms-lite' ), ], false ), ] ); $this->field_element( 'row', $field, [ 'slug' => 'show_thumbnail_in_entry', 'content' => $this->field_element( 'toggle', $field, [ 'slug' => 'show_thumbnail_in_entry', 'value' => isset( $field['show_thumbnail_in_entry'] ) ? '1' : '0', 'desc' => esc_html__( 'Show Thumbnail in Entry ', 'wpforms-lite' ), ], false ), ] ); $this->field_option( 'label_hide', $field ); $this->field_option( 'advanced-options', $field, [ 'markup' => 'close' ] ); } /** * Get active provider slug. * * @since 1.10.0 */ protected function get_active_provider_slug(): string { if ( ! class_exists( Settings::class ) ) { return ''; } return ( new Settings() )->get_current_provider(); } /** * Field preview inside the builder. * * @since 1.10.0 * * @param array $field Field data. * * @noinspection ReturnTypeCanBeDeclaredInspection */ public function field_preview( $field ) { $this->field_preview_option( 'label', $field, [ 'label_badge' => $this->get_field_preview_badge(), ] ); $size = $field['size'] ?? 'medium'; $field_id = $field['id'] ?? 0; $this->print_map( $size, $field_id ); $this->print_location_list_preview( $field ); $this->field_preview_option( 'description', $field ); $this->field_preview_option( 'hide-remaining', $field ); } /** * Print map HTML. * * @since 1.10.0 * * @param string $size Field size. * @param int|string $field_id Field ID. May be a string for Repeater child fields (e.g. "5_0"). */ protected function print_map( string $size, $field_id ): void { printf( '<div class="wpforms-field-row wpforms-field-%1$s wpforms-geolocation-map" id="wpforms-field-%2$s-map"></div>', esc_attr( $size ), esc_attr( wpforms_validate_field_id( $field_id ) ) ); } /** * Print location list preview. * * @since 1.10.0 * * @param array $field Field settings. * * @noinspection PhpUnusedLocalVariableInspection * @noinspection HtmlWrongAttributeValue */ private function print_location_list_preview( array $field ): void { $choices = $field['choices'] ?? []; $show_locations_list = ! empty( $field['show_locations_list'] ); $allow_location_selection = $show_locations_list && ! empty( $field['allow_location_selection'] ) && count( $choices ) > 1; printf( '<ul class="wpforms-field-map-choices wpforms-field-row%1$s">', ! $show_locations_list ? ' wpforms-hidden' : '' ); foreach ( $choices as $key => $choice ) { echo '<li>'; printf( '<input type="%1$s">', $allow_location_selection ? 'radio' : 'hidden' ); echo '<label>'; printf( '<span class="wpforms-field-map-location-name">%1$s</span>', isset( $choice['name'] ) ? esc_html( $choice['name'] ) : '' ); printf( '<span class="wpforms-field-map-location-address">%1$s</span>', isset( $choice['address'] ) ? esc_html( $choice['address'] ) : '' ); echo '</label>'; echo '</li>'; } echo '</ul>'; } /** * Determine if the current choice is a valid marker. * * @since 1.10.0 * * @param array $choice Choice data. */ protected function is_valid_marker( array $choice ): bool { if ( ! isset( $choice['latitude'], $choice['longitude'] ) ) { return false; } if ( wpforms_is_empty_string( $choice['latitude'] ) || wpforms_is_empty_string( $choice['longitude'] ) ) { return false; } if ( ! empty( $choice['marker_type'] ) && $choice['marker_type'] === 'image' && empty( $choice['image'] ) ) { return false; } if ( ( ! isset( $choice['name'] ) || wpforms_is_empty_string( $choice['name'] ) ) && ( ! isset( $choice['address'] ) || wpforms_is_empty_string( $choice['address'] ) ) ) { return false; } return true; } /** * Field display on the form front-end. * * @since 1.10.0 * * @param array $field Field settings. * @param array $deprecated Deprecated array. * @param array $form_data Form data and settings. * * @noinspection ReturnTypeCanBeDeclaredInspection */ public function field_display( $field, $deprecated, $form_data ) { } /** * Get Locations options HTML template. * * @since 1.10.0 * * @param array $field Field settings. * * @noinspection PhpCastIsUnnecessaryInspection * @noinspection UnnecessaryCastingInspection * * @return string */ private function get_location_options( array $field ): string { $field_id = ! empty( $field['id'] ) ? (int) $field['id'] : 0; $locations = $field['choices'] ?? [ [] ]; $next_id = max( array_keys( $locations ) ) + 1; ob_start(); $this->field_element( 'label', $field, [ 'slug' => 'locations', 'value' => esc_html__( 'Locations', 'wpforms-lite' ), ] ); printf( '<ul class="choices-list wpforms-undo-redo-container" data-next-id="%1$d" data-field-id="%2$d" data-field-type="location">', (int) $next_id, (int) $field_id ); foreach ( $locations as $location_index => $location ) { $this->print_location_row( $location, (int) $location_index, $field_id ); } echo '</ul>'; return ob_get_clean(); } /** * Print Locations options row. * * @since 1.10.0 * * @param array $location Location data. * @param int $location_index Index. * @param int $field_id Field ID. * * @return void * * @noinspection HtmlFormInputWithoutLabel */ private function print_location_row( array $location, int $location_index, int $field_id ): void { $location = wp_parse_args( array_filter( $location ), [ 'name' => '', 'address' => '', 'description' => '', 'marker_type' => 'icon', 'icon' => 'face-smile', 'icon_style' => 'regular', 'icon_color' => '#d63638', 'latitude' => '', 'longitude' => '', 'image' => '', 'size' => 'small', ] ); $base = sprintf( 'fields[%s][choices][%d]', wpforms_validate_field_id( $field_id ), absint( $location_index ) ); $id_base = sprintf( 'fields-%s-choices-%d-', wpforms_validate_field_id( $field_id ), absint( $location_index ) ); $has_image = ! empty( $location['image'] ); ?> <li data-key="<?php echo absint( $location_index ); ?>" class="wpforms-geolocation-map-field-location-size-<?php echo esc_attr( $location['size'] ); ?> wpforms-geolocation-map-field-location-<?php echo esc_attr( $location['marker_type'] ); ?>"> <span class="move"><i class="fa fa-grip-lines"></i></span> <input type="text" name="<?php echo esc_attr( $base ); ?>[name]" value="<?php echo esc_attr( $location['name'] ); ?>" data-1p-ignore="true" class="label wpforms-geolocation-map-field-location-name" placeholder="<?php esc_attr_e( 'Name', 'wpforms-lite' ); ?>"> <a class="add" href="#"><i class="fa fa-plus-circle"></i></a> <a class="remove" href="#"><i class="fa fa-minus-circle"></i></a> <input type="text" name="<?php echo esc_attr( $base ); ?>[address]" id="<?php echo esc_attr( $id_base ); ?>address" value="<?php echo esc_attr( $location['address'] ); ?>" class="wpforms-geolocation-map-field-location-address" placeholder="<?php esc_attr_e( 'Address', 'wpforms-lite' ); ?>"> <input type="hidden" name="<?php echo esc_attr( $base ); ?>[latitude]" value="<?php echo esc_attr( $location['latitude'] ); ?>" class="wpforms-geolocation-map-field-location-latitude"> <input type="hidden" name="<?php echo esc_attr( $base ); ?>[longitude]" value="<?php echo esc_attr( $location['longitude'] ); ?>" class="wpforms-geolocation-map-field-location-longitude"> <input type="text" name="<?php echo esc_attr( $base ); ?>[description]" value="<?php echo esc_attr( $location['description'] ); ?>" class="wpforms-geolocation-map-field-location-description" placeholder="<?php esc_attr_e( 'Description', 'wpforms-lite' ); ?>"> <select name="<?php echo esc_attr( $base ); ?>[marker_type]" class="wpforms-geolocation-map-field-location-marker-type"> <option value="icon" <?php selected( 'icon', $location['marker_type'] ); ?>><?php esc_html_e( 'Icon', 'wpforms-lite' ); ?></option> <option value="image" <?php selected( 'image', $location['marker_type'] ); ?>><?php esc_html_e( 'Image', 'wpforms-lite' ); ?></option> </select> <select name="<?php echo esc_attr( $base ); ?>[size]" class="wpforms-geolocation-map-field-location-size"> <option value="small" <?php selected( 'small', $location['size'] ); ?>><?php esc_html_e( 'Small', 'wpforms-lite' ); ?></option> <option value="medium" <?php selected( 'medium', $location['size'] ); ?>><?php esc_html_e( 'Medium', 'wpforms-lite' ); ?></option> <option value="large" <?php selected( 'large', $location['size'] ); ?>><?php esc_html_e( 'Large', 'wpforms-lite' ); ?></option> </select> <?php // Icon Choice. ?> <div class="wpforms-icon-select"> <i class="ic-fa-preview ic-fa-<?php echo esc_attr( $location['icon_style'] ); ?> ic-fa-<?php echo esc_attr( $location['icon'] ); ?>"></i> <span><?php echo esc_html( $location['icon'] ); ?></span> <input type="hidden" name="<?php echo esc_attr( $base ); ?>[icon]" value="<?php echo esc_attr( $location['icon'] ); ?>" class="source-icon"> <input type="hidden" name="<?php echo esc_attr( $base ); ?>[icon_style]" value="<?php echo esc_attr( $location['icon_style'] ); ?>" class="source-icon-style"> </div> <div class="wpforms-geolocation-map-field-location-icon-color wpforms-panel-field-color wpforms-panel-field-colorpicker"> <input type="text" name="<?php echo esc_attr( $base ); ?>[icon_color]" value="<?php echo esc_attr( $location['icon_color'] ); ?>" class="wpforms-color-picker" data-swatches="#D63638|#E27730|#FFB900|#00A32A|#0399ED|#036AAB|#7A30E2|#E230BB" data-fallback-color="<?php echo esc_attr( $location['icon_color'] ); ?>"> </div> <?php // Image Choice. ?> <div class="wpforms-image-upload"> <button class="wpforms-btn wpforms-btn-sm wpforms-btn-blue wpforms-btn-block wpforms-image-upload-add" data-after-upload="hide"<?php echo $has_image ? ' style="display:none;"' : ''; ?>><?php esc_html_e( 'Upload Image', 'wpforms-lite' ); ?></button> <input type="hidden" name="<?php echo esc_attr( $base ); ?>[image]" value="<?php echo esc_url_raw( $location['image'] ); ?>" class="source"> <div class="preview"><?php if ( $has_image ) { ?> <img src="<?php echo esc_url_raw( $location['image'] ); ?>"><a href="#" title="<?php esc_attr_e( 'Remove Image', 'wpforms-lite' ); ?>" class="wpforms-image-upload-remove"><i class="fa fa-trash-o"></i></a> <?php } ?></div> </div> </li> <?php } /** * Get search radius options in kilometers. * * @since 1.10.0 * * @return array */ private function get_search_radius_km_options(): array { return [ 10 => esc_html__( '10 km', 'wpforms-lite' ), 25 => esc_html__( '25 km', 'wpforms-lite' ), 50 => esc_html__( '50 km', 'wpforms-lite' ), 100 => esc_html__( '100 km', 'wpforms-lite' ), ]; } /** * Get search radius options in miles. * * @since 1.10.0 * * @return array */ private function get_search_radius_miles_options(): array { return [ 10 => esc_html__( '10 mi', 'wpforms-lite' ), 25 => esc_html__( '25 mi', 'wpforms-lite' ), 50 => esc_html__( '50 mi', 'wpforms-lite' ), 100 => esc_html__( '100 mi', 'wpforms-lite' ), ]; } } Fields/Addons/LikertScale/Field.php 0000644 00000022750 15252506741 0013135 0 ustar 00 <?php namespace WPForms\Forms\Fields\Addons\LikertScale; use WPForms\Forms\Fields\Traits\ProField as ProFieldTrait; use WPForms_Field; /** * Likert Scale field. * * @since 1.9.4 */ class Field extends WPForms_Field { use ProFieldTrait; /** * Primary class constructor. * * @since 1.9.4 */ public function init() { // Define field type information. $this->name = esc_html__( 'Likert Scale', 'wpforms-lite' ); $this->keywords = esc_html__( 'survey, rating scale', 'wpforms-lite' ); $this->type = 'likert_scale'; $this->icon = 'fa-ellipsis-h'; $this->order = 400; $this->group = 'fancy'; $this->addon_slug = 'surveys-polls'; $this->default_settings = [ 'size' => 'large', 'style' => 'modern', 'survey' => '1', 'rows' => [ 1 => esc_html__( 'Item #1', 'wpforms-lite' ), 2 => esc_html__( 'Item #2', 'wpforms-lite' ), 3 => esc_html__( 'Item #3', 'wpforms-lite' ), ], 'columns' => [ 1 => esc_html__( 'Strongly Disagree', 'wpforms-lite' ), 2 => esc_html__( 'Disagree', 'wpforms-lite' ), 3 => esc_html__( 'Neutral', 'wpforms-lite' ), 4 => esc_html__( 'Agree', 'wpforms-lite' ), 5 => esc_html__( 'Strongly Agree', 'wpforms-lite' ), ], ]; $this->init_pro_field(); $this->hooks(); } /** * Add hooks. * * @since 1.9.4 */ protected function hooks() {} /** * Field options panel inside the builder. * * @since 1.9.4 * * @param array $field Field settings. */ public function field_options( $field ) { /** * Basic field options. */ // Options open markup. $this->field_option( 'basic-options', $field, [ 'markup' => 'open', 'after_title' => $this->get_field_options_notice(), ] ); // Label. $this->field_option( 'label', $field ); // Rows. $values = ! empty( $field['rows'] ) ? $field['rows'] : $this->default_settings['rows']; $lbl = $this->field_element( 'label', $field, [ 'slug' => 'rows', 'value' => esc_html__( 'Rows', 'wpforms-lite' ), 'tooltip' => esc_html__( 'Add rows to the likert scale.', 'wpforms-lite' ), ], false ); $fld = sprintf( '<ul id="wpforms-field-option-%1$d-rows-list" data-next-id="%2$s" class="choices-list wpforms-undo-redo-container %3$s" data-field-id="%1$d" data-field-type="%4$s" data-choice-type="%5$s">', esc_attr( $field['id'] ), max( array_keys( $values ) ) + 1, ! empty( $field['single_row'] ) ? 'wpforms-hidden' : '', $this->type, 'rows' ); foreach ( $values as $key => $value ) { $fld .= sprintf( '<li data-key="%d">', $key ); $fld .= '<span class="move"><i class="fa fa-grip-lines" aria-hidden="true"></i></span>'; $fld .= sprintf( '<input type="text" name="fields[%s][rows][%s]" value="%s" class="label">', esc_attr( $field['id'] ), $key, esc_attr( $value ) ); $fld .= '<a class="add" href="#" title="' . esc_attr__( 'Add likert scale row', 'wpforms-lite' ) . '"><i class="fa fa-plus-circle"></i></a>'; $fld .= '<a class="remove" href="# title="' . esc_attr__( 'Remove likert scale row', 'wpforms-lite' ) . '"><i class="fa fa-minus-circle"></i></a>'; $fld .= '</li>'; } $fld .= '</ul>'; $this->field_element( 'row', $field, [ 'slug' => 'rows', 'content' => $lbl . $fld, ] ); // Single rows. $this->field_element( 'row', $field, [ 'slug' => 'single_row', 'content' => $this->field_element( 'toggle', $field, [ 'slug' => 'single_row', 'value' => isset( $field['single_row'] ) ? '1' : '0', 'desc' => esc_html__( 'Make this a single-row rating scale', 'wpforms-lite' ), 'tooltip' => esc_html__( 'Check this option to make this a single-row rating scale and remove the row choices.', 'wpforms-lite' ), ], false ), ] ); // Multiple row responses. $this->field_element( 'row', $field, [ 'slug' => 'multiple_responses', 'content' => $this->field_element( 'toggle', $field, [ 'slug' => 'multiple_responses', 'value' => isset( $field['multiple_responses'] ) ? '1' : '0', 'desc' => esc_html__( 'Allow multiple responses per row', 'wpforms-lite' ), 'tooltip' => esc_html__( 'Check this option to allow multiple responses per row (uses checkboxes).', 'wpforms-lite' ), ], false ), ] ); // Columns. $values = ! empty( $field['columns'] ) ? $field['columns'] : $this->default_settings['columns']; $lbl = $this->field_element( 'label', $field, [ 'slug' => 'columns', 'value' => esc_html__( 'Columns', 'wpforms-lite' ), 'tooltip' => esc_html__( 'Add columns to the likert scale.', 'wpforms-lite' ), ], false ); $fld = sprintf( '<ul id="wpforms-field-option-%1$d-columns-list" data-next-id="%2$s" class="choices-list wpforms-undo-redo-container" data-field-id="%1$d" data-field-type="%3$s" data-choice-type="%4$s">', esc_attr( $field['id'] ), max( array_keys( $values ) ) + 1, $this->type, 'columns' ); foreach ( $values as $key => $value ) { $fld .= sprintf( '<li data-key="%d">', $key ); $fld .= '<span class="move"><i class="fa fa-grip-lines" aria-hidden="true"></i></span>'; $fld .= sprintf( '<input type="text" name="fields[%s][columns][%s]" value="%s">', $field['id'], $key, esc_attr( $value ) ); $fld .= '<a class="add" href="#" title="' . esc_attr__( 'Add likert scale column', 'wpforms-lite' ) . '"><i class="fa fa-plus-circle"></i></a>'; $fld .= '<a class="remove" href="# title="' . esc_attr__( 'Remove likert scale column', 'wpforms-lite' ) . '"><i class="fa fa-minus-circle"></i></a>'; $fld .= '</li>'; } $fld .= '</ul>'; $this->field_element( 'row', $field, [ 'slug' => 'columns', 'content' => $lbl . $fld, ] ); // Description. $this->field_option( 'description', $field ); // Required toggle. $this->field_option( 'required', $field ); // Options close markup. $this->field_option( 'basic-options', $field, [ 'markup' => 'close', ] ); /* * Advanced field options. */ // Options open markup. $this->field_option( 'advanced-options', $field, [ 'markup' => 'open', ] ); // Style (theme). $lbl = $this->field_element( 'label', $field, [ 'slug' => 'style', 'value' => esc_html__( 'Style', 'wpforms-lite' ), 'tooltip' => esc_html__( 'Select the style for the likert scale.', 'wpforms-lite' ), ], false ); $fld = $this->field_element( 'select', $field, [ 'slug' => 'style', 'value' => ! empty( $field['style'] ) ? esc_attr( $field['style'] ) : 'modern', 'options' => [ 'modern' => esc_html__( 'Modern', 'wpforms-lite' ), 'classic' => esc_html__( 'Classic', 'wpforms-lite' ), ], ], false ); $this->field_element( 'row', $field, [ 'slug' => 'style', 'content' => $lbl . $fld, ] ); // Size. $this->field_option( 'size', $field ); // Custom CSS classes. $this->field_option( 'css', $field ); // Hide label. $this->field_option( 'label_hide', $field ); // Options close markup. $this->field_option( 'advanced-options', $field, [ 'markup' => 'close', ] ); } /** * Field preview inside the builder. * * @since 1.9.4 * * @param array $field Field settings. */ public function field_preview( $field ) { // phpcs:ignore Generic.Metrics.CyclomaticComplexity.TooHigh // Define data. $rows = ! empty( $field['rows'] ) ? $field['rows'] : $this->default_settings['rows']; $columns = ! empty( $field['columns'] ) ? $field['columns'] : $this->default_settings['columns']; $input_type = ! empty( $field['multiple_responses'] ) ? 'checkbox' : 'radio'; $style = ! empty( $field['style'] ) ? sanitize_html_class( $field['style'] ) : 'modern'; $single = ! empty( $field['single_row'] ); $width = $single ? round( 100 / count( $columns ), 4 ) : round( 80 / count( $columns ), 4 ); // Label. $this->field_preview_option( 'label', $field, [ 'label_badge' => $this->get_field_preview_badge(), ] ); ?> <table class="<?php echo esc_attr( $style ); ?><?php echo $single ? ' single-row' : ''; ?>"> <thead> <tr> <?php if ( ! $single ) { echo '<th style="width:20%;"></th>'; } foreach ( $columns as $column ) { printf( '<th style="width:%d%%;">%s</th>', esc_attr( $width ), esc_html( sanitize_text_field( $column ) ) ); } ?> </tr> </thead> <tbody> <?php foreach ( $rows as $row ) { echo '<tr>'; if ( ! $single ) { echo '<th>' . esc_html( sanitize_text_field( $row ) ) . '</th>'; } /** * Column is needed for foreach syntax. * * @noinspection PhpUnusedLocalVariableInspection */ foreach ( $columns as $column ) { echo '<td>'; echo '<input type="' . esc_attr( $input_type ) . '" readonly>'; echo '<label></label>'; echo '</td>'; } echo '</tr>'; if ( $single ) { break; } } ?> </tbody> </table> <?php // Description. $this->field_preview_option( 'description', $field ); // Hide remaining elements. $this->field_preview_option( 'hide-remaining', $field ); } /** * Field display on the form front-end. * * @since 1.9.4 * * @param array $field Field settings. * @param array $deprecated Deprecated array. * @param array $form_data Form data and settings. */ public function field_display( $field, $deprecated, $form_data ) { } } Fields/Addons/Coupon/Field.php 0000644 00000016445 15252506741 0012202 0 ustar 00 <?php namespace WPForms\Forms\Fields\Addons\Coupon; use WPForms\Forms\Fields\Traits\ProField as ProFieldTrait; use WPForms_Field; /** * Coupon Field class. * * @since 1.0.0 */ class Field extends WPForms_Field { use ProFieldTrait; /** * Whether the addon is active. * * @since 1.9.4 * * @var bool */ private $is_addon_active = false; /** * Define field type information. * * @since 1.9.4 */ public function init() { // Define field type information. $this->name = esc_html__( 'Coupon', 'wpforms-lite' ); $this->keywords = esc_html__( 'discount, sale', 'wpforms-lite' ); $this->type = 'payment-coupon'; $this->icon = 'fa-ticket'; $this->order = 100; $this->group = 'payment'; $this->addon_slug = 'coupons'; $this->is_addon_active = function_exists( 'wpforms_' . $this->addon_slug ); $this->init_pro_field(); $this->hooks(); } /** * Define field hooks. * * @since 1.9.4 */ protected function hooks() { add_filter( 'wpforms_field_new_display_duplicate_button', [ $this, 'field_display_duplicate_button' ], 20, 2 ); add_filter( 'wpforms_field_preview_display_duplicate_button', [ $this, 'field_display_duplicate_button' ], 20, 2 ); } /** * Disallow field preview "Duplicate" button. * * @since 1.9.4 * * @param bool|mixed $display Display switch. * @param array $field Field settings. * * @return bool */ public function field_display_duplicate_button( $display, array $field ) { return $field['type'] === $this->type ? false : $display; } /** * Define additional field options. * * @since 1.9.4 * * @param array $field Field data and settings. */ public function field_options( $field ) { // Options open markup. $this->field_option( 'basic-options', $field, [ 'markup' => 'open', 'after_title' => $this->get_field_options_notice(), ] ); $this->field_option( 'label', $field ); $this->field_option( 'description', $field ); $coupons = []; $form_coupons = []; if ( $this->is_addon_active ) { $coupons = wpforms_coupons()->get( 'repository' )->get_coupons( [ 'limit' => -1, 'fields' => 'id=>name', ] ); $form_coupons = wpforms_coupons()->get( 'repository' )->get_form_coupons( $this->get_form_id() ); } $warning = sprintf( '<p class="wpforms-alert wpforms-alert-warning%1$s">%2$s</p>', empty( $form_coupons ) && empty( $this->is_disabled_field ) ? '' : ' wpforms-hidden', esc_html__( 'You haven\'t selected any coupons that can be used with this form. Please choose at least one coupon.', 'wpforms-lite' ) ); $coupons_field_label = $this->field_element( 'label', $field, [ 'slug' => 'allowed_coupons', 'value' => esc_html__( 'Allowed Coupons', 'wpforms-lite' ), 'tooltip' => esc_html__( 'Choose coupons that can be used in the field.', 'wpforms-lite' ), ], false ); $coupons_field = $this->get_allowed_coupons_field( $coupons, $form_coupons, $field ); $allowed_forms_json = sprintf( '<input type="hidden" name="fields[%1$s][allowed_coupons_json]" class="wpforms-coupons-allowed_coupons_json" value="%2$s">', $field['id'], wp_json_encode( $form_coupons ) ); $this->field_element( 'row', $field, [ 'slug' => 'allowed_coupons', 'content' => $coupons_field_label . $coupons_field . $allowed_forms_json . $warning, ] ); $this->field_option( 'required', $field ); $this->field_option( 'basic-options', $field, [ 'markup' => 'close' ] ); $this->field_option( 'advanced-options', $field, [ 'markup' => 'open' ] ); $this->field_option( 'button_text', $field ); $button_text_label = $this->field_element( 'label', $field, [ 'slug' => 'button_text', 'value' => esc_html__( 'Button Text', 'wpforms-lite' ), 'tooltip' => esc_html__( 'Change button text.', 'wpforms-lite' ), ], false ); $button_text_field = $this->field_element( 'text', $field, [ 'slug' => 'button_text', 'value' => isset( $field['button_text'] ) && ! wpforms_is_empty_string( $field['button_text'] ) ? $field['button_text'] : esc_html__( 'Apply', 'wpforms-lite' ), ], false ); $this->field_element( 'row', $field, [ 'slug' => 'button_text', 'content' => $button_text_label . $button_text_field, ] ); $this->field_option( 'css', $field ); $this->field_option( 'label_hide', $field ); $this->field_option( 'advanced-options', $field, [ 'markup' => 'close' ] ); } /** * Get allowed coupons' field. * * @since 1.9.4 * * @param array $coupons Coupons. * @param array $form_coupons Form coupons. * @param array $field Field data. * * @return string * @noinspection HtmlUnknownAttribute */ private function get_allowed_coupons_field( array $coupons, array $form_coupons, array $field ): string { $output = sprintf( '<select id="wpforms-field-option-%1$d-%2$s" name="fields[%1$d][%2$s]" multiple>', $field['id'], 'allowed_coupons' ); foreach ( $coupons as $arg_key => $arg_option ) { $selected = selected( true, in_array( $arg_key, $form_coupons, true ), false ); $output .= sprintf( '<option value="%s" %s>%s</option>', esc_attr( $arg_key ), $selected, $arg_option ); } $output .= '</select>'; return $output; } /** * Field preview inside the builder. * * @since 1.9.4 * * @param array $field Field data. */ public function field_preview( $field ) { // Label. $this->field_preview_option( 'label', $field, [ 'label_badge' => $this->get_field_preview_badge(), ] ); $allowed_coupons = []; if ( $this->is_addon_active ) { $allowed_coupons = wpforms_coupons()->get( 'repository' )->get_form_coupons( $this->get_form_id() ); } printf( '<div class="wpforms-field-payment-coupon-wrapper"> <input type="text" class="wpforms-field-payment-coupon-input"> <button type="button" aria-live="assertive" class="wpforms-field-payment-coupon-button">%1$s</button> <i class="fa fa-exclamation-triangle%2$s"></i> </div>', esc_html( $this->get_button_text( $field ) ), empty( $allowed_coupons ) && empty( $this->is_disabled_field ) ? '' : ' wpforms-hidden' ); // Description. $this->field_preview_option( 'description', $field ); // Hide remaining elements. $this->field_preview_option( 'hide-remaining', $field ); } /** * Get form ID. In AJAX requests the $form_id property doesn't exist. * * @since 1.9.4 * * @return bool|int */ protected function get_form_id() { if ( $this->form_id ) { return $this->form_id; } // phpcs:ignore WordPress.Security.NonceVerification.Missing $this->form_id = isset( $_POST['id'] ) ? absint( $_POST['id'] ) : false; return $this->form_id; } /** * Get the apply button text. * * @since 1.9.4 * * @param array $field Field data. * * @return string */ protected function get_button_text( array $field ): string { return isset( $field['button_text'] ) && ! wpforms_is_empty_string( $field['button_text'] ) ? $field['button_text'] : __( 'Apply', 'wpforms-lite' ); } /** * Field display on the frontend. * * @since 1.9.4 * * @param array $field Field data. * @param array $deprecated Field attributes. * @param array $form_data Form data. */ public function field_display( $field, $deprecated, $form_data ) { } } Fields/Addons/Signature/Field.php 0000644 00000011631 15252506741 0012670 0 ustar 00 <?php namespace WPForms\Forms\Fields\Addons\Signature; use WPForms\Forms\Fields\Traits\ProField as ProFieldTrait; use WPForms_Field; /** * Signature field. * * @since 1.9.4 */ class Field extends WPForms_Field { use ProFieldTrait; /** * Init class. * * @since 1.9.4 */ public function init() { // Define field type information. $this->name = esc_html__( 'Signature', 'wpforms-lite' ); $this->keywords = esc_html__( 'user, e-signature', 'wpforms-lite' ); $this->type = 'signature'; $this->icon = 'fa-pencil'; $this->order = 200; $this->group = 'fancy'; $this->addon_slug = 'signatures'; $this->default_settings = [ 'size' => 'large', 'input_method' => 'draw', ]; $this->init_pro_field(); $this->hooks(); } /** * Add hooks. * * @since 1.9.4 */ protected function hooks() { add_action( 'wpforms_builder_enqueues', [ $this, 'builder_enqueues' ] ); } /** * Enqueue builder assets. * * @since 2.0.0 */ public function builder_enqueues(): void { $min = wpforms_get_min_suffix(); wp_enqueue_script( 'wpforms-signature-field', WPFORMS_PLUGIN_URL . "assets/js/admin/builder/fields/signature{$min}.js", [ 'wpforms-builder', 'wpforms-utils' ], WPFORMS_VERSION, false ); } /** * Field options panel inside the builder. * * @since 1.9.4 * * @param array $field Field settings. */ public function field_options( $field ) { /** * Basic field options. */ // Options open markup. $this->field_option( 'basic-options', $field, [ 'markup' => 'open', 'after_title' => $this->get_field_options_notice(), ] ); // Label. $this->field_option( 'label', $field ); // Description. $this->field_option( 'description', $field ); // Required toggle. $this->field_option( 'required', $field ); // Options close markup. $this->field_option( 'basic-options', $field, [ 'markup' => 'close', ] ); /* * Advanced field options. */ // Options open markup. $this->field_option( 'advanced-options', $field, [ 'markup' => 'open', ] ); // Input method selector. $lbl = $this->field_element( 'label', $field, [ 'slug' => 'input_method', 'value' => esc_html__( 'Input Method', 'wpforms-lite' ), 'tooltip' => esc_html__( 'Select how users provide their signature.', 'wpforms-lite' ), ], false ); $input_method = isset( $field['input_method'] ) ? sanitize_key( $field['input_method'] ) : 'draw'; $fld = $this->field_element( 'select', $field, [ 'slug' => 'input_method', 'value' => $input_method, 'options' => [ 'draw' => esc_html__( 'Draw', 'wpforms-lite' ), 'type' => esc_html__( 'Type', 'wpforms-lite' ), ], ], false ); $this->field_element( 'row', $field, [ 'slug' => 'input_method', 'content' => $lbl . $fld, ] ); // Ink color picker. $lbl = $this->field_element( 'label', $field, [ 'slug' => 'ink_color', 'value' => esc_html__( 'Ink Color', 'wpforms-lite' ), 'tooltip' => esc_html__( 'Select the color for the signature ink.', 'wpforms-lite' ), ], false ); $ink_color = isset( $field['ink_color'] ) ? wpforms_sanitize_hex_color( $field['ink_color'] ) : ''; $ink_color = empty( $ink_color ) ? '#000000' : $ink_color; $fld = $this->field_element( 'color', $field, [ 'slug' => 'ink_color', 'value' => $ink_color, 'data' => [ 'fallback-color' => $ink_color, ], ], false ); $this->field_element( 'row', $field, [ 'slug' => 'ink_color', 'content' => $lbl . $fld, // The builder script ( signature.js ) hides this row in Type mode, where ink color does not apply. 'class' => 'color-picker-row', ] ); // Custom CSS classes. $this->field_option( 'css', $field ); // Size. $this->field_option( 'size', $field ); // Hide label. $this->field_option( 'label_hide', $field ); // Options close markup. $this->field_option( 'advanced-options', $field, [ 'markup' => 'close', ] ); } /** * Field preview inside the builder. * * @since 1.9.4 * * @param array $field Field settings. */ public function field_preview( $field ) { // Label. $this->field_preview_option( 'label', $field, [ 'label_badge' => $this->get_field_preview_badge(), ] ); // Signature placeholder. echo '<div class="wpforms-signature-wrap"></div>'; // Description. $this->field_preview_option( 'description', $field ); // Hide remaining elements. $this->field_preview_option( 'hide-remaining', $field ); } /** * Field display on the form front-end. * * @since 1.9.4 * * @param array $field Field settings. * @param array $deprecated Deprecated array. * @param array $form_data Form data and settings. */ public function field_display( $field, $deprecated, $form_data ) { } } Fields/Addons/NetPromoterScore/Field.php 0000644 00000012767 15252506741 0014214 0 ustar 00 <?php namespace WPForms\Forms\Fields\Addons\NetPromoterScore; use WPForms\Forms\Fields\Traits\ProField as ProFieldTrait; use WPForms_Field; /** * Net Promoter Score field. * * @since 1.9.4 */ class Field extends WPForms_Field { use ProFieldTrait; /** * Primary class constructor. * * @since 1.9.4 */ public function init() { // Define field type information. $this->name = esc_html__( 'Net Promoter Score', 'wpforms-lite' ); $this->keywords = esc_html__( 'survey, nps', 'wpforms-lite' ); $this->type = 'net_promoter_score'; $this->icon = 'fa-tachometer'; $this->order = 410; $this->group = 'fancy'; $this->addon_slug = 'surveys-polls'; $this->default_settings = [ 'size' => 'large', 'survey' => '1', 'style' => 'modern', ]; $this->init_pro_field(); $this->hooks(); } /** * Add hooks. * * @since 1.9.4 */ protected function hooks() { } /** * Field options panel inside the builder. * * @since 1.9.4 * * @param array $field Field settings. */ public function field_options( $field ) { /** * Basic field options. */ // Options open markup. $this->field_option( 'basic-options', $field, [ 'markup' => 'open', 'after_title' => $this->get_field_options_notice(), ] ); // Label. $this->field_option( 'label', $field ); // Description. $this->field_option( 'description', $field ); // Required toggle. $this->field_option( 'required', $field ); // Options close markup. $this->field_option( 'basic-options', $field, [ 'markup' => 'close', ] ); /* * Advanced field options. */ // Options open markup. $this->field_option( 'advanced-options', $field, [ 'markup' => 'open', ] ); // Style (theme). $lbl = $this->field_element( 'label', $field, [ 'slug' => 'style', 'value' => esc_html__( 'Style', 'wpforms-lite' ), 'tooltip' => esc_html__( 'Select the style for the net promoter score.', 'wpforms-lite' ), ], false ); $fld = $this->field_element( 'select', $field, [ 'slug' => 'style', 'value' => ! empty( $field['style'] ) ? esc_attr( $field['style'] ) : 'modern', 'options' => [ 'modern' => esc_html__( 'Modern', 'wpforms-lite' ), 'classic' => esc_html__( 'Classic', 'wpforms-lite' ), ], ], false ); $this->field_element( 'row', $field, [ 'slug' => 'style', 'content' => $lbl . $fld, ] ); // Size. $this->field_option( 'size', $field ); // Start label. $lowest_lbl_label = $this->field_element( 'label', $field, [ 'slug' => 'lowest_label', 'value' => esc_html__( 'Lowest Score Label', 'wpforms-lite' ), ], false ); $lowest_lbl_field = $this->field_element( 'text', $field, [ 'slug' => 'lowest_label', 'value' => $field['lowest_label'] ?? esc_html__( 'Not at all Likely', 'wpforms-lite' ), ], false ); $this->field_element( 'row', $field, [ 'slug' => 'lowest_label', 'content' => $lowest_lbl_label . $lowest_lbl_field, ] ); // End label. $highest_lbl_label = $this->field_element( 'label', $field, [ 'slug' => 'highest_label', 'value' => esc_html__( 'Highest Score Label', 'wpforms-lite' ), ], false ); $highest_lbl_field = $this->field_element( 'text', $field, [ 'slug' => 'highest_label', 'value' => $field['highest_label'] ?? esc_html__( 'Extremely Likely', 'wpforms-lite' ), ], false ); $this->field_element( 'row', $field, [ 'slug' => 'highest_label', 'content' => $highest_lbl_label . $highest_lbl_field, ] ); // Custom CSS classes. $this->field_option( 'css', $field ); // Hide label. $this->field_option( 'label_hide', $field ); // Options close markup. $this->field_option( 'advanced-options', $field, [ 'markup' => 'close', ] ); } /** * Field preview inside the builder. * * @since 1.9.4 * * @param array $field Field settings. */ public function field_preview( $field ) { // Define data. $style = ! empty( $field['style'] ) ? sanitize_html_class( $field['style'] ) : 'modern'; // Label. $this->field_preview_option( 'label', $field, [ 'label_badge' => $this->get_field_preview_badge(), ] ); // Lowest/Highest labels. $lowest_label = $field['lowest_label'] ?? esc_html__( 'Not at all Likely', 'wpforms-lite' ); $highest_label = $field['highest_label'] ?? esc_html__( 'Extremely Likely', 'wpforms-lite' ); ?> <table class="<?php echo esc_attr( $style ); ?>"> <thead> <tr> <th colspan="11"> <span class="not-likely"><?php echo esc_html( $lowest_label ); ?></span> <span class="extremely-likely"><?php echo esc_html( $highest_label ); ?></span> </th> </tr> </thead> <tbody> <tr> <?php for ( $i = 0; $i < 11; $i++ ) { ?> <td> <input type="radio" readonly> <label><?php echo absint( $i ); ?></label> </td> <?php } ?> </tr> </tbody> </table> <?php // Description. $this->field_preview_option( 'description', $field ); // Hide remaining elements. $this->field_preview_option( 'hide-remaining', $field ); } /** * Field display on the form front-end. * * @since 1.9.4 * * @param array $field Field settings. * @param array $deprecated Deprecated array. * @param array $form_data Form data and settings. */ public function field_display( $field, $deprecated, $form_data ) { } } Fields/CustomCaptcha/Field.php 0000644 00000015414 15252506741 0012260 0 ustar 00 <?php namespace WPForms\Forms\Fields\CustomCaptcha; use WPForms\Forms\Fields\Traits\ProField as ProFieldTrait; use WPForms_Field; /** * Custom Captcha field. * * @since 1.9.4 */ class Field extends WPForms_Field { use ProFieldTrait; /** * The field type. * * @since 1.9.4 */ public const TYPE = 'captcha'; /** * Min & max values to participate in equation and operators. * * @since 1.9.4 * * @var array */ public $math; /** * Questions to ask. * * @since 1.9.4 * * @var array */ protected $qs; /** * * Init class. * * @since 1.9.4 */ public function init() { // Define field type information. $this->name = esc_html__( 'Custom Captcha', 'wpforms-lite' ); $this->keywords = esc_html__( 'spam, math, maths, question', 'wpforms-lite' ); $this->type = self::TYPE; $this->icon = 'fa-question-circle'; $this->order = 300; $this->group = 'fancy'; $this->allow_read_only = false; $this->qs = [ 1 => [ 'question' => esc_html__( 'What is 7+4?', 'wpforms-lite' ), 'answer' => esc_html__( '11', 'wpforms-lite' ), ], ]; $this->math = [ 'min' => 1, 'max' => 15, 'cal' => [ '+', '*' ], ]; $this->init_pro_field(); $this->hooks(); } /** * Register hooks. * * @since 1.9.4 */ protected function hooks() { } /** * Field options panel inside the builder. * * @since 1.9.4 * * @param array $field Field settings. */ public function field_options( $field ) { // Defaults. $format = ! empty( $field['format'] ) ? esc_attr( $field['format'] ) : 'math'; $qs = ! empty( $field['questions'] ) ? $field['questions'] : $this->qs; $qs = array_filter( $qs ); // Field is always required. $this->field_element( 'text', $field, [ 'type' => 'hidden', 'slug' => 'required', 'value' => '1', ] ); /* * Basic field options. */ // Options open markup. $this->field_option( 'basic-options', $field, [ 'markup' => 'open', 'after_title' => $this->get_field_options_notice(), ] ); // Label. $this->field_option( 'label', $field ); // Format. $lbl = $this->field_element( 'label', $field, [ 'slug' => 'format', 'value' => esc_html__( 'Type', 'wpforms-lite' ), 'tooltip' => esc_html__( 'Select type of captcha to use.', 'wpforms-lite' ), ], false ); $fld = $this->field_element( 'select', $field, [ 'slug' => 'format', 'value' => $format, 'options' => [ 'math' => esc_html__( 'Math', 'wpforms-lite' ), 'qa' => esc_html__( 'Question and Answer', 'wpforms-lite' ), ], ], false ); $this->field_element( 'row', $field, [ 'slug' => 'format', 'content' => $lbl . $fld, ] ); // Questions. $lbl = $this->field_element( 'label', $field, [ 'slug' => 'questions', 'value' => esc_html__( 'Questions and Answers', 'wpforms-lite' ), 'tooltip' => esc_html__( 'Add questions to ask the user. Questions are randomly selected.', 'wpforms-lite' ), ], false ); $fld = sprintf( '<ul id="wpforms-field-option-%1$d-questions-list" data-next-id="%2$s" data-field-id="%1$d" data-field-type="%3$s" class="choices-list wpforms-undo-redo-container">', esc_attr( $field['id'] ), max( array_keys( $qs ) ) + 1, esc_attr( $this->type ) ); foreach ( $qs as $key => $value ) { $fld .= '<li data-key="' . absint( $key ) . '">'; $fld .= sprintf( '<input type="text" name="fields[%1$d][questions][%2$s][question]" value="%3$s" data-prev-value="%3$s" class="question" placeholder="%4$s">', (int) $field['id'], esc_attr( $key ), esc_attr( $value['question'] ), esc_html__( 'Question', 'wpforms-lite' ) ); $fld .= '<a class="add" href="#"><i class="fa fa-plus-circle"></i></a><a class="remove" href="#"><i class="fa fa-minus-circle"></i></a>'; $fld .= sprintf( '<input type="text" name="fields[%d][questions][%s][answer]" value="%s" class="answer" placeholder="%s">', (int) $field['id'], esc_attr( $key ), esc_attr( $value['answer'] ), esc_html__( 'Answer', 'wpforms-lite' ) ); $fld .= '</li>'; } $fld .= '</ul>'; $this->field_element( 'row', $field, [ 'slug' => 'questions', 'content' => $lbl . $fld, 'class' => $format === 'math' ? 'wpforms-hidden' : '', ] ); // Description. $this->field_option( 'description', $field ); // Options close markup. $this->field_option( 'basic-options', $field, [ 'markup' => 'close', ] ); /* * Advanced field options. */ // Options open markup. $this->field_option( 'advanced-options', $field, [ 'markup' => 'open', ] ); // Size. $this->field_option( 'size', $field, [ 'class' => $format === 'math' ? 'wpforms-hidden' : '', ] ); // Custom CSS classes. $this->field_option( 'css', $field ); // Placeholder. $this->field_option( 'placeholder', $field ); // Hide Label. $this->field_option( 'label_hide', $field ); // Options close markup. $this->field_option( 'advanced-options', $field, [ 'markup' => 'close', ] ); } /** * Field preview inside the builder. * * @since 1.9.4 * * @param array $field Field settings. */ public function field_preview( $field ) { // Define data. $placeholder = ! empty( $field['placeholder'] ) ? $field['placeholder'] : ''; $format = ! empty( $field['format'] ) ? $field['format'] : 'math'; $num1 = wp_rand( $this->math['min'], $this->math['max'] ); $num2 = wp_rand( $this->math['min'], $this->math['max'] ); $cal = $this->math['cal'][ wp_rand( 0, count( $this->math['cal'] ) - 1 ) ]; $questions = ! empty( $field['questions'] ) ? $field['questions'] : $this->qs; // Label. $this->field_preview_option( 'label', $field, [ 'label_badge' => $this->get_field_preview_badge(), ] ); $first_question = array_shift( $questions ); ?> <div class="format-selected-<?php echo esc_attr( $format ); ?> format-selected"> <span class="wpforms-equation"><?php echo esc_html( "$num1 $cal $num2 = " ); ?></span> <p class="wpforms-question"><?php echo wp_kses( $first_question['question'], wpforms_builder_preview_get_allowed_tags() ); ?></p> <input type="text" placeholder="<?php echo esc_attr( $placeholder ); ?>" class="primary-input" readonly> </div> <?php // Description. $this->field_preview_option( 'description', $field ); } /** * Field display on the form front-end. * * @since 1.9.4 * * @param array $field Field settings. * @param array $deprecated Deprecated array. * @param array $form_data Form data and settings. */ public function field_display( $field, $deprecated, $form_data ) { } } Fields/CreditCard/Field.php 0000644 00000015661 15252506741 0011532 0 ustar 00 <?php namespace WPForms\Forms\Fields\CreditCard; use WPForms\Forms\Fields\Traits\ProField as ProFieldTrait; use WPForms_Field; /** * Credit card field (legacy). * * @since 1.0.0 */ class Field extends WPForms_Field { use ProFieldTrait; /** * Primary class constructor. * * @since 1.0.0 */ public function init() { // Define field type information. $this->name = esc_html__( 'Credit Card', 'wpforms-lite' ); $this->type = 'credit-card'; $this->icon = 'fa-credit-card'; $this->order = 90; $this->group = 'payment'; $this->init_pro_field(); $this->hooks(); } /** * Hooks. * * @since 1.8.1 */ protected function hooks(): void { } /** * Field options panel inside the builder. * * @since 1.0.0 * * @param array $field Field settings. */ public function field_options( $field ) { /* * Basic field options. */ // Options open markup. $this->field_option( 'basic-options', $field, [ 'markup' => 'open', 'after_title' => $this->get_field_options_notice(), ] ); // Label. $this->field_option( 'label', $field ); // Description. $this->field_option( 'description', $field ); // Required toggle. $this->field_option( 'required', $field ); // Options close markup. $args = [ 'markup' => 'close', ]; $this->field_option( 'basic-options', $field, $args ); /* * Advanced field options. */ // Options open markup. $args = [ 'markup' => 'open', ]; $this->field_option( 'advanced-options', $field, $args ); // Size. $this->field_option( 'size', $field ); // Card Number. $cardnumber_placeholder = ! empty( $field['cardnumber_placeholder'] ) ? esc_attr( $field['cardnumber_placeholder'] ) : ''; printf( '<div class="wpforms-clear wpforms-field-option-row wpforms-field-option-row-cardnumber" id="wpforms-field-option-row-%1$d-cardnumber" data-subfield="cardnumber" data-field-id="%1$d">', absint( $field['id'] ) ); $this->field_element( 'label', $field, [ 'slug' => 'cardnumber_placeholder', 'value' => esc_html__( 'Card Number Placeholder Text', 'wpforms-lite' ), ] ); echo '<div class="placeholder">'; printf( '<input type="text" class="placeholder-update" id="wpforms-field-option-%1$d-cardnumber_placeholder" name="fields[%1$d][cardnumber_placeholder]" value="%2$s" data-field-id="%1$d" data-subfield="credit-card-cardnumber">', absint( $field['id'] ), esc_attr( $cardnumber_placeholder ) ); echo '</div>'; echo '</div>'; // CVC/Security Code. $cardcvc_placeholder = ! empty( $field['cardcvc_placeholder'] ) ? $field['cardcvc_placeholder'] : ''; printf( '<div class="wpforms-clear wpforms-field-option-row wpforms-field-option-row-cvc" id="wpforms-field-option-row-%1$d-cvc" data-subfield="cvc" data-field-id="%1$d">', absint( $field['id'] ) ); $this->field_element( 'label', $field, [ 'slug' => 'cardcvc_placeholder', 'value' => esc_html__( 'Security Code Placeholder Text', 'wpforms-lite' ), ] ); echo '<div class="placeholder">'; printf( '<input type="text" class="placeholder-update" id="wpforms-field-option-%1$d-cardcvc_placeholder" name="fields[%1$d][cardcvc_placeholder]" value="%2$s" data-field-id="%1$d" data-subfield="credit-card-cardcvc">', absint( $field['id'] ), esc_attr( $cardcvc_placeholder ) ); echo '</div>'; echo '</div>'; // Card Name. $cardname_placeholder = ! empty( $field['cardname_placeholder'] ) ? $field['cardname_placeholder'] : ''; printf( '<div class="wpforms-clear wpforms-field-option-row wpforms-field-option-row-cardname" id="wpforms-field-option-row-%1$d-cardname" data-subfield="cardname" data-field-id="%1$d">', absint( $field['id'] ) ); $this->field_element( 'label', $field, [ 'slug' => 'cardname_placeholder', 'value' => esc_html__( 'Name on Card Placeholder Text', 'wpforms-lite' ), ] ); echo '<div class="placeholder">'; printf( '<input type="text" class="placeholder-update" id="wpforms-field-option-%1$d-cardname_placeholder" name="fields[%1$d][cardname_placeholder]" value="%2$s" data-field-id="%1$d" data-subfield="credit-card-cardname">', absint( $field['id'] ), esc_attr( $cardname_placeholder ) ); echo '</div>'; echo '</div>'; // Custom CSS classes. $this->field_option( 'css', $field ); // Hide Label. $this->field_option( 'label_hide', $field ); // Hide sublabels. $this->field_option( 'sublabel_hide', $field ); // Options close markup. $args = [ 'markup' => 'close', ]; $this->field_option( 'advanced-options', $field, $args ); } /** * Field preview inside the builder. * * @since 1.0.0 * * @param array $field Field settings. */ public function field_preview( $field ) { // Define data. $number_placeholder = ! empty( $field['cardnumber_placeholder'] ) ? esc_attr( $field['cardnumber_placeholder'] ) : ''; $cvc_placeholder = ! empty( $field['cardcvc_placeholder'] ) ? esc_attr( $field['cardcvc_placeholder'] ) : ''; $name_placeholder = ! empty( $field['cardname_placeholder'] ) ? esc_attr( $field['cardname_placeholder'] ) : ''; // Label. $this->field_preview_option( 'label', $field, [ 'label_badge' => $this->get_field_preview_badge(), ] ); ?> <div class="format-selected format-selected-full"> <div class="wpforms-field-row"> <div class="wpforms-credit-card-cardnumber"> <label class="wpforms-sub-label"><?php esc_html_e( 'Card Number', 'wpforms-lite' ); ?></label> <input type="text" placeholder="<?php echo esc_attr( $number_placeholder ); ?>" readonly> </div> <div class="wpforms-credit-card-cardcvc"> <label class="wpforms-sub-label"><?php esc_html_e( 'Security Code', 'wpforms-lite' ); ?></label> <input type="text" placeholder="<?php echo esc_attr( $cvc_placeholder ); ?>" readonly> </div> </div> <div class="wpforms-field-row"> <div class="wpforms-credit-card-cardname"> <label class="wpforms-sub-label"><?php esc_html_e( 'Name on Card', 'wpforms-lite' ); ?></label> <input type="text" placeholder="<?php echo esc_attr( $name_placeholder ); ?>" readonly> </div> <div class="wpforms-credit-card-expiration"> <label class="wpforms-sub-label"><?php esc_html_e( 'Expiration', 'wpforms-lite' ); ?></label> <div class="wpforms-credit-card-cardmonth"> <select readonly> <option>MM</option> </select> </div> <span>/</span> <div class="wpforms-credit-card-cardyear"> <select readonly> <option>YY</option> </select> </div> </div> </div> </div> <?php // Description. $this->field_preview_option( 'description', $field ); } /** * Field display on the form front-end. * * @since 1.0.0 * * @param array $field Field data and settings. * @param array $deprecated Deprecated field attributes. Use field properties. * @param array $form_data Form data and settings. */ public function field_display( $field, $deprecated, $form_data ) { } } Fields/Rating/Field.php 0000644 00000030366 15252506741 0010751 0 ustar 00 <?php namespace WPForms\Forms\Fields\Rating; use WPForms\Forms\Fields\Traits\ProField as ProFieldTrait; use WPForms_Field; /** * Rating field. * * @since 1.9.4 */ class Field extends WPForms_Field { use ProFieldTrait; /** * Default icon color. * * @since 1.9.4 */ protected const DEFAULT_ICON_COLOR = [ 'classic' => '#e27730', 'modern' => '#066aab', ]; /** * Primary class constructor. * * @since 1.9.4 */ public function init() { // Define field type information. $this->name = esc_html__( 'Rating', 'wpforms-lite' ); $this->keywords = esc_html__( 'review, emoji, star', 'wpforms-lite' ); $this->type = 'rating'; $this->icon = 'fa-star'; $this->order = 310; $this->group = 'fancy'; $this->default_settings = [ 'icon_color' => $this->get_default_icon_color(), ]; $this->init_pro_field(); $this->hooks(); } /** * Hooks. * * @since 1.9.4 */ protected function hooks(): void { add_action( 'wpforms_builder_enqueues', [ $this, 'builder_enqueues' ] ); } /** * Builder enqueues. * * @since 1.9.8 */ public function builder_enqueues(): void { $min = wpforms_get_min_suffix(); wp_enqueue_script( 'wpforms-rating-field', WPFORMS_PLUGIN_URL . "assets/js/admin/builder/fields/rating{$min}.js", [ 'wpforms-builder', 'wpforms-utils' ], WPFORMS_VERSION, false ); } /** * Field options panel inside the builder. * * @since 1.9.4 * * @param array $field Field settings. * * @noinspection PackedHashtableOptimizationInspection */ public function field_options( $field ) { /** * Basic field options. */ // Options open markup. $this->field_option( 'basic-options', $field, [ 'markup' => 'open', 'after_title' => $this->get_field_options_notice(), ] ); // Label. $this->field_option( 'label', $field ); // Description. $this->field_option( 'description', $field ); // Scale. $lbl = $this->field_element( 'label', $field, [ 'slug' => 'scale', 'value' => esc_html__( 'Scale', 'wpforms-lite' ), 'tooltip' => esc_html__( 'Select rating scale', 'wpforms-lite' ), ], false ); $fld = $this->field_element( 'select', $field, [ 'slug' => 'scale', 'value' => ! empty( $field['scale'] ) ? esc_attr( $field['scale'] ) : '5', 'options' => [ '2' => '2', '3' => '3', '4' => '4', '5' => '5', '6' => '6', '7' => '7', '8' => '8', '9' => '9', '10' => '10', ], ], false ); $this->field_element( 'row', $field, [ 'slug' => 'scale', 'content' => $lbl . $fld, ] ); // Required toggle. $this->field_option( 'required', $field ); // Options close markup. $this->field_option( 'basic-options', $field, [ 'markup' => 'close', ] ); /* * Advanced field options. */ // Options open markup. $this->field_option( 'advanced-options', $field, [ 'markup' => 'open', ] ); // Icon. $lbl = $this->field_element( 'label', $field, [ 'slug' => 'icon', 'value' => esc_html__( 'Icon', 'wpforms-lite' ), 'tooltip' => esc_html__( 'Select icon to display', 'wpforms-lite' ), ], false ); $fld = $this->field_element( 'select', $field, [ 'slug' => 'icon', 'value' => ! empty( $field['icon'] ) ? esc_attr( $field['icon'] ) : 'star', 'options' => [ 'star' => esc_html__( 'Star', 'wpforms-lite' ), 'heart' => esc_html__( 'Heart', 'wpforms-lite' ), 'thumb' => esc_html__( 'Thumb', 'wpforms-lite' ), 'smiley' => esc_html__( 'Smiley Face', 'wpforms-lite' ), ], ], false ); $this->field_element( 'row', $field, [ 'slug' => 'icon', 'content' => $lbl . $fld, ] ); // Icon size. $lbl = $this->field_element( 'label', $field, [ 'slug' => 'icon_size', 'value' => esc_html__( 'Icon Size', 'wpforms-lite' ), 'tooltip' => esc_html__( 'Select the size of the rating icon', 'wpforms-lite' ), ], false ); $fld = $this->field_element( 'select', $field, [ 'slug' => 'icon_size', 'value' => ! empty( $field['icon_size'] ) ? esc_attr( $field['icon_size'] ) : 'medium', 'options' => [ 'small' => esc_html__( 'Small', 'wpforms-lite' ), 'medium' => esc_html__( 'Medium', 'wpforms-lite' ), 'large' => esc_html__( 'Large', 'wpforms-lite' ), ], ], false ); $this->field_element( 'row', $field, [ 'slug' => 'icon_size', 'content' => $lbl . $fld, ] ); $this->score_labels( $field ); // Icon color picker. $lbl = $this->field_element( 'label', $field, [ 'slug' => 'icon_color', 'value' => esc_html__( 'Icon Color', 'wpforms-lite' ), 'tooltip' => esc_html__( 'Select the color for the rating icon', 'wpforms-lite' ), ], false ); $icon_color = isset( $field['icon_color'] ) ? wpforms_sanitize_hex_color( $field['icon_color'] ) : ''; $icon_color = empty( $icon_color ) ? $this->get_default_icon_color() : $icon_color; $fld = $this->field_element( 'color', $field, [ 'slug' => 'icon_color', 'value' => $icon_color, 'data' => [ 'fallback-color' => $icon_color, ], ], false ); $this->field_element( 'row', $field, [ 'slug' => 'icon_color', 'content' => $lbl . $fld, 'class' => 'color-picker-row', ] ); // Custom CSS classes. $this->field_option( 'css', $field ); // Hide label. $this->field_option( 'label_hide', $field ); // Options close markup. $this->field_option( 'advanced-options', $field, [ 'markup' => 'close', ] ); } /** * Score labels. * * @since 1.9.8 * * @param array $field Field settings. */ private function score_labels( array $field ): void { // Lowest score label. $lowest_label = $this->field_element( 'label', $field, [ 'slug' => 'lowest_label', 'value' => esc_html__( 'Lowest Score Label', 'wpforms-lite' ), 'tooltip' => esc_html__( 'This label indicates the lowest score on the scale.', 'wpforms-lite' ), ], false ); $lowest_field = $this->field_element( 'text', $field, [ 'slug' => 'lowest_label', 'value' => $field['lowest_label'] ?? '', ], false ); $this->field_element( 'row', $field, [ 'slug' => 'lowest_label', 'content' => $lowest_label . $lowest_field, ] ); // Highest score label. $highest_label = $this->field_element( 'label', $field, [ 'slug' => 'highest_label', 'value' => esc_html__( 'Highest Score Label', 'wpforms-lite' ), 'tooltip' => esc_html__( 'This label indicates the highest score on the scale.', 'wpforms-lite' ), ], false ); $highest_field = $this->field_element( 'text', $field, [ 'slug' => 'highest_label', 'value' => $field['highest_label'] ?? '', ], false ); $this->field_element( 'row', $field, [ 'slug' => 'highest_label', 'content' => $highest_label . $highest_field, ] ); // Label position. $label_position = $this->field_element( 'label', $field, [ 'slug' => 'label_position', 'value' => esc_html__( 'Label Position', 'wpforms-lite' ), 'tooltip' => esc_html__( 'Select the position of the label', 'wpforms-lite' ), ], false ); $select_position = $this->field_element( 'select', $field, [ 'slug' => 'label_position', 'value' => ! empty( $field['label_position'] ) ? esc_attr( $field['label_position'] ) : 'below', 'options' => [ 'above' => esc_html__( 'Above', 'wpforms-lite' ), 'below' => esc_html__( 'Below', 'wpforms-lite' ), ], ], false ); $this->field_element( 'row', $field, [ 'slug' => 'label_position', 'content' => $label_position . $select_position, ] ); } /** * Field preview inside the builder. * * @since 1.9.4 * * @param array $field Field settings. */ public function field_preview( $field ): void { // Label. $this->field_preview_option( 'label', $field, [ 'label_badge' => $this->get_field_preview_badge(), ] ); echo '<div class="wpforms-rating-field">'; $this->get_field_preview_icons( $field ); $this->get_field_preview_labels( $field ); echo '</div>'; // Description. $this->field_preview_option( 'description', $field ); } /** * Get field preview icons. * * @since 1.9.8 * * @param array $field Field settings. */ private function get_field_preview_icons( array $field ): void { // Define data. $scale = ! empty( $field['scale'] ) ? esc_attr( $field['scale'] ) : 5; $icon = ! empty( $field['icon'] ) ? esc_attr( $field['icon'] ) : 'star'; $icon_size = ! empty( $field['icon_size'] ) ? esc_attr( $field['icon_size'] ) : 'medium'; $icon_color = ! empty( $field['icon_color'] ) ? esc_attr( $field['icon_color'] ) : $this->get_default_icon_color(); $icon_class = $this->get_preview_icon_class( $icon ); // Set icon size. $icon_size_css = $this->get_icon_size_css( $icon_size ); echo '<div class="wpforms-rating-field-icons">'; // Primary input. for ( $i = 1; $i <= 10; $i++ ) { printf( '<i class="fa %s %s rating-icon" aria-hidden="true" style="color:%s; display:%s; font-size:%dpx;"></i>', esc_attr( $icon_class ), esc_attr( $icon_size ), esc_attr( $icon_color ), $i <= $scale ? 'inline-block' : 'none', esc_attr( $icon_size_css ) ); } echo '</div>'; } /** * Get preview icon class based on the selected icon. * * @since 1.9.8 * * @param string $icon Selected icon. * * @return string Icon class. */ private function get_preview_icon_class( string $icon ): string { $icon_class = ''; // Set icon class. switch ( $icon ) { case 'star': $icon_class = 'fa-star'; break; case 'heart': $icon_class = 'fa-heart'; break; case 'thumb': $icon_class = 'fa-thumbs-up'; break; case 'smiley': $icon_class = 'fa-smile-o'; break; } return $icon_class; } /** * Get field preview labels. * * @since 1.9.8 * * @param array $field Field settings. */ private function get_field_preview_labels( array $field ): void { // Lowest score label. $lowest_label = ! empty( $field['lowest_label'] ) ? esc_html( $field['lowest_label'] ) : ''; // Highest score label. $highest_label = ! empty( $field['highest_label'] ) ? esc_html( $field['highest_label'] ) : ''; $class = [ 'wpforms-rating-field-labels' ]; if ( ! empty( $field['label_position'] ) && $field['label_position'] === 'above' ) { $class[] = 'wpforms-rating-field-labels-position-above'; } if ( empty( $lowest_label ) && empty( $highest_label ) ) { $class[] = 'wpforms-hidden'; } echo '<div class=" ' . wpforms_sanitize_classes( $class, true ) . ' ">'; printf( '<span class="wpforms-rating-field-lowest-label wpforms-sub-label">%s</span>', esc_html( $lowest_label ) ); printf( '<span class="wpforms-rating-field-highest-label wpforms-sub-label">%s</span>', esc_html( $highest_label ) ); echo '</div>'; } /** * Field display on the form front-end. * * @since 1.9.4 * * @param array $field Field settings. * @param array $deprecated Deprecated, don't use. * @param array $form_data Form data and settings. */ public function field_display( $field, $deprecated, $form_data ) { } /** * Get icon size CSS value in pixels. * * @since 1.9.4 * * @param string $icon_size Icon size value. */ protected function get_icon_size_css( $icon_size ): string { $render_engine = wpforms_get_render_engine(); $icon_sizes = [ 'classic' => [ 'small' => '18', 'medium' => '28', 'large' => '38', ], 'modern' => [ 'small' => '16', 'medium' => '24', 'large' => '38', ], ]; $default = $render_engine === 'modern' ? '24' : '28'; return ! empty( $icon_sizes[ $render_engine ][ $icon_size ] ) ? $icon_sizes[ $render_engine ][ $icon_size ] : $default; } /** * Get default icon color. * * @since 1.9.4 * * @return string */ public function get_default_icon_color(): string { $render_engine = wpforms_get_render_engine(); return array_key_exists( $render_engine, self::DEFAULT_ICON_COLOR ) ? self::DEFAULT_ICON_COLOR[ $render_engine ] : self::DEFAULT_ICON_COLOR['modern']; } } Fields/Divider/Field.php 0000644 00000006732 15252506741 0011113 0 ustar 00 <?php namespace WPForms\Forms\Fields\Divider; use WPForms\Forms\Fields\Traits\ProField as ProFieldTrait; use WPForms_Field; /** * Section Divider field. * * @since 1.9.4 */ class Field extends WPForms_Field { use ProFieldTrait; /** * Primary class constructor. * * @since 1.9.4 */ public function init() { // Define field type information. $this->name = esc_html__( 'Section Divider', 'wpforms-lite' ); $this->keywords = esc_html__( 'line, hr', 'wpforms-lite' ); $this->type = 'divider'; $this->icon = 'fa-arrows-h'; $this->order = 170; $this->group = 'fancy'; $this->allow_read_only = false; $this->default_settings = [ 'label_disable' => '1', ]; $this->init_pro_field(); $this->hooks(); } /** * Hooks. * * @since 1.9.4 */ protected function hooks() { } /** * Field options panel inside the builder. * * @since 1.9.4 * * @param array $field Field data. */ public function field_options( $field ) { /* * Basic field options. */ // Options open markup. $this->field_option( 'basic-options', $field, [ 'markup' => 'open', 'after_title' => $this->get_field_options_notice(), ] ); // Label. $this->field_option( 'label', $field ); // Description. $this->field_option( 'description', $field ); // Set label to the disabled. $args = [ 'type' => 'hidden', 'slug' => 'label_disable', 'value' => '1', ]; $this->field_element( 'text', $field, $args ); // Options close markup. $args = [ 'markup' => 'close', ]; $this->field_option( 'basic-options', $field, $args ); /* * Advanced field options. */ // Options open markup. $args = [ 'markup' => 'open', ]; $this->field_option( 'advanced-options', $field, $args ); // Custom CSS classes. $this->field_option( 'css', $field ); // Hide Divider Line toggle. $this->hide_divider_line_option( $field ); // Options close markup. $args = [ 'markup' => 'close', ]; $this->field_option( 'advanced-options', $field, $args ); } /** * Hide the Divider Line option. * * @since 1.9.7 * * @param array $field Field data. */ private function hide_divider_line_option( array $field ): void { $hide_divider_line_value = $field['hide_divider_line'] ?? '0'; $hide_divider_line = $this->field_element( 'toggle', $field, [ 'slug' => 'hide_divider_line', 'value' => $hide_divider_line_value, 'desc' => esc_html__( 'Hide Divider Line', 'wpforms-lite' ), 'tooltip' => esc_html__( 'Do not show the horizontal divider line.', 'wpforms-lite' ), ], false ); $this->field_element( 'row', $field, [ 'slug' => 'hide_divider_line', 'content' => $hide_divider_line, ] ); } /** * Field preview inside the builder. * * @since 1.9.4 * * @param array $field Field data. */ public function field_preview( $field ) { // Label. $this->field_preview_option( 'label', $field, [ 'label_badge' => $this->get_field_preview_badge(), ] ); // Description. $this->field_preview_option( 'description', $field ); } /** * Field display on the form front-end. * * @since 1.9.4 * * @param array $field Field data and settings. * @param array $deprecated Deprecated field attributes. Use field properties. * @param array $form_data Form data and settings. */ public function field_display( $field, $deprecated, $form_data ) { } } Fields/Address/Frontend.php 0000644 00000003030 15252506741 0011632 0 ustar 00 <?php namespace WPForms\Forms\Fields\Address; use WPForms\Forms\Fields\Base\Frontend as FrontendBase; /** * Address field frontend class. * * @since 1.9.5 */ class Frontend extends FrontendBase { /** * Register hooks. * * @since 1.9.5 * * @noinspection ReturnTypeCanBeDeclaredInspection */ public function hooks() { add_filter( 'wpforms_frontend_strings', [ $this, 'strings' ] ); add_action( 'wpforms_wp_footer', [ $this, 'assets_footer' ], 15 ); } /** * Add address field related settings to a wpforms_settings array. * * @since 1.9.5 * * @param array|mixed $strings The wpforms_settings array. * * @return array */ public function strings( $strings ): array { $strings = (array) $strings; /** * Modify the list of countries without states. * * @since 1.9.5 * * @param array $countries The list of country codes, defaults to [ 'GB', 'DE', 'CH', 'NL' ]. * * @return array */ $countries = (array) apply_filters( 'wpforms_forms_fields_address_frontend_strings_list_countries_without_states', [ 'GB', 'DE', 'CH', 'NL' ] ); $strings['address_field']['list_countries_without_states'] = array_map( 'strtoupper', $countries ); return $strings; } /** * Load the assets needed for the Address field. * * @since 1.9.5 */ public function assets_footer(): void { $min = wpforms_get_min_suffix(); wp_enqueue_script( 'wpforms-address-field', WPFORMS_PLUGIN_URL . "assets/js/frontend/fields/address{$min}.js", [ 'wpforms' ], WPFORMS_VERSION, true ); } } Fields/Address/Field.php 0000644 00000070003 15252506741 0011102 0 ustar 00 <?php namespace WPForms\Forms\Fields\Address; use WPForms\Forms\Fields\Traits\ProField as ProFieldTrait; use WPForms_Field; /** * Address field. * * @since 1.9.4 */ class Field extends WPForms_Field { use ProFieldTrait; /** * Address schemes: 'us' or 'international' by default. * * @since 1.9.4 * * @var array */ public $schemes; /** * Primary class constructor. * * @since 1.9.4 */ public function init() { // Define field type information. $this->name = esc_html__( 'Address', 'wpforms-lite' ); $this->type = 'address'; $this->icon = 'fa-map-marker'; $this->order = 70; $this->group = 'fancy'; // Allow for additional or customizing address schemes. $default_schemes = [ 'us' => [ 'label' => esc_html__( 'US', 'wpforms-lite' ), 'address1_label' => esc_html__( 'Address Line 1', 'wpforms-lite' ), 'address2_label' => esc_html__( 'Address Line 2', 'wpforms-lite' ), 'city_label' => esc_html__( 'City', 'wpforms-lite' ), 'postal_label' => esc_html__( 'Zip Code', 'wpforms-lite' ), 'state_label' => esc_html__( 'State', 'wpforms-lite' ), 'states' => wpforms_us_states(), ], 'international' => [ 'label' => esc_html__( 'International', 'wpforms-lite' ), 'address1_label' => esc_html__( 'Address Line 1', 'wpforms-lite' ), 'address2_label' => esc_html__( 'Address Line 2', 'wpforms-lite' ), 'city_label' => esc_html__( 'City', 'wpforms-lite' ), 'postal_label' => esc_html__( 'Postal Code', 'wpforms-lite' ), 'state_label' => esc_html__( 'State / Province / Region', 'wpforms-lite' ), 'states' => '', 'country_label' => esc_html__( 'Country', 'wpforms-lite' ), 'countries' => wpforms_countries(), ], ]; /** * Allow modifying address schemes. * * @since 1.2.7 * * @param array $schemes Address schemes. */ $this->schemes = apply_filters( 'wpforms_address_schemes', $default_schemes ); // phpcs:ignore WPForms.PHP.ValidateHooks.InvalidHookName $this->init_pro_field(); $this->hooks(); } /** * Hooks. * * @since 1.9.4 */ protected function hooks() { } /** * Field options panel inside the builder. * * @since 1.9.4 * * @param array $field Field data. */ public function field_options( $field ) { // phpcs:ignore Generic.Metrics.CyclomaticComplexity.TooHigh /* * Basic field options. */ // Options open markup. $this->field_option( 'basic-options', $field, [ 'markup' => 'open', 'after_title' => $this->get_field_options_notice(), ] ); // Label. $this->field_option( 'label', $field ); // Address Scheme - was "format" key prior to 1.2.7. $scheme = ! empty( $field['scheme'] ) ? esc_attr( $field['scheme'] ) : 'us'; if ( empty( $scheme ) && ! empty( $field['format'] ) ) { $scheme = esc_attr( $field['format'] ); } $tooltip = esc_html__( 'Select scheme format for the address field.', 'wpforms-lite' ); $options = array_map( static function ( $s ) { return $s['label']; }, $this->schemes ); $output = $this->field_element( 'label', $field, [ 'slug' => 'scheme', 'value' => esc_html__( 'Scheme', 'wpforms-lite' ), 'tooltip' => $tooltip, ], false ); $output .= $this->field_element( 'select', $field, [ 'slug' => 'scheme', 'value' => $scheme, 'options' => $options, ], false ); $this->field_element( 'row', $field, [ 'slug' => 'scheme', 'content' => $output, ] ); // Description. $this->field_option( 'description', $field ); // Required toggle. $this->field_option( 'required', $field ); // Options close markup. $this->field_option( 'basic-options', $field, [ 'markup' => 'close', ] ); /* * Advanced field options. */ // Options open markup. $this->field_option( 'advanced-options', $field, [ 'markup' => 'open', ] ); // Size. $this->field_option( 'size', $field ); // Address Line 1. $address1_placeholder = ! empty( $field['address1_placeholder'] ) ? esc_attr( $field['address1_placeholder'] ) : ''; $address1_default = ! empty( $field['address1_default'] ) ? esc_attr( $field['address1_default'] ) : ''; printf( '<div class="wpforms-clear wpforms-field-option-row wpforms-field-option-row-address1" id="wpforms-field-option-row-%1$d-address1" data-subfield="address-1" data-field-id="%1$s">', wpforms_validate_field_id( $field['id'] ) // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ); $this->field_element( 'label', $field, [ 'slug' => 'address1_placeholder', 'value' => esc_html__( 'Address Line 1', 'wpforms-lite' ), ] ); // phpcs:disable WordPress.Security.EscapeOutput.OutputNotEscaped echo '<div class="wpforms-field-options-columns-2 wpforms-field-options-columns">'; echo '<div class="placeholder wpforms-field-options-column">'; printf( '<input type="text" class="placeholder" id="wpforms-field-option-%1$s-address1_placeholder" name="fields[%1$s][address1_placeholder]" value="%2$s">', wpforms_validate_field_id( $field['id'] ), esc_attr( $address1_placeholder ) ); printf( '<label for="wpforms-field-option-%s-address1_placeholder" class="sub-label">%s</label>', wpforms_validate_field_id( $field['id'] ), esc_html__( 'Placeholder', 'wpforms-lite' ) ); echo '</div>'; echo '<div class="default wpforms-field-options-column">'; printf( '<input type="text" class="default" id="wpforms-field-option-%1$d-address1_default" name="fields[%1$s][address1_default]" value="%2$s">', wpforms_validate_field_id( $field['id'] ), esc_attr( $address1_default ) ); printf( '<label for="wpforms-field-option-%s-address1_default" class="sub-label">%s</label>', wpforms_validate_field_id( $field['id'] ), esc_html__( 'Default Value', 'wpforms-lite' ) ); echo '</div>'; echo '</div>'; // phpcs:enable WordPress.Security.EscapeOutput.OutputNotEscaped echo '</div>'; // Address Line 2. $address2_placeholder = ! empty( $field['address2_placeholder'] ) ? esc_attr( $field['address2_placeholder'] ) : ''; $address2_default = ! empty( $field['address2_default'] ) ? esc_attr( $field['address2_default'] ) : ''; $address2_hide = ! empty( $field['address2_hide'] ); printf( '<div class="wpforms-clear wpforms-field-option-row wpforms-field-option-row-address2" id="wpforms-field-option-row-%1$d-address2" data-subfield="address-2" data-field-id="%1$s">', wpforms_validate_field_id( $field['id'] ) // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ); echo '<div class="wpforms-field-header">'; $this->field_element( 'label', $field, [ 'slug' => 'address2_placeholder', 'value' => esc_html__( 'Address Line 2', 'wpforms-lite' ), ] ); $this->field_element( 'toggle', $field, [ 'slug' => 'address2_hide', 'value' => $address2_hide, 'desc' => esc_html__( 'Hide', 'wpforms-lite' ), 'title' => esc_html__( 'Turn On if you want to hide this sub field.', 'wpforms-lite' ), 'label-left' => true, 'control-class' => 'wpforms-field-option-in-label-right', 'class' => 'wpforms-subfield-hide', ] ); echo '</div>'; // phpcs:disable WordPress.Security.EscapeOutput.OutputNotEscaped echo '<div class="wpforms-field-options-columns-2 wpforms-field-options-columns">'; echo '<div class="placeholder wpforms-field-options-column">'; printf( '<input type="text" class="placeholder" id="wpforms-field-option-%1$d-address2_placeholder" name="fields[%1$s][address2_placeholder]" value="%2$s">', wpforms_validate_field_id( $field['id'] ), esc_attr( $address2_placeholder ) ); printf( '<label for="wpforms-field-option-%s-address2_placeholder" class="sub-label">%s</label>', wpforms_validate_field_id( $field['id'] ), esc_html__( 'Placeholder', 'wpforms-lite' ) ); echo '</div>'; echo '<div class="default wpforms-field-options-column">'; printf( '<input type="text" class="default" id="wpforms-field-option-%1$d-address2_default" name="fields[%1$s][address2_default]" value="%2$s">', wpforms_validate_field_id( $field['id'] ), esc_attr( $address2_default ) ); printf( '<label for="wpforms-field-option-%s-address2_default" class="sub-label">%s</label>', wpforms_validate_field_id( $field['id'] ), esc_html__( 'Default Value', 'wpforms-lite' ) ); echo '</div>'; echo '</div>'; // phpcs:enable WordPress.Security.EscapeOutput.OutputNotEscaped echo '</div>'; // City. $city_placeholder = ! empty( $field['city_placeholder'] ) ? esc_attr( $field['city_placeholder'] ) : ''; $city_default = ! empty( $field['city_default'] ) ? esc_attr( $field['city_default'] ) : ''; printf( '<div class="wpforms-clear wpforms-field-option-row wpforms-field-option-row-city" id="wpforms-field-option-row-%1$s-city" data-subfield="city" data-field-id="%1$s">', wpforms_validate_field_id( $field['id'] ) // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ); $this->field_element( 'label', $field, [ 'slug' => 'city_placeholder', 'value' => esc_html__( 'City', 'wpforms-lite' ), ] ); // phpcs:disable WordPress.Security.EscapeOutput.OutputNotEscaped echo '<div class="wpforms-field-options-columns-2 wpforms-field-options-columns">'; echo '<div class="placeholder wpforms-field-options-column">'; printf( '<input type="text" class="placeholder" id="wpforms-field-option-%1$s-city_placeholder" name="fields[%1$s][city_placeholder]" value="%2$s">', wpforms_validate_field_id( $field['id'] ), esc_attr( $city_placeholder ) ); printf( '<label for="wpforms-field-option-%s-city_placeholder" class="sub-label">%s</label>', wpforms_validate_field_id( $field['id'] ), esc_html__( 'Placeholder', 'wpforms-lite' ) ); echo '</div>'; echo '<div class="default wpforms-field-options-column">'; printf( '<input type="text" class="default" id="wpforms-field-option-%1$s-city_default" name="fields[%1$s][city_default]" value="%2$s">', wpforms_validate_field_id( $field['id'] ), esc_attr( $city_default ) ); printf( '<label for="wpforms-field-option-%s-city_default" class="sub-label">%s</label>', wpforms_validate_field_id( $field['id'] ), esc_html__( 'Default Value', 'wpforms-lite' ) ); echo '</div>'; echo '</div>'; // phpcs:enable WordPress.Security.EscapeOutput.OutputNotEscaped echo '</div>'; // State. $state_placeholder = ! empty( $field['state_placeholder'] ) ? $field['state_placeholder'] : ''; printf( '<div class="wpforms-clear wpforms-field-option-row wpforms-field-option-row-state" id="wpforms-field-option-row-%1$s-state" data-subfield="state" data-field-id="%1$s">', wpforms_validate_field_id( $field['id'] ) // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ); $this->field_element( 'label', $field, [ 'slug' => 'state_placeholder', 'value' => esc_html__( 'State / Province / Region', 'wpforms-lite' ), ] ); // phpcs:disable WordPress.Security.EscapeOutput.OutputNotEscaped echo '<div class="wpforms-field-options-columns-2 wpforms-field-options-columns">'; echo '<div class="placeholder wpforms-field-options-column">'; printf( '<input type="text" class="placeholder" id="wpforms-field-option-%1$s-state_placeholder" name="fields[%1$s][state_placeholder]" value="%2$s">', wpforms_validate_field_id( $field['id'] ), esc_attr( $state_placeholder ) ); printf( '<label for="wpforms-field-option-%s-state_placeholder" class="sub-label">%s</label>', wpforms_validate_field_id( $field['id'] ), esc_html__( 'Placeholder', 'wpforms-lite' ) ); echo '</div>'; echo '<div class="default wpforms-field-options-column">'; $this->subfield_default( $field, 'state', 'states' ); printf( '<label for="wpforms-field-option-%s-state_default" class="sub-label">%s</label>', wpforms_validate_field_id( $field['id'] ), esc_html__( 'Default Value', 'wpforms-lite' ) ); echo '</div>'; echo '</div>'; // phpcs:enable WordPress.Security.EscapeOutput.OutputNotEscaped echo '</div>'; // ZIP/Postal. $postal_placeholder = ! empty( $field['postal_placeholder'] ) ? esc_attr( $field['postal_placeholder'] ) : ''; $postal_default = ! empty( $field['postal_default'] ) ? esc_attr( $field['postal_default'] ) : ''; $postal_hide = ! empty( $field['postal_hide'] ); $postal_visibility = ! isset( $this->schemes[ $scheme ]['postal_label'] ) ? 'wpforms-hidden' : ''; printf( '<div class="wpforms-clear wpforms-field-option-row wpforms-field-option-row-postal %1$s" id="wpforms-field-option-row-%2$s-postal" data-subfield="postal" data-field-id="%2$s">', sanitize_html_class( $postal_visibility ), wpforms_validate_field_id( $field['id'] ) // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ); echo '<div class="wpforms-field-header">'; $this->field_element( 'label', $field, [ 'slug' => 'postal_placeholder', 'value' => esc_html__( 'ZIP / Postal', 'wpforms-lite' ), ] ); $this->field_element( 'toggle', $field, [ 'slug' => 'postal_hide', 'value' => $postal_hide, 'desc' => esc_html__( 'Hide', 'wpforms-lite' ), 'title' => esc_html__( 'Turn On if you want to hide this sub field.', 'wpforms-lite' ), 'label-left' => true, 'control-class' => 'wpforms-field-option-in-label-right', 'class' => 'wpforms-subfield-hide', ] ); echo '</div>'; // phpcs:disable WordPress.Security.EscapeOutput.OutputNotEscaped echo '<div class="wpforms-field-options-columns-2 wpforms-field-options-columns">'; echo '<div class="placeholder wpforms-field-options-column">'; printf( '<input type="text" class="placeholder" id="wpforms-field-option-%1$s-postal_placeholder" name="fields[%1$s][postal_placeholder]" value="%2$s">', wpforms_validate_field_id( $field['id'] ), esc_attr( $postal_placeholder ) ); printf( '<label for="wpforms-field-option-%s-postal_placeholder" class="sub-label">%s</label>', wpforms_validate_field_id( $field['id'] ), esc_html__( 'Placeholder', 'wpforms-lite' ) ); echo '</div>'; echo '<div class="default wpforms-field-options-column">'; printf( '<input type="text" class="default" id="wpforms-field-option-%1$s-postal_default" name="fields[%1$s][postal_default]" value="%2$s">', wpforms_validate_field_id( $field['id'] ), esc_attr( $postal_default ) ); printf( '<label for="wpforms-field-option-%s-postal_default" class="sub-label">%s</label>', wpforms_validate_field_id( $field['id'] ), esc_html__( 'Default Value', 'wpforms-lite' ) ); echo '</div>'; echo '</div>'; // phpcs:enable WordPress.Security.EscapeOutput.OutputNotEscaped echo '</div>'; // Country. $country_placeholder = ! empty( $field['country_placeholder'] ) ? $field['country_placeholder'] : ''; $country_hide = ! empty( $field['country_hide'] ); $country_visibility = ! isset( $this->schemes[ $scheme ]['countries'] ) ? 'wpforms-hidden' : ''; printf( '<div class="wpforms-clear wpforms-field-option-row wpforms-field-option-row-country %1$s" id="wpforms-field-option-row-%2$s-country" data-subfield="country" data-field-id="%2$s">', sanitize_html_class( $country_visibility ), wpforms_validate_field_id( $field['id'] ) // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ); echo '<div class="wpforms-field-header">'; $this->field_element( 'label', $field, [ 'slug' => 'country_placeholder', 'value' => esc_html__( 'Country', 'wpforms-lite' ), ] ); $this->field_element( 'toggle', $field, [ 'slug' => 'country_hide', 'value' => $country_hide, 'desc' => esc_html__( 'Hide', 'wpforms-lite' ), 'title' => esc_html__( 'Turn On if you want to hide this sub field.', 'wpforms-lite' ), 'label-left' => true, 'control-class' => 'wpforms-field-option-in-label-right', 'class' => 'wpforms-subfield-hide', ] ); echo '</div>'; // phpcs:disable WordPress.Security.EscapeOutput.OutputNotEscaped echo '<div class="wpforms-field-options-columns-2 wpforms-field-options-columns">'; echo '<div class="placeholder wpforms-field-options-column">'; printf( '<input type="text" class="placeholder" id="wpforms-field-option-%1$s-country_placeholder" name="fields[%1$s][country_placeholder]" value="%2$s">', wpforms_validate_field_id( $field['id'] ), esc_attr( $country_placeholder ) ); printf( '<label for="wpforms-field-option-%s-country_placeholder" class="sub-label">%s</label>', wpforms_validate_field_id( $field['id'] ), esc_html__( 'Placeholder', 'wpforms-lite' ) ); echo '</div>'; echo '<div class="default wpforms-field-options-column">'; $this->subfield_default( $field, 'country', 'countries' ); printf( '<label for="wpforms-field-option-%s-country_default" class="sub-label">%s</label>', wpforms_validate_field_id( $field['id'] ), esc_html__( 'Default Value', 'wpforms-lite' ) ); echo '</div>'; echo '</div>'; // phpcs:enable WordPress.Security.EscapeOutput.OutputNotEscaped echo '</div>'; // Custom CSS classes. $this->field_option( 'css', $field ); // Hide label. $this->field_option( 'label_hide', $field ); // Hide sublabel. $this->field_option( 'sublabel_hide', $field ); // Options close markup. $this->field_option( 'advanced-options', $field, [ 'markup' => 'close', ] ); } /** * Field preview inside the builder. * * @since 1.9.4 * * @param array $field Field data. */ public function field_preview( $field ) { // phpcs:ignore Generic.Metrics.CyclomaticComplexity.MaxExceeded // Define data. $address1_placeholder = ! empty( $field['address1_placeholder'] ) ? $field['address1_placeholder'] : ''; $address1_default = ! empty( $field['address1_default'] ) ? $field['address1_default'] : ''; $address2_placeholder = ! empty( $field['address2_placeholder'] ) ? $field['address2_placeholder'] : ''; $address2_default = ! empty( $field['address2_default'] ) ? $field['address2_default'] : ''; $address2_hide = ! empty( $field['address2_hide'] ) ? 'wpforms-hide' : ''; $city_placeholder = ! empty( $field['city_placeholder'] ) ? $field['city_placeholder'] : ''; $city_default = ! empty( $field['city_default'] ) ? $field['city_default'] : ''; $postal_placeholder = ! empty( $field['postal_placeholder'] ) ? $field['postal_placeholder'] : ''; $postal_default = ! empty( $field['postal_default'] ) ? $field['postal_default'] : ''; $postal_hide = ! empty( $field['postal_hide'] ) ? 'wpforms-hide' : ''; $country_hide = ! empty( $field['country_hide'] ) ? 'wpforms-hide' : ''; $format = ! empty( $field['format'] ) ? $field['format'] : 'us'; $scheme_selected = ! empty( $field['scheme'] ) ? $field['scheme'] : $format; // Label. $this->field_preview_option( 'label', $field, [ 'label_badge' => $this->get_field_preview_badge(), ] ); // Field elements. foreach ( $this->schemes as $slug => $scheme ) { $address1_label = $scheme['address1_label'] ?? esc_html__( 'Address Line 1', 'wpforms-lite' ); $address2_label = $scheme['address2_label'] ?? esc_html__( 'Address Line 2', 'wpforms-lite' ); $city_label = $scheme['city_label'] ?? esc_html__( 'City', 'wpforms-lite' ); $state_label = $scheme['state_label'] ?? esc_html__( 'State / Province / Region', 'wpforms-lite' ); $postal_label = $scheme['postal_label'] ?? esc_html__( 'Postal Code', 'wpforms-lite' ); $country_label = $scheme['country_label'] ?? esc_html__( 'Country', 'wpforms-lite' ); $is_active_scheme = $slug === $scheme_selected; $scheme_hide_class = ! $is_active_scheme ? 'wpforms-hide' : ''; $state_placeholder = ! empty( $field['state_placeholder'] ) ? $field['state_placeholder'] : ''; $state_default = $is_active_scheme && ! empty( $field['state_default'] ) ? $field['state_default'] : ''; $country_placeholder = ! empty( $field['country_placeholder'] ) ? $field['country_placeholder'] : ''; $country_default = $is_active_scheme && ! empty( $field['country_default'] ) ? $field['country_default'] : ''; // Wrapper. printf( '<div class="wpforms-address-scheme wpforms-address-scheme-%s %s">', wpforms_sanitize_classes( $slug ), // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped wpforms_sanitize_classes( $scheme_hide_class ) // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ); // Row 1 - Address Line 1. printf( '<div class="wpforms-field-row wpforms-address-1"> <input type="text" placeholder="%s" value="%s" readonly> <label class="wpforms-sub-label">%s</label> </div>', esc_attr( $address1_placeholder ), esc_attr( $address1_default ), esc_html( $address1_label ) ); // Row 2 - Address Line 2. printf( '<div class="wpforms-field-row wpforms-address-2 %s"> <input type="text" placeholder="%s" value="%s" readonly> <label class="wpforms-sub-label">%s</label> </div>', wpforms_sanitize_classes( $address2_hide ), // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped esc_attr( $address2_placeholder ), esc_attr( $address2_default ), esc_html( $address2_label ) ); // Row 3 - City & State. echo '<div class="wpforms-field-row">'; // City. printf( '<div class="wpforms-city wpforms-one-half "> <input type="text" placeholder="%s" value="%s" readonly> <label class="wpforms-sub-label">%s</label> </div>', esc_attr( $city_placeholder ), esc_attr( $city_default ), esc_html( $city_label ) ); // State / Providence / Region. echo '<div class="wpforms-state wpforms-one-half last">'; if ( isset( $scheme['states'] ) && empty( $scheme['states'] ) ) { // State text input. printf( '<input type="text" placeholder="%s" value="%s" readonly>', esc_attr( $state_placeholder ), esc_attr( $state_default ) ); } elseif ( ! empty( $scheme['states'] ) && is_array( $scheme['states'] ) ) { $state_option = $this->dropdown_empty_value( (string) $state_label ); if ( ! empty( $state_placeholder ) ) { $state_option = $state_placeholder; } if ( $is_active_scheme && ! empty( $state_default ) ) { $state_option = $scheme['states'][ $state_default ]; } // State select. printf( '<select readonly> <option class="placeholder" selected>%s</option> </select>', esc_html( $state_option ) ); } printf( '<label class="wpforms-sub-label">%s</label>', esc_html( $state_label ) ); echo '</div>'; // End row 3 - City & State. echo '</div>'; // Row 4 - Zip & Country. echo '<div class="wpforms-field-row">'; // ZIP / Postal. printf( '<div class="wpforms-postal wpforms-one-half %s"> <input type="text" placeholder="%s" value="%s" readonly> <label class="wpforms-sub-label">%s</label> </div>', wpforms_sanitize_classes( $postal_hide ), // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped esc_attr( $postal_placeholder ), esc_attr( $postal_default ), esc_html( $postal_label ) ); // Country. printf( '<div class="wpforms-country wpforms-one-half last %s">', sanitize_html_class( $country_hide ) ); if ( isset( $scheme['countries'] ) && empty( $scheme['countries'] ) ) { // Country text input. printf( '<input type="text" placeholder="%s" value="%s" readonly>', esc_attr( $country_placeholder ), esc_attr( $country_default ) ); } elseif ( ! empty( $scheme['countries'] ) && is_array( $scheme['countries'] ) ) { $country_option = $this->dropdown_empty_value( (string) $country_label ); if ( ! empty( $country_placeholder ) ) { $country_option = $country_placeholder; } if ( $is_active_scheme && ! empty( $country_default ) ) { $country_option = $scheme['countries'][ $country_default ]; } // Country select. printf( '<select readonly><option class="placeholder" selected>%s</option></select>', esc_html( $country_option ) ); printf( '<label class="wpforms-sub-label">%s</label>', esc_html( $country_label ) ); } echo '</div>'; // End row 4 - Zip & Country. echo '</div>'; // End wrapper. echo '</div>'; } // Description. $this->field_preview_option( 'description', $field ); } /** * Field display on the form front-end. * * @since 1.9.4 * * @param array $field Field data and settings. * @param array $deprecated Deprecated field attributes. Use field properties instead. * @param array $form_data Form data and settings. */ public function field_display( $field, $deprecated, $form_data ) { } /** * Output "Default" option fields for State/Country subfields. * * The default value should be set only for the scheme it belongs to. * * @since 1.9.4 * * @param array $field Address field data. * @param string $subfield_slug Subfield slug, either `state` or `country`. * @param string $subfield_key Subfield key in `$scheme` data, either `states` or `countries`. * * @noinspection HtmlUnknownAttribute */ private function subfield_default( array $field, string $subfield_slug, string $subfield_key ): void { // Scheme or default value may not be set yet. $active_scheme = ! empty( $field['scheme'] ) ? $field['scheme'] : 'us'; $default_value = ! empty( $field[ "{$subfield_slug}_default" ] ) ? $field[ "{$subfield_slug}_default" ] : ''; foreach ( $this->schemes as $scheme_slug => $scheme_data ) { $subfield_label = empty( $scheme_data[ $subfield_slug . '_label' ] ) ? ucfirst( $subfield_slug ) : $scheme_data[ $subfield_slug . '_label' ]; $empty_value = $this->dropdown_empty_value( $subfield_label ); $is_active_scheme = $scheme_slug === $active_scheme; // If a scheme contains an array of values, we display a select dropdown. Otherwise, text input. if ( ! empty( $scheme_data[ $subfield_key ] ) && is_array( $scheme_data[ $subfield_key ] ) ) { $options_escaped = sprintf( '<option value="">%s</option>', esc_html( $empty_value ) ); foreach ( $scheme_data[ $subfield_key ] as $value => $label ) { $options_escaped .= sprintf( '<option value="%s"%s>%s</option>', esc_attr( $value ), $is_active_scheme ? selected( $default_value, $value, false ) : '', esc_html( $label ) ); } if ( $is_active_scheme ) { printf( '<select class="default" id="wpforms-field-option-%1$s-%2$s_default" name="fields[%1$s][%2$s_default]" data-scheme="%3$s">%4$s</select>', wpforms_validate_field_id( $field['id'] ), // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped esc_attr( $subfield_slug ), esc_attr( $scheme_slug ), $options_escaped // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ); continue; } printf( '<select class="default wpforms-hidden-strict" id="" name="" data-scheme="%s">%s</select>', esc_attr( $scheme_slug ), $options_escaped // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ); continue; } if ( $is_active_scheme ) { printf( '<input type="text" class="default" id="wpforms-field-option-%1$s-%2$s_default" name="fields[%1$s][%2$s_default]" value="%3$s" data-scheme="%4$s">', wpforms_validate_field_id( $field['id'] ), // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped esc_attr( $subfield_slug ), esc_attr( $default_value ), esc_attr( $scheme_slug ) ); continue; } printf( '<input type="text" class="default wpforms-hidden-strict" id="" name="" value="" data-scheme="%s">', esc_attr( $scheme_slug ) ); } } /** * Get a select dropdown "placeholder" option which is displayed if nothing is selected. * * @since 1.9.4 * * @param string $name Select field name, can be lowercase or uppercase. * * @return string */ protected function dropdown_empty_value( string $name ): string { return sprintf( /* translators: %s - subfield name, e.g., state, country. */ __( '--- Select %s ---', 'wpforms-lite' ), $name ); } } Fields/Pagebreak/Field.php 0000644 00000062447 15252506741 0011413 0 ustar 00 <?php namespace WPForms\Forms\Fields\Pagebreak; use WPForms\Forms\Fields\Traits\IndicatorRendererTrait; use WPForms\Forms\Fields\Traits\ProField as ProFieldTrait; use WPForms_Field; /** * Pagebreak field. * * @since 1.9.4 */ class Field extends WPForms_Field { use ProFieldTrait; use IndicatorRendererTrait; /** * Default indicator color. * * @since 1.9.4 */ private const DEFAULT_INDICATOR_COLOR = [ 'classic' => '#72b239', 'modern' => '#066aab', ]; /** * Pages information. * * @since 1.9.4 * * @var array|bool */ protected $pagebreak; /** * Primary class constructor. * * @since 1.9.4 */ public function init() { // Define field type information. $this->name = esc_html__( 'Page Break', 'wpforms-lite' ); $this->keywords = esc_html__( 'progress bar, multi step, multi part', 'wpforms-lite' ); $this->type = 'pagebreak'; $this->icon = 'fa-files-o'; $this->order = 160; $this->group = 'fancy'; $this->allow_read_only = false; $this->init_pro_field(); $this->hooks(); } /** * Hooks. * * @since 1.9.4 */ protected function hooks() { add_filter( 'wpforms_field_preview_class', [ $this, 'preview_field_class' ], 10, 2 ); add_filter( 'wpforms_field_preview_display_duplicate_button', [ $this, 'field_display_duplicate_button' ], 10, 2 ); add_filter( 'wpforms_field_new_display_duplicate_button', [ $this, 'field_display_duplicate_button' ], 10, 2 ); } /** * Get allow page navigation tooltip strings. * * @since 1.10.0 * * @return array Associative array with 'enabled' and 'disabled' keys. */ protected function get_allow_page_navigation_strings(): array { return [ 'enabled' => esc_html__( 'Lets visitors move between pages even if required fields are empty. Required fields are checked when the form is submitted.', 'wpforms-lite' ), 'disabled' => esc_html__( 'Only available when using the Circles or Connector Progress Indicator.', 'wpforms-lite' ), ]; } /** * Field options panel inside the builder. * * @since 1.9.4 * * @param array $field Field data. */ public function field_options( $field ) { $position = ! empty( $field['position'] ) ? esc_attr( $field['position'] ) : ''; $position_class = ! empty( $field['position'] ) ? 'wpforms-pagebreak-' . $position : ''; $this->field_options_basic( $field, $position, $position_class ); $this->field_options_advanced( $field, $position, $position_class ); } /** * Advanced field options panel inside the builder. * * @since 1.9.4 * * @param array $field Field data. * @param string $position Position. * @param string $position_class Position CSS class. */ private function field_options_basic( array $field, string $position, string $position_class ): void { // Hidden field indicating the position. $this->field_element( 'text', $field, [ 'type' => 'hidden', 'slug' => 'position', 'value' => $position, 'class' => 'position', ] ); // Options open markup. $this->field_option( 'basic-options', $field, [ 'markup' => 'open', 'class' => $position_class, 'after_title' => $this->get_field_options_notice(), ] ); $this->field_options_basic_top( $field, $position ); $this->render_page_title_option( $field, $position ); $this->render_next_button_option( $field, $position ); $this->render_previous_button_options( $field, $position ); // Options close markup. $this->field_option( 'basic-options', $field, [ 'markup' => 'close', ] ); } /** * Render page title option. * * @since 1.10.0 * * @param array $field Field data. * @param string $position Position. */ private function render_page_title_option( array $field, string $position ): void { // Don't display for bottom page breaks. if ( $position === 'bottom' ) { return; } $lbl = $this->field_element( 'label', $field, [ 'slug' => 'title', 'value' => esc_html__( 'Page Title', 'wpforms-lite' ), 'tooltip' => esc_html__( 'Enter text for the page title.', 'wpforms-lite' ), ], false ); $fld = $this->field_element( 'text', $field, [ 'slug' => 'title', 'value' => ! empty( $field['title'] ) ? esc_attr( $field['title'] ) : '', ], false ); $indicator = ! empty( $field['indicator'] ) ? esc_attr( $field['indicator'] ) : 'progress'; $this->field_element( 'row', $field, [ 'slug' => 'title', 'content' => $lbl . $fld, 'class' => $indicator === 'none' ? 'wpforms-hidden' : '', ] ); // Allow Page Navigation toggle (only for top position). if ( $position === 'top' ) { $this->render_page_navigation_toggle( $field ); } } /** * Render page navigation toggle option. * * @since 1.10.0 * * @param array $field Field data. */ private function render_page_navigation_toggle( array $field ): void { $indicator = ! empty( $field['indicator'] ) ? esc_attr( $field['indicator'] ) : 'progress'; $is_enabled = in_array( $indicator, [ 'circles', 'connector' ], true ); // Set tooltip text based on the enabled state. $strings = $this->get_allow_page_navigation_strings(); $toggle_data = [ 'slug' => 'allow_page_navigation', 'value' => ! empty( $field['allow_page_navigation'] ), 'desc' => esc_html__( 'Allow Page Navigation', 'wpforms-lite' ), 'tooltip' => $is_enabled ? $strings['enabled'] : $strings['disabled'], 'class' => [ 'wpforms-pagebreak-allow-page-navigation' ], ]; if ( ! $is_enabled ) { $toggle_data['attrs'] = [ 'disabled' => 'disabled' ]; $toggle_data['control-class'] = 'wpforms-toggle-control-disabled'; } $fld = $this->field_element( 'toggle', $field, $toggle_data, false ); $classes = []; if ( $indicator === 'none' ) { $classes[] = 'wpforms-hidden'; } $this->field_element( 'row', $field, [ 'slug' => 'allow_page_navigation', 'content' => $fld, 'class' => $classes, 'data' => [ 'indicator-dependent' => 'circles,connector', ], ] ); } /** * Render next button option. * * @since 1.10.0 * * @param array $field Field data. * @param string $position Position. */ private function render_next_button_option( array $field, string $position ): void { // The next label is only for normal (non-top, non-bottom) pagebreaks. if ( ! empty( $position ) ) { return; } $lbl = $this->field_element( 'label', $field, [ 'slug' => 'next', 'value' => esc_html__( 'Next Label', 'wpforms-lite' ), 'tooltip' => esc_html__( 'Enter text for Next page navigation button.', 'wpforms-lite' ), ], false ); $fld = $this->field_element( 'text', $field, [ 'slug' => 'next', 'value' => ! empty( $field['next'] ) ? esc_attr( $field['next'] ) : esc_html__( 'Next', 'wpforms-lite' ), ], false ); $this->field_element( 'row', $field, [ 'slug' => 'next', 'content' => $lbl . $fld, ] ); } /** * Render previous button options (toggle and label). * * @since 1.10.0 * * @param array $field Field data. * @param string $position Position. */ private function render_previous_button_options( array $field, string $position ): void { // Previous options are not available to top page breaks. if ( $position === 'top' ) { return; } // Previous button toggle. $fld = $this->field_element( 'toggle', $field, [ 'slug' => 'prev_toggle', // Backward compatibility for forms that were created before the toggle was added. 'value' => ! empty( $field['prev_toggle'] ) || ! empty( $field['prev'] ), 'desc' => esc_html__( 'Display Previous', 'wpforms-lite' ), 'tooltip' => esc_html__( 'Toggle displaying the Previous page navigation button.', 'wpforms-lite' ), ], false ); $this->field_element( 'row', $field, [ 'slug' => 'prev_toggle', 'content' => $fld, ] ); // Previous button label. $lbl = $this->field_element( 'label', $field, [ 'slug' => 'prev', 'value' => esc_html__( 'Previous Label', 'wpforms-lite' ), 'tooltip' => esc_html__( 'Enter text for Previous page navigation button.', 'wpforms-lite' ), ], false ); $fld = $this->field_element( 'text', $field, [ 'slug' => 'prev', 'value' => ! empty( $field['prev'] ) ? esc_attr( $field['prev'] ) : '', ], false ); $this->field_element( 'row', $field, [ 'slug' => 'prev', 'content' => $lbl . $fld, 'class' => empty( $field['prev_toggle'] ) ? 'wpforms-hidden' : '', ] ); } /** * Generate the field UI for progress text configuration within a form. * * @since 1.9.7 * * @param array $field The field data used to generate the progress text UI elements. */ private function field_progress_text( array $field ): void { $lbl = $this->field_element( 'label', $field, [ 'slug' => 'progress_text', 'value' => esc_html__( 'Progress Text', 'wpforms-lite' ), 'tooltip' => esc_html__( 'Enter text for the progress indicator.', 'wpforms-lite' ), ], false ); $fld = $this->field_element( 'text', $field, [ 'slug' => 'progress_text', 'value' => ! empty( $field['progress_text'] ) ? esc_html( $field['progress_text'] ) : 'Step {current_page} of {last_page}', 'after' => esc_html__( 'Enter text to show the user\'s progress. You can use {current_page} and {last_page} to indicate the current and last steps.', 'wpforms-lite' ), 'class' => [ 'wpforms-pagebreak-progress-text', 'wpforms-smart-tags-enabled' ], 'smarttags' => [ 'type' => 'other', // Field-local shortlist of the two page tokens for the Smart Tags picker. 'custom' => [ 'current_page' => esc_html__( 'Current Page', 'wpforms-lite' ), 'last_page' => esc_html__( 'Last Page', 'wpforms-lite' ), ], ], ], false ); $indicator = ! empty( $field['indicator'] ) ? esc_attr( $field['indicator'] ) : 'progress'; $this->field_element( 'row', $field, [ 'slug' => 'progress_text', 'content' => $lbl . $fld, 'class' => $indicator !== 'progress' ? 'wpforms-hidden' : '', // Hide if the indicator is not set to progress. ] ); } /** * Field options panel inside the builder. * * @since 1.9.4 * * @param array $field Field data. * @param string $position Position. */ private function field_options_basic_top( array $field, string $position ): void { // Options specific to the top pagebreak. if ( $position !== 'top' ) { return; } // Indicator themes. $themes = [ 'progress' => esc_html__( 'Progress Bar', 'wpforms-lite' ), 'circles' => esc_html__( 'Circles', 'wpforms-lite' ), 'connector' => esc_html__( 'Connector', 'wpforms-lite' ), 'none' => esc_html__( 'None', 'wpforms-lite' ), ]; /** * Filter the available Pagebreak Indicator themes. * * @since 1.6.6 * * @param array $themes Available themes. */ $themes = apply_filters( 'wpforms_pagebreak_indicator_themes', $themes ); // phpcs:ignore WPForms.PHP.ValidateHooks.InvalidHookName $lbl = $this->field_element( 'label', $field, [ 'slug' => 'indicator', 'value' => esc_html__( 'Progress Indicator', 'wpforms-lite' ), 'tooltip' => esc_html__( 'Select theme for Page Indicator which is displayed at the top of the form.', 'wpforms-lite' ), ], false ); $indicator = ! empty( $field['indicator'] ) ? esc_attr( $field['indicator'] ) : 'progress'; $fld = $this->field_element( 'select', $field, [ 'slug' => 'indicator', 'value' => $indicator, 'options' => $themes, 'class' => [ 'wpforms-pagebreak-progress-indicator' ], ], false ); $this->field_element( 'row', $field, [ 'slug' => 'indicator', 'content' => $lbl . $fld, ] ); // Indicator color picker. $lbl = $this->field_element( 'label', $field, [ 'slug' => 'indicator_color', 'value' => esc_html__( 'Page Indicator Color', 'wpforms-lite' ), 'tooltip' => esc_html__( 'Select the primary color for the Page Indicator theme.', 'wpforms-lite' ), ], false ); $indicator_color = isset( $field['indicator_color'] ) ? wpforms_sanitize_hex_color( $field['indicator_color'] ) : self::get_default_indicator_color(); $fld = $this->field_element( 'color', $field, [ 'slug' => 'indicator_color', 'value' => $indicator_color, 'data' => [ 'fallback-color' => $indicator_color, ], 'class' => [ 'wpforms-pagebreak-indicator-color' ], ], false ); $indicator_color_classes = [ 'color-picker-row' ]; if ( $indicator === 'none' ) { $indicator_color_classes[] = 'wpforms-hidden'; } $this->field_element( 'row', $field, [ 'slug' => 'indicator_color', 'content' => $lbl . $fld, 'class' => $indicator_color_classes, ] ); $this->field_progress_text( $field ); } /** * Advanced field options panel inside the builder. * * @since 1.9.4 * * @param array $field Field data. * @param string $position Position. * @param string $position_class Position CSS class. */ private function field_options_advanced( array $field, string $position, string $position_class ): void { if ( $position === 'bottom' ) { return; } /** * Advanced field options. */ // Options open markup. $this->field_option( 'advanced-options', $field, [ 'markup' => 'open', 'class' => $position_class, ] ); // Navigation alignment, only available to the top. if ( $position === 'top' ) { $lbl = $this->field_element( 'label', $field, [ 'slug' => 'nav_align', 'value' => esc_html__( 'Page Navigation Alignment', 'wpforms-lite' ), 'tooltip' => esc_html__( 'Select the alignment for the Next/Previous page navigation buttons', 'wpforms-lite' ), ], false ); $fld = $this->field_element( 'select', $field, [ 'slug' => 'nav_align', 'value' => ! empty( $field['nav_align'] ) ? esc_attr( $field['nav_align'] ) : '', 'options' => [ 'left' => esc_html__( 'Left', 'wpforms-lite' ), 'right' => esc_html__( 'Right', 'wpforms-lite' ), '' => esc_html__( 'Center', 'wpforms-lite' ), 'split' => esc_html__( 'Split', 'wpforms-lite' ), ], ], false ); $this->field_element( 'row', $field, [ 'slug' => 'nav_align', 'content' => $lbl . $fld, ] ); // Scroll animation toggle. $fld = $this->field_element( 'toggle', $field, [ 'slug' => 'scroll_disabled', 'value' => ! empty( $field['scroll_disabled'] ), 'desc' => esc_html__( 'Disable Scroll Animation', 'wpforms-lite' ), 'tooltip' => esc_html__( 'By default, a user\'s view is pulled to the top of each form page. Set to ON to disable this animation.', 'wpforms-lite' ), ], false ); $this->field_element( 'row', $field, [ 'slug' => 'scroll_disabled', 'content' => $fld, ] ); } // Custom CSS classes. $this->field_option( 'css', $field ); // Options close markup. $this->field_option( 'advanced-options', $field, [ 'markup' => 'close', ] ); } /** * Field preview inside the builder. * * @since 1.9.4 * * @param array $field Field data. */ public function field_preview( $field ) { // phpcs:ignore Generic.Metrics.CyclomaticComplexity.TooHigh $nav_align = 'wpforms-pagebreak-buttons-left'; $prev = ! empty( $field['prev'] ) ? $field['prev'] : esc_html__( 'Previous', 'wpforms-lite' ); $prev_class = empty( $field['prev'] ) && empty( $field['prev_toggle'] ) ? 'wpforms-hidden' : ''; $next = ! empty( $field['next'] ) ? $field['next'] : esc_html__( 'Next', 'wpforms-lite' ); $next_class = empty( $next ) ? 'wpforms-hidden' : ''; $position = ! empty( $field['position'] ) ? $field['position'] : 'normal'; $title = ! empty( $field['title'] ) ? $field['title'] : ''; $label = $position === 'top' ? esc_html__( 'First Page / Progress Indicator', 'wpforms-lite' ) : ''; $label = $position === 'normal' && empty( $label ) ? esc_html__( 'Page Break', 'wpforms-lite' ) : $label; /** * Fires before the page break is displayed on the preview. * * @since 1.7.9 * * @param array $form_data Form data and settings. * @param array $field Field data. */ do_action( 'wpforms_field_page_break_field_preview_before', $this->form_data, $field ); // phpcs:ignore WPForms.PHP.ValidateHooks.InvalidHookName if ( $position !== 'top' ) { if ( empty( $this->form_data ) && ! empty( $this->form_id ) ) { $this->form_data = wpforms()->obj( 'form' )->get( $this->form_id, [ 'content_only' => true ] ); } if ( empty( $this->pagebreak ) ) { $this->pagebreak = wpforms_get_pagebreak_details( $this->form_data ); } if ( ! empty( $this->pagebreak['top']['nav_align'] ) ) { $nav_align = 'wpforms-pagebreak-buttons-' . $this->pagebreak['top']['nav_align']; } echo '<div class="wpforms-pagebreak-buttons ' . sanitize_html_class( $nav_align ) . '">'; printf( '<button class="wpforms-pagebreak-button wpforms-pagebreak-prev %s">%s</button>', sanitize_html_class( $prev_class ), esc_html( $prev ) ); if ( $position !== 'bottom' ) { printf( '<button class="wpforms-pagebreak-button wpforms-pagebreak-next %s">%s</button>', sanitize_html_class( $next_class ), esc_html( $next ) ); if ( $next_class !== 'wpforms-hidden' ) { /** This action is documented in includes/class-frontend.php. */ do_action( 'wpforms_display_submit_after', $this->form_data, 'next' ); // phpcs:ignore WPForms.PHP.ValidateHooks.InvalidHookName } } echo '</div>'; } // Visual divider. echo '<div class="wpforms-pagebreak-divider">'; if ( $position !== 'bottom' ) { printf( '<span class="pagebreak-label">%1$s <span class="wpforms-pagebreak-title">%2$s</span>%3$s</span>', esc_html( $label ), esc_html( $title ), $this->get_field_preview_badge() // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ); } echo '<span class="line"></span>'; echo '</div>'; // Display a page indicator for the top position. if ( $position === 'top' ) { $this->field_preview_page_indicator( $field ); } /** * Fires after a page break is displayed on the preview. * * @since 1.7.9 * * @param array $form_data Form data and settings. * @param array $field Field data. */ do_action( 'wpforms_field_page_break_field_preview_after', $this->form_data, $field ); // phpcs:ignore WPForms.PHP.ValidateHooks.InvalidHookName } /** * Display page indicator preview in the builder. * * @since 1.10.0 * * @param array $field Field data. */ private function field_preview_page_indicator( array $field ): void { $indicator = ! empty( $field['indicator'] ) ? sanitize_html_class( $field['indicator'] ) : 'progress'; $color = ! empty( $field['indicator_color'] ) ? wpforms_sanitize_hex_color( $field['indicator_color'] ) : self::get_default_indicator_color(); // Get all pagebreak fields to determine total pages. if ( empty( $this->form_data ) && ! empty( $this->form_id ) ) { $this->form_data = wpforms()->obj( 'form' )->get( $this->form_id, [ 'content_only' => true ] ); } $pages = $this->get_preview_pages(); $wrapper_style = $indicator === 'none' ? ' style=display:none;' : ''; echo '<div class="wpforms-page-indicator wpforms-page-indicator-' . esc_attr( $indicator ) . '" data-allow-page-navigation="' . esc_attr( $field['allow_page_navigation'] ?? false ) . '"' . esc_attr( $wrapper_style ) . '>'; if ( $indicator === 'circles' ) { $this->field_preview_indicator_circles( $pages, $color ); } elseif ( $indicator === 'connector' ) { $this->field_preview_indicator_connector( $pages, $color ); } elseif ( $indicator === 'progress' ) { $this->field_preview_indicator_progress( $pages, $color, $field ); } echo '</div>'; } /** * Get preview pages for indicator. * * @since 1.10.0 * * @return array */ private function get_preview_pages(): array { if ( empty( $this->form_data['fields'] ) ) { return []; } $pages = []; foreach ( $this->form_data['fields'] as $form_field ) { if ( $form_field['type'] === 'pagebreak' && isset( $form_field['position'] ) && $form_field['position'] !== 'bottom' ) { $pages[] = [ 'title' => $form_field['title'] ?? '', ]; } } return $pages; } /** * Display circles indicator preview. * * @since 1.10.0 * * @param array $pages Pages data. * @param string $color Indicator color. */ private function field_preview_indicator_circles( array $pages, string $color ): void { $page_num = 1; foreach ( $pages as $page ) { $this->render_circles_indicator_item( $page, $page_num, $color ); ++$page_num; } } /** * Display connector indicator preview. * * @since 1.10.0 * * @param array $pages Pages data. * @param string $color Indicator color. */ private function field_preview_indicator_connector( array $pages, string $color ): void { $page_num = 1; $total_pages = max( count( $pages ), 2 ); $width = 100 / $total_pages . '%'; foreach ( $pages as $page ) { $this->render_connector_indicator_item( $page, $page_num, $color, $width ); ++$page_num; } } /** * Display progress indicator preview. * * @since 1.10.0 * * @param array $pages Pages data. * @param string $color Indicator color. * @param array $field Field data. */ private function field_preview_indicator_progress( array $pages, string $color, array $field ): void { $title = ! empty( $pages[0]['title'] ) ? $pages[0]['title'] : ''; $total_pages = max( count( $pages ), 2 ); $width = 100 / $total_pages . '%'; $background_color = ! empty( $color ) ? $color : ''; printf( '<span class="wpforms-page-indicator-page-title">%s</span>', esc_html( $title ) ); printf( '<span class="wpforms-page-indicator-page-title-sep" %s> - </span>', empty( $title ) ? 'style="display:none;"' : '' ); // Render progress text. $this->render_progress_text( $field, $total_pages ); // Render progress bar. $this->render_progress_bar( $width, $background_color ); } /** * Render progress text for progress indicator. * * @since 1.10.0 * * @param array $field Field data containing progress_text. * @param int $total_pages Total number of pages. */ protected function render_progress_text( array $field, int $total_pages ): void { $progress_text = ! empty( $field['progress_text'] ) ? str_replace( [ '{current_page}', '{last_page}' ], [ '%1$s', '%2$s' ], str_replace( '%', '%%', $field['progress_text'] ) ) : /* translators: %1$s - current step in multipage form, %2$d - total number of pages. */ esc_html__( 'Step %1$s of %2$d', 'wpforms-lite' ); printf( '<span class="wpforms-page-indicator-steps">' . esc_html( $progress_text ) . '</span>', '<span class="wpforms-page-indicator-steps-current">1</span>', esc_attr( $total_pages ) ); } /** * Render progress bar. * * @since 1.10.0 * * @param string $width Width percentage. * @param string $background_color Background color. */ protected function render_progress_bar( string $width, string $background_color ): void { printf( '<div class="wpforms-page-indicator-page-progress-wrap"><div class="wpforms-page-indicator-page-progress" style="width:%s;%s"></div></div>', esc_attr( $width ), ! empty( $background_color ) ? 'background-color:' . sanitize_hex_color( $background_color ) : '' ); } /** * Add a class to the builder field preview. * * @since 1.9.4 * * @param string|mixed $css CSS classes. * @param array $field Field data and settings. * * @return string */ public function preview_field_class( $css, $field ): string { $css = (string) $css; if ( $field['type'] !== 'pagebreak' ) { return $css; } if ( ! empty( $field['position'] ) && $field['position'] === 'top' ) { $css .= ' wpforms-field-stick wpforms-pagebreak-top'; } elseif ( ! empty( $field['position'] ) && $field['position'] === 'bottom' ) { $css .= ' wpforms-field-stick wpforms-pagebreak-bottom'; } else { $css .= ' wpforms-pagebreak-normal'; } return $css; } /** * Field display on the form front-end. * * @since 1.9.4 * * @param array $field Field data and settings. * @param array $deprecated Field attributes. * @param array $form_data Form data and settings. */ public function field_display( $field, $deprecated, $form_data ) { } /** * Get the default indicator color. * * @since 1.9.4 * * @return string */ public static function get_default_indicator_color(): string { $render_engine = wpforms_get_render_engine(); return array_key_exists( $render_engine, self::DEFAULT_INDICATOR_COLOR ) ? self::DEFAULT_INDICATOR_COLOR[ $render_engine ] : self::DEFAULT_INDICATOR_COLOR['modern']; } /** * Disallow the field preview "Duplicate" button. * * @since 1.9.9 * * @param bool|mixed $display Display switch. * @param array $field Field settings. * * @return bool */ public function field_display_duplicate_button( $display, array $field ): bool { $type = $field['type'] ?? ''; if ( $type === $this->type ) { // Pagebreak fields cannot be duplicated. return false; } return (bool) $display; } }
dvadf
dvadf
| ver. 1.4 |
Github
|
.
| PHP 7.3.33 | Generation time: 0.01 |
proxy
|
phpinfo
|
Settings