Commit realizado el 12:13:52 08-04-2024

This commit is contained in:
Pagina Web Monito
2024-04-08 12:13:55 -04:00
commit 0c33094de9
7815 changed files with 1365694 additions and 0 deletions

View File

@@ -0,0 +1,712 @@
<?php
/**
* Global admin related items and functionality.
*
* @since 1.3.9
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
use WPForms\Admin\Notice;
/**
* Load styles for all WPForms-related admin screens.
*
* @since 1.3.9
*/
function wpforms_admin_styles() {
if ( ! wpforms_is_admin_page() ) {
return;
}
$min = wpforms_get_min_suffix();
// jQuery confirm.
wp_enqueue_style(
'jquery-confirm',
WPFORMS_PLUGIN_URL . 'assets/lib/jquery.confirm/jquery-confirm.min.css',
[],
'3.3.4'
);
// Minicolors (color picker).
wp_enqueue_style(
'minicolors',
WPFORMS_PLUGIN_URL . 'assets/lib/jquery.minicolors/jquery.minicolors.min.css',
[],
'2.2.6'
);
// FontAwesome.
wp_enqueue_style(
'wpforms-font-awesome',
WPFORMS_PLUGIN_URL . 'assets/lib/font-awesome/font-awesome.min.css',
null,
'4.7.0'
);
// Main admin styles.
wp_enqueue_style(
'wpforms-admin',
WPFORMS_PLUGIN_URL . "assets/css/admin{$min}.css",
[],
WPFORMS_VERSION
);
// Remove TinyMCE editor styles from third-party themes and plugins.
remove_editor_styles();
// WordPress 5.7 color set.
if ( version_compare( get_bloginfo( 'version' ), '5.7', '>=' ) ) {
wp_enqueue_style(
'wpforms-admin-wp5.7-color',
WPFORMS_PLUGIN_URL . "assets/css/admin-wp5.7-colors{$min}.css",
[ 'wpforms-admin' ],
WPFORMS_VERSION
);
}
}
add_action( 'admin_enqueue_scripts', 'wpforms_admin_styles', 5 );
/**
* Load scripts for all WPForms-related admin screens.
*
* @since 1.3.9
*/
function wpforms_admin_scripts() {
if ( ! wpforms_is_admin_page() ) {
return;
}
$min = wpforms_get_min_suffix();
wp_enqueue_media();
// jQuery confirm.
wp_enqueue_script(
'jquery-confirm',
WPFORMS_PLUGIN_URL . 'assets/lib/jquery.confirm/jquery-confirm.min.js',
[ 'jquery' ],
'3.3.4',
false
);
// Minicolors (color picker).
wp_enqueue_script(
'minicolors',
WPFORMS_PLUGIN_URL . 'assets/lib/jquery.minicolors/jquery.minicolors.min.js',
[ 'jquery' ],
'2.2.6',
false
);
// Choices.js.
wp_enqueue_script(
'choicesjs',
WPFORMS_PLUGIN_URL . 'assets/lib/choices.min.js',
[],
'9.0.1',
false
);
// jQuery Conditionals.
wp_enqueue_script(
'jquery-conditionals',
WPFORMS_PLUGIN_URL . 'assets/lib/jquery.conditionals.min.js',
[ 'jquery' ],
'1.0.1',
false
);
wp_enqueue_script(
'wpforms-generic-utils',
WPFORMS_PLUGIN_URL . "assets/js/utils{$min}.js",
[ 'jquery' ],
WPFORMS_VERSION,
true
);
// Load admin utils JS.
wp_enqueue_script(
'wpforms-admin-utils',
WPFORMS_PLUGIN_URL . "assets/js/admin-utils{$min}.js",
[ 'jquery' ],
WPFORMS_VERSION,
true
);
// Main admin script.
wp_enqueue_script(
'wpforms-admin',
WPFORMS_PLUGIN_URL . "assets/js/admin{$min}.js",
[ 'jquery' ],
WPFORMS_VERSION,
false
);
$default_choicesjs_loading_text = esc_html__( 'Loading...', 'wpforms-lite' );
$default_choicesjs_no_results_text = esc_html__( 'No results found', 'wpforms-lite' );
$default_choicesjs_no_choices_text = esc_html__( 'No choices to choose from', 'wpforms-lite' );
$strings = [
'addon_activate' => esc_html__( 'Activate', 'wpforms-lite' ),
'addon_activated' => esc_html__( 'Activated', 'wpforms-lite' ),
'addon_active' => esc_html__( 'Active', 'wpforms-lite' ),
'addon_deactivate' => esc_html__( 'Deactivate', 'wpforms-lite' ),
'addon_inactive' => esc_html__( 'Inactive', 'wpforms-lite' ),
'addon_install' => esc_html__( 'Install Addon', 'wpforms-lite' ),
'addon_error' => sprintf(
wp_kses( /* translators: %1$s - addon download URL, %2$s - link to manual installation guide. */
__( 'Could not install the addon. Please <a href="%1$s" target="_blank" rel="noopener noreferrer">download it from wpforms.com</a> and <a href="%2$s" target="_blank" rel="noopener noreferrer">install it manually</a>.', 'wpforms-lite' ),
[
'a' => [
'href' => true,
'target' => true,
'rel' => true,
],
]
),
'https://wpforms.com/account/licenses/',
'https://wpforms.com/docs/how-to-manually-install-addons-in-wpforms/'
),
'plugin_error' => esc_html__( 'Could not install the plugin automatically. Please download and install it manually.', 'wpforms-lite' ),
'addon_search' => esc_html__( 'Searching Addons', 'wpforms-lite' ),
'ajax_url' => admin_url( 'admin-ajax.php' ),
'cancel' => esc_html__( 'Cancel', 'wpforms-lite' ),
'continue' => esc_html__( 'Continue', 'wpforms-lite' ),
'close' => esc_html__( 'Close', 'wpforms-lite' ),
'close_refresh' => esc_html__( 'Close and Refresh', 'wpforms-lite' ),
'entry_delete_confirm' => esc_html__( 'Are you sure you want to delete this entry and all its information (files, notes, logs, etc.)?', 'wpforms-lite' ),
'entry_delete_all_confirm' => esc_html__( 'Are you sure you want to delete ALL entries and all their information (files, notes, logs, etc.)?', 'wpforms-lite' ),
'entry_delete_n_confirm' => sprintf( /* translators: %s - entry count. */
esc_html__( 'Are you sure you want to delete %s entry(s) and all the information (files, notes, logs, etc.)?', 'wpforms-lite' ),
'{entry_count}'
),
'entry_trash_confirm' => esc_html__( 'Are you sure you want to trash this entry and all its information (files, notes, logs, etc.)?', 'wpforms-lite' ),
'entry_trash_all_confirm' => esc_html__( 'Are you sure you want to trash ALL entries and all their information (files, notes, logs, etc.)?', 'wpforms-lite' ),
'entry_trash_n_confirm' => sprintf( /* translators: %s - entry count. */
esc_html__( 'Are you sure you want to trash %s entry(s) and all the information (files, notes, logs, etc.)?', 'wpforms-lite' ),
'{entry_count}'
),
'entry_empty_fields_hide' => esc_html__( 'Hide Empty Fields', 'wpforms-lite' ),
'entry_empty_fields_show' => esc_html__( 'Show Empty Fields', 'wpforms-lite' ),
'entry_field_columns' => esc_html__( 'Entries Field Columns', 'wpforms-lite' ),
'entry_note_delete_confirm' => esc_html__( 'Are you sure you want to delete this note?', 'wpforms-lite' ),
'entry_unstar' => esc_html__( 'Unstar entry', 'wpforms-lite' ),
'entry_star' => esc_html__( 'Star entry', 'wpforms-lite' ),
'entry_read' => esc_html__( 'Mark entry read', 'wpforms-lite' ),
'entry_unread' => esc_html__( 'Mark entry unread', 'wpforms-lite' ),
'form_delete_confirm' => esc_html__( 'Are you sure you want to delete this form and all its entries?', 'wpforms-lite' ),
'form_delete_n_confirm' => esc_html__( 'Are you sure you want to delete the selected forms and all their entries?', 'wpforms-lite' ),
'form_delete_all_confirm' => esc_html__( 'Are you sure you want to delete ALL the forms in the trash and all their entries?', 'wpforms-lite' ),
'form_duplicate_confirm' => esc_html__( 'Are you sure you want to duplicate this form?', 'wpforms-lite' ),
'heads_up' => esc_html__( 'Heads up!', 'wpforms-lite' ),
'importer_forms_required' => esc_html__( 'Please select at least one form to import.', 'wpforms-lite' ),
'isPro' => wpforms()->is_pro(),
'nonce' => wp_create_nonce( 'wpforms-admin' ),
'almost_done' => esc_html__( 'Almost Done', 'wpforms-lite' ),
'thanks_for_interest' => esc_html__( 'Thanks for your interest in WPForms Pro!', 'wpforms-lite' ),
'oops' => esc_html__( 'Oops!', 'wpforms-lite' ),
'ok' => esc_html__( 'OK', 'wpforms-lite' ),
'plugin_install_activate_btn' => esc_html__( 'Install and Activate', 'wpforms-lite' ),
'plugin_install_activate_confirm' => esc_html__( 'needs to be installed and activated to import its forms. Would you like us to install and activate it for you?', 'wpforms-lite' ),
'plugin_activate_btn' => esc_html__( 'Activate', 'wpforms-lite' ),
'plugin_activate_confirm' => esc_html__( 'needs to be activated to import its forms. Would you like us to activate it for you?', 'wpforms-lite' ),
'provider_delete_confirm' => esc_html__( 'Are you sure you want to disconnect this account?', 'wpforms-lite' ),
'provider_delete_error' => esc_html__( 'Could not disconnect this account.', 'wpforms-lite' ),
'provider_auth_error' => esc_html__( 'Could not authenticate with the provider.', 'wpforms-lite' ),
'connecting' => esc_html__( 'Connecting...', 'wpforms-lite' ),
'save_refresh' => esc_html__( 'Save and Refresh', 'wpforms-lite' ),
'server_error' => esc_html__( 'Unfortunately there was a server connection error.', 'wpforms-lite' ),
'settings_form_style_base' => sprintf(
wp_kses( /* translators: %s - WPForms.com docs page URL. */
__( 'You\'ve selected <strong>Base Styling Only</strong>, which may result in styling issues. <a href="%s" target="_blank" rel="noopener noreferrer">Please check out our tutorial</a> for common issues and recommendations.', 'wpforms-lite' ),
[
'strong' => [],
'a' => [
'href' => [],
'target' => [],
'rel' => [],
],
]
),
esc_url( wpforms_utm_link( 'https://wpforms.com/docs/how-to-choose-an-include-form-styling-setting/', 'settings-license-modal', 'Base Styling Only' ) )
),
'settings_form_style_none' => sprintf(
wp_kses( /* translators: %s - WPForms.com docs page URL. */
__( 'You\'ve selected <strong>No Styling</strong>, which will likely result in significant styling issues and is recommended only for developers. <a href="%s" target="_blank" rel="noopener noreferrer">Please check out our tutorial</a> for more details and recommendations.', 'wpforms-lite' ),
[
'strong' => [],
'a' => [
'href' => [],
'target' => [],
'rel' => [],
],
]
),
esc_url( wpforms_utm_link( 'https://wpforms.com/docs/how-to-choose-an-include-form-styling-setting/', 'settings-license-modal', 'No Styling' ) )
),
'testing' => esc_html__( 'Testing', 'wpforms-lite' ),
'upgrade_completed' => esc_html__( 'Upgrade was successfully completed!', 'wpforms-lite' ),
'upload_image_title' => esc_html__( 'Upload or Choose Your Image', 'wpforms-lite' ),
'upload_image_button' => esc_html__( 'Use Image', 'wpforms-lite' ),
'upgrade_modal' => wpforms_get_upgrade_modal_text(),
'choicesjs_loading' => $default_choicesjs_loading_text,
'choicesjs_no_results' => $default_choicesjs_no_results_text,
'choicesjs_no_choices' => $default_choicesjs_no_choices_text,
'debug' => wpforms_debug(),
'edit_license' => esc_html__( 'To edit the License Key, please first click the Remove Key button. Please note that removing this key will remove access to updates, addons, and support.', 'wpforms-lite' ),
'something_went_wrong' => esc_html__( 'Something went wrong', 'wpforms-lite' ),
'success' => esc_html__( 'Success', 'wpforms-lite' ),
'loading' => esc_html__( 'Loading...', 'wpforms-lite' ),
'use_simple_contact_form' => esc_html__( 'Use Simple Contact Form Template', 'wpforms-lite' ),
'error_select_template' => esc_html__( 'Please close the form builder and try again. If the error persists, contact our support team.', 'wpforms-lite' ),
'try_again' => sprintf(
wp_kses( /* translators: %s - link to WPForms.com docs page. */
__( 'Something went wrong. Please try again, and if the problem persists, <a href="%1$s" target="_blank" rel="noopener noreferrer">contact our support team</a>.', 'wpforms-lite' ),
[
'a' => [
'href' => [],
'target' => [],
'rel' => [],
],
]
),
esc_url( wpforms_utm_link( 'https://wpforms.com/contact/', 'error-modal', 'contact our support team' ) )
),
];
/**
* Allow theme/plugin developers to adjust main strings on backend/admin part.
*
* @since 1.3.9
*
* @param array $strings Main admin localized strings.
*/
$strings = (array) apply_filters( 'wpforms_admin_strings', $strings );
/**
* Allow theme/plugin developers to adjust Choices.js settings on backend/admin part.
*
* @see https://github.com/Choices-js/Choices#setup For configuration options.
*
* @since 1.7.3
*
* @param array $choicesjs_config Choicesjs configuration.
*/
$choicesjs_config = (array) apply_filters(
'wpforms_admin_scripts_choicesjs_config',
[
'searchEnabled' => false,
// Forces the search to look for exact matches anywhere in the string.
'fuseOptions' => [
'threshold' => 0.1,
'distance' => 1000,
],
'loadingText' => ! empty( $strings['choicesjs_loading'] ) ? $strings['choicesjs_loading'] : $default_choicesjs_loading_text,
'noResultsText' => ! empty( $strings['choicesjs_no_results'] ) ? $strings['choicesjs_no_results'] : $default_choicesjs_no_results_text,
'noChoicesText' => ! empty( $strings['choicesjs_no_choices'] ) ? $strings['choicesjs_no_choices'] : $default_choicesjs_no_choices_text,
]
);
wp_localize_script(
'wpforms-admin',
'wpforms_admin_choicesjs_config',
$choicesjs_config
);
wp_localize_script(
'wpforms-admin',
'wpforms_admin',
$strings
);
}
add_action( 'admin_enqueue_scripts', 'wpforms_admin_scripts' );
/**
* Add body class to WPForms admin pages for easy reference.
*
* @since 1.3.9
*
* @param string $classes CSS classes, space separated.
*
* @return string
*/
function wpforms_admin_body_class( $classes ) {
if ( ! wpforms_is_admin_page() ) {
return $classes;
}
return "$classes wpforms-admin-page";
}
add_filter( 'admin_body_class', 'wpforms_admin_body_class', 10, 1 );
/**
* Output the WPForms admin header.
*
* @since 1.3.9
*/
function wpforms_admin_header() {
// Bail if we're not on a WPForms screen or page (also exclude form builder).
if ( ! wpforms_is_admin_page() ) {
return;
}
/**
* Prevent admin header outputting if needed.
*
* @since 1.5.7
*
* @param bool $is_admin_header_visible True if admin page header should be outputted.
*/
if ( ! apply_filters( 'wpforms_admin_header', true ) ) {
return;
}
// Omit header from Welcome activation screen.
// phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput.InputNotValidated
if ( sanitize_key( $_REQUEST['page'] ) === 'wpforms-getting-started' ) {
return;
}
/**
* Fire before the admin header is outputted.
*
* @since 1.5.7
*/
do_action( 'wpforms_admin_header_before' );
?>
<div id="wpforms-header-temp"></div>
<div id="wpforms-header" class="wpforms-header">
<img class="wpforms-header-logo" src="<?php echo esc_url( WPFORMS_PLUGIN_URL . 'assets/images/logo.png' ); ?>" alt="WPForms Logo">
</div>
<?php
/**
* Fire after the admin header is outputted.
*
* @since 1.5.7
*/
do_action( 'wpforms_admin_header_after' );
}
add_action( 'in_admin_header', 'wpforms_admin_header', 100 );
/**
* Remove non-WPForms notices from WPForms pages.
*
* @since 1.3.9
* @since 1.6.9 Added callback for removing on `admin_footer` hook.
*/
function wpforms_admin_hide_unrelated_notices() { // phpcs:ignore Generic.Metrics.CyclomaticComplexity.MaxExceeded, Generic.Metrics.NestingLevel.MaxExceeded
if ( ! wpforms_is_admin_page() ) {
return;
}
global $wp_filter;
// Define rules to remove callbacks.
$rules = [
'user_admin_notices' => [], // remove all callbacks.
'admin_notices' => [],
'all_admin_notices' => [],
'admin_footer' => [
'render_delayed_admin_notices', // remove this particular callback.
],
];
// Extra deny callbacks (will be removed for each hook tag defined in $rules).
$common_deny_callbacks = [
'wpformsdb_admin_notice', // 'Database for WPForms' plugin.
];
$notice_types = array_keys( $rules );
foreach ( $notice_types as $notice_type ) {
if ( empty( $wp_filter[ $notice_type ]->callbacks ) || ! is_array( $wp_filter[ $notice_type ]->callbacks ) ) {
continue;
}
$remove_all_filters = empty( $rules[ $notice_type ] );
foreach ( $wp_filter[ $notice_type ]->callbacks as $priority => $hooks ) {
foreach ( $hooks as $name => $arr ) {
if ( is_object( $arr['function'] ) && is_callable( $arr['function'] ) ) {
if ( $remove_all_filters ) {
unset( $wp_filter[ $notice_type ]->callbacks[ $priority ][ $name ] );
}
continue;
}
$class = ! empty( $arr['function'][0] ) && is_object( $arr['function'][0] ) ? strtolower( get_class( $arr['function'][0] ) ) : '';
// Remove all callbacks except WPForms notices.
if ( $remove_all_filters && strpos( $class, 'wpforms' ) === false ) {
unset( $wp_filter[ $notice_type ]->callbacks[ $priority ][ $name ] );
continue;
}
$cb = is_array( $arr['function'] ) ? $arr['function'][1] : $arr['function'];
// Remove a specific callback.
if ( ! $remove_all_filters ) {
if ( in_array( $cb, $rules[ $notice_type ], true ) ) {
unset( $wp_filter[ $notice_type ]->callbacks[ $priority ][ $name ] );
}
continue;
}
// Remove non-WPForms callbacks from `$common_deny_callbacks` denylist.
if ( in_array( $cb, $common_deny_callbacks, true ) ) {
unset( $wp_filter[ $notice_type ]->callbacks[ $priority ][ $name ] );
}
}
}
}
}
add_action( 'admin_print_scripts', 'wpforms_admin_hide_unrelated_notices' );
/**
* Upgrade link used within the various admin pages.
*
* Previously was only included as a method in wpforms-lite.php, but made
* available globally in 1.3.9.
*
* @since 1.3.9
*
* @param string $medium URL parameter: utm_medium.
* @param string $content URL parameter: utm_content.
*
* @return string
*/
function wpforms_admin_upgrade_link( $medium = 'link', $content = '' ) {
$url = 'https://wpforms.com/lite-upgrade/';
if ( wpforms()->is_pro() ) {
$license_key = wpforms_get_license_key();
$url = add_query_arg(
'license_key',
sanitize_text_field( $license_key ),
'https://wpforms.com/pricing/'
);
}
// phpcs:ignore WPForms.Comments.PHPDocHooks.RequiredHookDocumentation
$upgrade = wpforms_utm_link( $url, apply_filters( 'wpforms_upgrade_link_medium', $medium ), $content );
/**
* Modify upgrade link.
*
* @since 1.5.1
*
* @param string $upgrade Upgrade links.
*/
return apply_filters( 'wpforms_upgrade_link', $upgrade );
}
/**
* Check the current PHP version and display a notice if on unsupported PHP.
*
* @since 1.4.0.1
* @since 1.5.0 Raising this awareness of old PHP version message from 5.2 to 5.3.
* @since 1.7.9 Raising this awareness of old PHP version message to 7.1.
* @since 1.8.4 Raising this awareness of old PHP version message to 7.3.
*/
function wpforms_check_php_version() {
// Display for PHP below 7.4.
if ( PHP_VERSION_ID >= 70400 ) {
return;
}
// Display for admins only.
if ( ! is_super_admin() ) {
return;
}
// Display on Dashboard page only.
if ( isset( $GLOBALS['pagenow'] ) && $GLOBALS['pagenow'] !== 'index.php' ) {
return;
}
// Display the notice, finally.
Notice::error(
'<p>' .
sprintf(
wp_kses( /* translators: %1$s - WPForms plugin name; %2$s - WPForms.com URL to a related doc. */
__( 'Your site is running an outdated version of PHP that is no longer supported and may cause issues with %1$s. <a href="%2$s" target="_blank" rel="noopener noreferrer">Read more</a> for additional information.', 'wpforms-lite' ),
[
'a' => [
'href' => [],
'target' => [],
'rel' => [],
],
]
),
'<strong>WPForms</strong>',
'https://wpforms.com/docs/supported-php-version/'
) .
'<br><br><em>' .
wp_kses(
__( '<strong>Please Note:</strong> Support for PHP 7.3 and below will be discontinued soon. After this, if no further action is taken, WPForms functionality will be disabled.', 'wpforms-lite' ),
[
'strong' => [],
'em' => [],
]
) .
'</em></p>'
);
}
add_action( 'admin_init', 'wpforms_check_php_version' );
/**
* Get an upgrade modal text.
*
* @since 1.4.4
*
* @param string $type Either "pro" or "elite". Default is "pro".
*
* @return string
*/
function wpforms_get_upgrade_modal_text( $type = 'pro' ) {
switch ( $type ) {
case 'elite':
$level = 'WPForms Elite';
break;
case 'pro':
default:
$level = 'WPForms Pro';
}
if ( wpforms()->is_pro() ) {
return '<p>' .
sprintf(
wp_kses( /* translators: %s - WPForms.com contact page URL. */
__( 'Thank you for considering upgrading. If you have any questions, please <a href="%s" target="_blank" rel="noopener noreferrer">let us know</a>.', 'wpforms-lite' ),
[
'a' => [
'href' => [],
'target' => [],
'rel' => [],
],
]
),
esc_url(
wpforms_utm_link(
'https://wpforms.com/contact/',
'Upgrade Follow Up Modal',
'Contact Support'
)
)
) .
'</p>' .
'<p>' .
wp_kses(
__( 'After upgrading, your license key will remain the same.<br>You may need to do a quick refresh to unlock your new addons. In your WordPress admin, go to <strong>WPForms &raquo; Settings</strong>. If you don\'t see your updated plan, click <em>refresh</em>.', 'wpforms-lite' ),
[
'strong' => [],
'br' => [],
'em' => [],
]
) .
'</p>' .
'<p>' .
sprintf(
wp_kses( /* translators: %s - WPForms.com upgrade from Lite to paid docs page URL. */
__( 'Check out <a href="%s" target="_blank" rel="noopener noreferrer">our documentation</a> for step-by-step instructions.', 'wpforms-lite' ),
[
'a' => [
'href' => [],
'target' => [],
'rel' => [],
],
]
),
'https://wpforms.com/docs/upgrade-wpforms-license/'
) .
'</p>';
}
return '<p>' .
sprintf(
wp_kses( /* translators: %s - WPForms.com contact page URL. */
__( 'If you have any questions or issues just <a href="%s" target="_blank" rel="noopener noreferrer">let us know</a>.', 'wpforms-lite' ),
[
'a' => [
'href' => [],
'target' => [],
'rel' => [],
],
]
),
esc_url(
wpforms_utm_link(
'https://wpforms.com/contact/',
'Upgrade Intention Alert',
'Upgrade Intention Alert'
)
)
) .
'</p>' .
'<p>' .
sprintf(
wp_kses( /* translators: %s - license level, WPForms Pro or WPForms Elite. */
__( 'After purchasing a license, just <strong>enter your license key on the WPForms Settings page</strong>. This will let your site automatically upgrade to %s! (Don\'t worry, all your forms and settings will be preserved.)', 'wpforms-lite' ),
[
'strong' => [],
'br' => [],
]
),
$level
) .
'</p>' .
'<p>' .
sprintf(
wp_kses( /* translators: %s - WPForms.com upgrade from Lite to paid docs page URL. */
__( 'Check out <a href="%s" target="_blank" rel="noopener noreferrer">our documentation</a> for step-by-step instructions.', 'wpforms-lite' ),
[
'a' => [
'href' => [],
'target' => [],
'rel' => [],
],
]
),
esc_url(
wpforms_utm_link(
'https://wpforms.com/docs/upgrade-wpforms-lite-paid-license/',
'Upgrade Intention Alert',
'Upgrade Documentation'
)
)
) .
'</p>';
}
/**
* Hide the wp-admin area "Version x.x" in footer on WPForms pages.
*
* @since 1.5.7
*
* @param string $text Default "Version x.x" or "Get Version x.x" text.
*
* @return string
*/
function wpforms_admin_hide_wp_version( $text ) {
// Reset text if we're not on a WPForms screen or page.
if ( wpforms_is_admin_page() ) {
return 'WPForms ' . WPFORMS_VERSION;
}
return $text;
}
add_filter( 'update_footer', 'wpforms_admin_hide_wp_version', PHP_INT_MAX );

View File

@@ -0,0 +1,834 @@
<?php
/**
* Ajax actions used in by admin.
*
* @since 1.0.0
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Save a form.
*
* @since 1.0.0
*/
function wpforms_save_form() {
// Run a security check.
if ( ! check_ajax_referer( 'wpforms-builder', 'nonce', false ) ) {
wp_send_json_error( esc_html__( 'Your session expired. Please reload the builder.', 'wpforms-lite' ) );
}
// Check for permissions.
if ( ! wpforms_current_user_can( 'edit_forms' ) ) {
wp_send_json_error( esc_html__( 'You are not allowed to perform this action.', 'wpforms-lite' ) );
}
// Check for form data.
if ( empty( $_POST['data'] ) ) {
wp_send_json_error( esc_html__( 'Something went wrong while performing this action.', 'wpforms-lite' ) );
}
// phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
$form_post = json_decode( wp_unslash( $_POST['data'] ) );
$data = [
'fields' => [],
];
if ( $form_post ) {
foreach ( $form_post as $post_input_data ) {
// For input names that are arrays (e.g. `menu-item-db-id[3][4][5]`),
// derive the array path keys via regex and set the value in $_POST.
preg_match( '#([^\[]*)(\[(.+)\])?#', $post_input_data->name, $matches );
$array_bits = [ $matches[1] ];
if ( isset( $matches[3] ) ) {
$array_bits = array_merge( $array_bits, explode( '][', $matches[3] ) );
}
$new_post_data = [];
// Build the new array value from leaf to trunk.
for ( $i = count( $array_bits ) - 1; $i >= 0; $i -- ) {
if ( $i === count( $array_bits ) - 1 ) {
$new_post_data[ $array_bits[ $i ] ] = wp_slash( $post_input_data->value );
} else {
$new_post_data = [
$array_bits[ $i ] => $new_post_data,
];
}
}
$data = array_replace_recursive( $data, $new_post_data );
}
}
// Get form tags.
$form_tags = isset( $data['settings']['form_tags_json'] ) ? json_decode( wp_unslash( $data['settings']['form_tags_json'] ), true ) : [];
// Clear not needed data.
unset( $data['settings']['form_tags_json'] );
// Store tags labels in the form settings.
$data['settings']['form_tags'] = wp_list_pluck( $form_tags, 'label' );
// Update form data.
$form_id = wpforms()->get( 'form' )->update( $data['id'], $data, [ 'context' => 'save_form' ] );
/**
* Fires after updating form data.
*
* @since 1.4.0
*
* @param int $form_id Form ID.
* @param array $data Form data.
*/
do_action( 'wpforms_builder_save_form', $form_id, $data );
if ( ! $form_id ) {
wp_send_json_error( esc_html__( 'Something went wrong while saving the form.', 'wpforms-lite' ) );
}
// Update form tags.
wp_set_post_terms(
$form_id,
wpforms()->get( 'forms_tags_ajax' )->get_processed_tags( $form_tags ),
WPForms_Form_Handler::TAGS_TAXONOMY
);
$response_data = [
'form_name' => esc_html( $data['settings']['form_title'] ),
'form_desc' => $data['settings']['form_desc'],
'redirect' => admin_url( 'admin.php?page=wpforms-overview' ),
];
/**
* Allows filtering ajax response data after form was saved.
*
* @since 1.5.1
*
* @param array $response_data The data to be sent in the response.
* @param int $form_id Form ID.
* @param array $data Form data.
*/
$response_data = apply_filters(
'wpforms_builder_save_form_response_data',
$response_data,
$form_id,
$data
);
wp_send_json_success( $response_data );
}
add_action( 'wp_ajax_wpforms_save_form', 'wpforms_save_form' );
/**
* Create a new form.
*
* @since 1.0.0
*/
function wpforms_new_form() { // phpcs:ignore Generic.Metrics.CyclomaticComplexity.TooHigh
check_ajax_referer( 'wpforms-builder', 'nonce' );
if ( empty( $_POST['title'] ) ) {
wp_send_json_error(
[
'error_type' => 'missing_form_title',
'message' => esc_html__( 'No form name provided.', 'wpforms-lite' ),
]
);
}
$form_title = sanitize_text_field( wp_unslash( $_POST['title'] ) );
$form_template = empty( $_POST['template'] ) ? 'blank' : sanitize_text_field( wp_unslash( $_POST['template'] ) );
if ( ! wpforms()->get( 'builder_templates' )->is_valid_template( $form_template ) ) {
wp_send_json_error(
[
'error_type' => 'invalid_template',
'message' => esc_html__( 'The template you selected is currently not available, but you can try again later. If you continue to have trouble, please reach out to support.', 'wpforms-lite' ),
]
);
}
$title_query = new WP_Query(
[
'post_type' => 'wpforms',
'title' => $form_title,
'posts_per_page' => 1,
'fields' => 'ids',
'update_post_meta_cache' => false,
'update_post_term_cache' => false,
'no_found_rows' => true,
]
);
$title_exists = $title_query->post_count > 0;
$form_id = wpforms()->get( 'form' )->add(
$form_title,
[],
[
'template' => $form_template,
]
);
if ( $title_exists ) {
// Skip creating a revision for this action.
remove_action( 'post_updated', 'wp_save_post_revision' );
wp_update_post(
[
'ID' => $form_id,
'post_title' => $form_title . ' (ID #' . $form_id . ')',
]
);
// Restore the initial revisions state.
add_action( 'post_updated', 'wp_save_post_revision', 10, 1 );
}
if ( ! $form_id ) {
wp_send_json_error(
[
'error_type' => 'cant_create_form',
'message' => esc_html__( 'Error creating form.', 'wpforms-lite' ),
]
);
}
if ( wpforms_current_user_can( 'edit_form_single', $form_id ) ) {
wp_send_json_success(
[
'id' => $form_id,
'redirect' => add_query_arg(
[
'view' => 'fields',
'form_id' => $form_id,
'newform' => '1',
],
admin_url( 'admin.php?page=wpforms-builder' )
),
]
);
}
if ( wpforms_current_user_can( 'view_forms' ) ) {
wp_send_json_success( [ 'redirect' => admin_url( 'admin.php?page=wpforms-overview' ) ] );
}
wp_send_json_success( [ 'redirect' => admin_url() ] );
}
add_action( 'wp_ajax_wpforms_new_form', 'wpforms_new_form' );
/**
* Update form template.
*
* @since 1.0.0
*/
function wpforms_update_form_template() {
// Run a security check.
check_ajax_referer( 'wpforms-builder', 'nonce' );
// Check for form ID.
if ( empty( $_POST['form_id'] ) ) {
wp_send_json_error(
[
'error_type' => 'invalid_form_id',
'message' => esc_html__( 'No form ID provided.', 'wpforms-lite' ),
]
);
}
// Set initial variables.
$form_id = absint( $_POST['form_id'] );
$form_template = empty( $_POST['template'] ) ? 'blank' : sanitize_text_field( wp_unslash( $_POST['template'] ) );
// Check for valid template.
if ( ! wpforms()->get( 'builder_templates' )->is_valid_template( $form_template ) ) {
wp_send_json_error(
[
'error_type' => 'invalid_template',
'message' => esc_html__( 'The template you selected is currently not available, but you can try again later. If you continue to have trouble, please reach out to support.', 'wpforms-lite' ),
]
);
}
// Get current form data.
$data = wpforms()->get( 'form' )->get(
$form_id,
[
'content_only' => true,
]
);
// Get the cached data from the form template JSON.
$template_data = wpforms()->get( 'builder_templates' )->get_template( $form_template );
// If the template title is set, use it. Otherwise, clear the form title.
$template_title = ! empty( $template_data['name'] ) ? $template_data['name'] : '';
// If the form title is set, use it. Otherwise, use the template title.
$form_title = ! empty( $_POST['title'] ) ? sanitize_text_field( wp_unslash( $_POST['title'] ) ) : $template_title;
// If the these template titles are empty, use the form title.
$form_pages_title = $template_title ? $template_title : $form_title;
$form_conversational_title = ! empty( $template_data['data']['settings']['conversational_forms_title'] ) ? $template_data['data']['settings']['conversational_forms_title'] : $form_title;
// If these template slugs are empty, use the form title.
$form_conversational_slug = ! empty( $template_data['data']['settings']['conversational_forms_page_slug'] ) ? $template_data['data']['settings']['conversational_forms_page_slug'] : $form_title;
$form_pages_slug = ! empty( $template_data['data']['settings']['form_pages_page_slug'] ) ? $template_data['data']['settings']['form_pages_page_slug'] : $form_title;
// Loop over notifications.
$notifications = isset( $template_data['data']['settings']['notifications'] ) ? $template_data['data']['settings']['notifications'] : [];
foreach ( $notifications as $key => $notification ) {
// If the subject is empty, set it to an empty string.
$notification_subject = ! empty( $notification['subject'] ) ? sanitize_text_field( $notification['subject'] ) : '';
$data['settings']['notifications'][ $key ]['subject'] = $notification_subject;
}
// Loop over confirmations.
$confirmations = isset( $template_data['data']['settings']['confirmations'] ) ? $template_data['data']['settings']['confirmations'] : [];
foreach ( $confirmations as $key => $confirmation ) {
// If the message is empty, set it to an empty string.
$confirmation_message = ! empty( $confirmation['message'] ) ? wp_kses_post( $confirmation['message'] ) : '';
$data['settings']['confirmations'][ $key ]['message'] = $confirmation_message;
}
// Set updated form titles.
$data['settings']['form_title'] = sanitize_text_field( $form_title );
$data['settings']['form_pages_title'] = sanitize_text_field( $form_pages_title );
$data['settings']['conversational_forms_title'] = sanitize_text_field( $form_conversational_title );
// Set updated form slugs.
$data['settings']['form_pages_page_slug'] = sanitize_title( $form_pages_slug );
$data['settings']['conversational_forms_page_slug'] = sanitize_title( $form_conversational_slug );
// Try to update the form.
$updated = (bool) wpforms()->get( 'form' )->update(
$form_id,
$data,
[
'template' => $form_template,
]
);
// If the form was updated, return the form ID and redirect to the form builder.
if ( $updated ) {
wp_send_json_success(
[
'id' => $form_id,
'redirect' => add_query_arg(
[
'view' => 'fields',
'form_id' => $form_id,
],
admin_url( 'admin.php?page=wpforms-builder' )
),
]
);
}
// Otherwise, return an error.
wp_send_json_error(
[
'error_type' => 'cant_update',
'message' => esc_html__( 'Error updating form template.', 'wpforms-lite' ),
]
);
}
add_action( 'wp_ajax_wpforms_update_form_template', 'wpforms_update_form_template' );
/**
* Form Builder update next field ID.
*
* @since 1.2.9
*/
function wpforms_builder_increase_next_field_id() {
// Run a security check.
check_ajax_referer( 'wpforms-builder', 'nonce' );
// Check for permissions.
if ( ! wpforms_current_user_can( 'edit_forms' ) ) {
wp_send_json_error();
}
// Check for required items.
if ( empty( $_POST['form_id'] ) ) {
wp_send_json_error();
}
$args = [];
// In the case of duplicating the Layout field that contains a bunch of fields,
// we need to set the next `field_id` to the desired value which is passed via POST argument.
if ( ! empty( $_POST['field_id'] ) ) {
$args['field_id'] = absint( $_POST['field_id'] );
}
wpforms()->get( 'form' )->next_field_id( absint( $_POST['form_id'] ), $args );
wp_send_json_success();
}
add_action( 'wp_ajax_wpforms_builder_increase_next_field_id', 'wpforms_builder_increase_next_field_id' );
/**
* Form Builder Dynamic Choices option toggle.
*
* This can be triggered with select/radio/checkbox fields.
*
* @since 1.2.8
*/
function wpforms_builder_dynamic_choices() {
// Run a security check.
check_ajax_referer( 'wpforms-builder', 'nonce' );
// Check for permissions.
if ( ! wpforms_current_user_can( 'edit_forms' ) ) {
wp_send_json_error();
}
// Check for valid/required items.
if ( ! isset( $_POST['field_id'] ) || empty( $_POST['type'] ) || ! in_array( $_POST['type'], [ 'post_type', 'taxonomy' ], true ) ) {
wp_send_json_error();
}
$type = sanitize_key( $_POST['type'] );
$id = absint( $_POST['field_id'] );
// Fetch the option row HTML to be returned to the builder.
$field = new WPForms_Field_Select( false );
$field_args = [
'id' => $id,
'dynamic_choices' => $type,
];
$option_row = $field->field_option( 'dynamic_choices_source', $field_args, [], false );
wp_send_json_success(
[
'markup' => $option_row,
]
);
}
add_action( 'wp_ajax_wpforms_builder_dynamic_choices', 'wpforms_builder_dynamic_choices' );
/**
* Form Builder Dynamic Choices Source option toggle.
*
* This can be triggered with select/radio/checkbox fields.
*
* @since 1.2.8
*/
function wpforms_builder_dynamic_source() {
// Run a security check.
check_ajax_referer( 'wpforms-builder', 'nonce' );
// Check for permissions.
if ( ! wpforms_current_user_can( 'edit_forms' ) ) {
wp_send_json_error();
}
// Check for required items.
if ( ! isset( $_POST['field_id'] ) || empty( $_POST['form_id'] ) || empty( $_POST['type'] ) || empty( $_POST['source'] ) ) {
wp_send_json_error();
}
$type = sanitize_key( $_POST['type'] );
$source = sanitize_key( $_POST['source'] );
$id = absint( $_POST['field_id'] );
$form_id = absint( $_POST['form_id'] );
$items = [];
$total = 0;
$source_name = '';
$type_name = '';
if ( $type === 'post_type' ) {
$type_name = esc_html__( 'post type', 'wpforms-lite' );
$args = [
'post_type' => $source,
'posts_per_page' => 20,
'orderby' => 'title',
'order' => 'ASC',
];
$posts = wpforms_get_hierarchical_object(
apply_filters(
'wpforms_dynamic_choice_post_type_args',
$args,
[
'id' => $id,
],
$form_id
),
true
);
$total = wp_count_posts( $source );
$total = $total->publish;
$pt = get_post_type_object( $source );
$source_name = '';
if ( $pt !== null ) {
$source_name = $pt->labels->name;
}
foreach ( $posts as $post ) {
$items[] = esc_html( wpforms_get_post_title( $post ) );
}
} elseif ( $type === 'taxonomy' ) {
$type_name = esc_html__( 'taxonomy', 'wpforms-lite' );
$args = [
'taxonomy' => $source,
'hide_empty' => false,
'number' => 20,
];
$terms = wpforms_get_hierarchical_object(
apply_filters(
'wpforms_dynamic_choice_taxonomy_args',
$args,
[
'id' => $id,
],
$form_id
),
true
);
$total = wp_count_terms( $source );
$tax = get_taxonomy( $source );
$source_name = $tax->labels->name;
foreach ( $terms as $term ) {
$items[] = esc_html( wpforms_get_term_name( $term ) );
}
}
if ( empty( $items ) ) {
$items = [];
}
wp_send_json_success(
[
'items' => $items,
'source' => $source,
'source_name' => $source_name,
'total' => $total,
'type' => $type,
'type_name' => $type_name,
]
);
}
add_action( 'wp_ajax_wpforms_builder_dynamic_source', 'wpforms_builder_dynamic_source' );
/**
* Perform test connection to verify that the current web host can successfully
* make outbound SSL connections.
*
* @since 1.4.5
*/
function wpforms_verify_ssl() {
// Run a security check.
check_ajax_referer( 'wpforms-admin', 'nonce' );
// Check for permissions.
if ( ! wpforms_current_user_can() ) {
wp_send_json_error(
[
'msg' => esc_html__( 'You do not have permission to perform this operation.', 'wpforms-lite' ),
]
);
}
$response = wp_remote_post( 'https://wpforms.com/connection-test.php' );
if ( 200 === wp_remote_retrieve_response_code( $response ) ) {
wp_send_json_success(
[
'msg' => esc_html__( 'Success! Your server can make SSL connections.', 'wpforms-lite' ),
]
);
}
wp_send_json_error(
[
'msg' => esc_html__( 'There was an error and the connection failed. Please contact your web host with the technical details below.', 'wpforms-lite' ),
'debug' => '<pre>' . print_r( map_deep( $response, 'wp_strip_all_tags' ), true ) . '</pre>',
]
);
}
add_action( 'wp_ajax_wpforms_verify_ssl', 'wpforms_verify_ssl' );
/**
* Deactivate addon.
*
* @since 1.0.0
* @since 1.6.2.3 Updated the permissions checking.
*/
function wpforms_deactivate_addon() {
// Run a security check.
check_ajax_referer( 'wpforms-admin', 'nonce' );
// Check for permissions.
if ( ! current_user_can( 'deactivate_plugins' ) ) {
wp_send_json_error( esc_html__( 'Plugin deactivation is disabled for you on this site.', 'wpforms-lite' ) );
}
$type = empty( $_POST['type'] ) ? 'addon' : sanitize_key( $_POST['type'] );
if ( isset( $_POST['plugin'] ) ) {
$plugin = sanitize_text_field( wp_unslash( $_POST['plugin'] ) );
deactivate_plugins( $plugin );
do_action( 'wpforms_plugin_deactivated', $plugin );
if ( $type === 'plugin' ) {
wp_send_json_success( esc_html__( 'Plugin deactivated.', 'wpforms-lite' ) );
} else {
wp_send_json_success( esc_html__( 'Addon deactivated.', 'wpforms-lite' ) );
}
}
wp_send_json_error( esc_html__( 'Could not deactivate the addon. Please deactivate from the Plugins page.', 'wpforms-lite' ) );
}
add_action( 'wp_ajax_wpforms_deactivate_addon', 'wpforms_deactivate_addon' );
/**
* Activate addon.
*
* @since 1.0.0
* @since 1.6.2.3 Updated the permissions checking.
*/
function wpforms_activate_addon() {
// Run a security check.
check_ajax_referer( 'wpforms-admin', 'nonce' );
// Check for permissions.
if ( ! current_user_can( 'activate_plugins' ) ) {
wp_send_json_error( esc_html__( 'Plugin activation is disabled for you on this site.', 'wpforms-lite' ) );
}
$success_messages = [
'plugin' => __( 'Plugin activated.', 'wpforms-lite' ),
'addon' => __( 'Addon activated.', 'wpforms-lite' ),
];
$error_messages = [
'plugin' => __( 'Could not activate the plugin. Please activate it on the Plugins page.', 'wpforms-lite' ),
'addon' => __( 'Could not activate the addon. Please activate it on the Plugins page.', 'wpforms-lite' ),
];
$type = ! empty( $_POST['type'] ) ? sanitize_key( $_POST['type'] ) : 'addon';
$success_message = $success_messages[ $type ];
$error_message = $error_messages[ $type ];
if ( isset( $_POST['plugin'] ) ) {
$plugin = sanitize_text_field( wp_unslash( $_POST['plugin'] ) );
$activate = wpforms_activate_plugin( $plugin );
/**
* Fire after plugin activating via the WPForms installer.
*
* @since 1.6.3.1
*
* @param string $plugin Path to the plugin file relative to the plugins' directory.
*/
do_action( 'wpforms_plugin_activated', $plugin );
if ( $activate === null ) {
wp_send_json_success( wp_kses_post( $success_message ) );
}
$error_message = $activate->get_error_message();
}
wp_send_json_error( wp_kses_post( $error_message ) );
}
add_action( 'wp_ajax_wpforms_activate_addon', 'wpforms_activate_addon' );
/**
* Install addon.
*
* @since 1.0.0
* @since 1.6.2.3 Updated the permissions checking.
*/
function wpforms_install_addon() {
// Run a security check.
check_ajax_referer( 'wpforms-admin', 'nonce' );
$generic_error = esc_html__( 'There was an error while performing your request.', 'wpforms-lite' );
$type = ! empty( $_POST['type'] ) ? sanitize_key( $_POST['type'] ) : 'addon';
// Check if new installations are allowed.
if ( ! wpforms_can_install( $type ) ) {
wp_send_json_error( $generic_error );
}
$error = $type === 'plugin'
? esc_html__( 'Could not install the plugin. Please download and install it manually.', 'wpforms-lite' )
: sprintf(
wp_kses( /* translators: %1$s - addon download URL, %2$s - link to manual installation guide. */
__( 'Could not install the addon. Please <a href="%1$s" target="_blank" rel="noopener noreferrer">download it from wpforms.com</a> and <a href="%2$s" target="_blank" rel="noopener noreferrer">install it manually</a>.', 'wpforms-lite' ),
[
'a' => [
'href' => true,
'target' => true,
'rel' => true,
],
]
),
'https://wpforms.com/account/licenses/',
'https://wpforms.com/docs/how-to-manually-install-addons-in-wpforms/'
);
$plugin_url = ! empty( $_POST['plugin'] ) ? esc_url_raw( wp_unslash( $_POST['plugin'] ) ) : '';
if ( empty( $plugin_url ) ) {
wp_send_json_error( $error );
}
$args_str = ! empty( $_POST['args'] ) ? sanitize_text_field( wp_unslash( $_POST['args'] ) ) : '';
$args = json_decode( $args_str, true ) ?? [];
// Set the current screen to avoid undefined notices.
set_current_screen( 'wpforms_page_wpforms-settings' );
// Prepare variables.
$url = esc_url_raw(
add_query_arg(
[
'page' => 'wpforms-addons',
],
admin_url( 'admin.php' )
)
);
ob_start();
$creds = request_filesystem_credentials( $url, '', false, false, null );
// Hide the filesystem credentials form.
ob_end_clean();
// Check for file system permissions.
if ( $creds === false ) {
wp_send_json_error( $error );
}
if ( ! WP_Filesystem( $creds ) ) {
wp_send_json_error( $error );
}
/*
* We do not need any extra credentials if we have gotten this far, so let's install the plugin.
*/
require_once WPFORMS_PLUGIN_DIR . 'includes/admin/class-install-skin.php';
// Do not allow WordPress to search/download translations, as this will break JS output.
remove_action( 'upgrader_process_complete', [ 'Language_Pack_Upgrader', 'async_upgrade' ], 20 );
// Create the plugin upgrader with our custom skin.
$installer = new WPForms\Helpers\PluginSilentUpgrader( new WPForms_Install_Skin() );
// Error check.
if ( ! method_exists( $installer, 'install' ) ) {
wp_send_json_error( $error );
}
$installer->install( $plugin_url, $args );
// Flush the cache and return the newly installed plugin basename.
wp_cache_flush();
$plugin_basename = $installer->plugin_info();
if ( empty( $plugin_basename ) ) {
wp_send_json_error( $error );
}
$result = [
'msg' => $generic_error,
'is_activated' => false,
'basename' => $plugin_basename,
];
// Check for permissions.
if ( ! current_user_can( 'activate_plugins' ) ) {
$result['msg'] = $type === 'plugin' ? esc_html__( 'Plugin installed.', 'wpforms-lite' ) : esc_html__( 'Addon installed.', 'wpforms-lite' );
wp_send_json_success( $result );
}
// Activate the plugin silently.
$activated = activate_plugin( $plugin_basename );
if ( ! is_wp_error( $activated ) ) {
/**
* Fire after plugin activating via the WPForms installer.
*
* @since 1.7.0
*
* @param string $plugin_basename Path to the plugin file relative to the plugins directory.
*/
do_action( 'wpforms_plugin_activated', $plugin_basename );
$result['is_activated'] = true;
$result['msg'] = $type === 'plugin' ? esc_html__( 'Plugin installed & activated.', 'wpforms-lite' ) : esc_html__( 'Addon installed & activated.', 'wpforms-lite' );
wp_send_json_success( $result );
}
// Fallback error just in case.
wp_send_json_error( $result );
}
add_action( 'wp_ajax_wpforms_install_addon', 'wpforms_install_addon' );
/**
* Search pages for dropdown.
*
* @since 1.7.9
*/
function wpforms_ajax_search_pages_for_dropdown() {
// Run a security check.
if ( ! check_ajax_referer( 'wpforms-builder', 'nonce', false ) ) {
wp_send_json_error( esc_html__( 'Your session expired. Please reload the builder.', 'wpforms-lite' ) );
}
if ( ! array_key_exists( 'search', $_GET ) ) {
wp_send_json_error( esc_html__( 'Incorrect usage of this operation.', 'wpforms-lite' ) );
}
$result_pages = wpforms_search_pages_for_dropdown(
sanitize_text_field( wp_unslash( $_GET['search'] ) )
);
if ( empty( $result_pages ) ) {
wp_send_json_success( [] );
}
wp_send_json_success( $result_pages );
}
add_action( 'wp_ajax_wpforms_ajax_search_pages_for_dropdown', 'wpforms_ajax_search_pages_for_dropdown' );

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,663 @@
<?php
use WPForms\Admin\Education\Helpers;
/**
* Output fields to be used on panels (settings etc).
*
* @since 1.0.0
*
* @param string $option
* @param string $panel
* @param string $field
* @param array $form_data
* @param string $label
* @param array $args
* @param bool $echo
*
* @return string
*/
function wpforms_panel_field( $option, $panel, $field, $form_data, $label, $args = [], $echo = true ) {
// Required params.
if ( empty( $option ) || empty( $panel ) || empty( $field ) ) {
return '';
}
// Setup basic vars.
$panel = esc_attr( $panel );
$field = esc_attr( $field );
$panel_id = sanitize_html_class( $panel );
$parent = ! empty( $args['parent'] ) ? esc_attr( $args['parent'] ) : '';
$subsection = ! empty( $args['subsection'] ) ? esc_attr( $args['subsection'] ) : '';
$index = isset( $args['index'] ) ? esc_attr( $args['index'] ) : '';
$index = is_numeric( $index ) ? absint( $index ) : $index;
$label = ! empty( $label ) ? wp_kses( $label, [ 'span' => [ 'class' => [] ] ] ) : '';
$class = ! empty( $args['class'] ) ? wpforms_sanitize_classes( $args['class'] ) : '';
$input_class = ! empty( $args['input_class'] ) ? wpforms_sanitize_classes( $args['input_class'] ) : '';
$default = isset( $args['default'] ) ? $args['default'] : '';
$placeholder = ! empty( $args['placeholder'] ) ? esc_attr( $args['placeholder'] ) : '';
$data_attr = '';
$output = '';
$smarttags_toggle = '';
$input_id = sprintf( 'wpforms-panel-field-%s-%s', sanitize_html_class( $panel_id ), sanitize_html_class( $field ) );
if ( ! empty( $args['input_id'] ) ) {
$input_id = esc_attr( $args['input_id'] );
}
if ( ! empty( $args['smarttags'] ) ) {
$type = ! empty( $args['smarttags']['type'] ) ? esc_attr( $args['smarttags']['type'] ) : 'fields';
$fields = ! empty( $args['smarttags']['fields'] ) ? esc_attr( $args['smarttags']['fields'] ) : '';
$smarttags_toggle = sprintf(
'<a href="#" class="toggle-smart-tag-display toggle-unfoldable-cont" data-type="%s" data-fields="%s">
<i class="fa fa-tags"></i><span>%s</span>
</a>',
esc_attr( $type ),
esc_attr( $fields ),
esc_html__( 'Show Smart Tags', 'wpforms-lite' )
);
}
if ( ! empty( $args['pro_badge'] ) ) {
$label .= Helpers::get_badge( 'Pro', 'sm', 'inline', 'silver' );
}
// Check if we should store values in a parent array.
if ( ! empty( $parent ) ) {
if ( $subsection && ! wpforms_is_empty_string( $index ) ) {
$field_name = sprintf( '%s[%s][%s][%s][%s]', $parent, $panel, $subsection, $index, $field );
$value = isset( $form_data[ $parent ][ $panel ][ $subsection ][ $index ][ $field ] ) ? $form_data[ $parent ][ $panel ][ $subsection ][ $index ][ $field ] : $default;
$input_id = sprintf( 'wpforms-panel-field-%s-%s-%s-%s', sanitize_html_class( $panel_id ), sanitize_html_class( $subsection ), sanitize_html_class( $index ), sanitize_html_class( $field ) );
} elseif ( ! empty( $subsection ) ) {
$field_name = sprintf( '%s[%s][%s][%s]', $parent, $panel, $subsection, $field );
$value = isset( $form_data[ $parent ][ $panel ][ $subsection ][ $field ] ) ? $form_data[ $parent ][ $panel ][ $subsection ][ $field ] : $default;
$input_id = sprintf( 'wpforms-panel-field-%s-%s-%s', sanitize_html_class( $panel_id ), sanitize_html_class( $subsection ), sanitize_html_class( $field ) );
$panel_id = sanitize_html_class( $panel . '-' . $subsection );
} else {
$field_name = sprintf( '%s[%s][%s]', $parent, $panel, $field );
$value = isset( $form_data[ $parent ][ $panel ][ $field ] ) ? $form_data[ $parent ][ $panel ][ $field ] : $default;
}
} else {
$field_name = sprintf( '%s[%s]', $panel, $field );
$value = isset( $form_data[ $panel ][ $field ] ) ? $form_data[ $panel ][ $field ] : $default;
}
if ( isset( $args['field_name'] ) ) {
$field_name = $args['field_name'];
}
if ( isset( $args['value'] ) ) {
$value = $args['value'];
}
// Check for data attributes.
if ( ! empty( $args['data'] ) ) {
foreach ( $args['data'] as $key => $val ) {
if ( is_array( $val ) ) {
$val = wp_json_encode( $val );
}
$data_attr .= ' data-' . $key . '=\'' . $val . '\'';
}
}
// Check for readonly inputs.
if ( ! empty( $args['readonly'] ) ) {
$data_attr .= 'readonly';
}
// Determine what field type to output.
switch ( $option ) {
// Text input.
case 'text':
// Handle min and max attributes for number fields.
if ( ! empty( $args['type'] ) && $args['type'] === 'number' ) {
if ( isset( $args['min'] ) && is_int( $args['min'] ) ) {
$data_attr .= sprintf( ' min="%1$d" oninput="validity.valid||(value=\'%1$d\');" ', esc_attr( $args['min'] ) );
}
if ( isset( $args['max'] ) && is_int( $args['max'] ) ) {
$data_attr .= sprintf( ' max="%1$d" oninput="validity.valid||(value=\'%1$d\');" ', esc_attr( $args['max'] ) );
}
}
$output = sprintf(
'<input type="%s" id="%s" name="%s" value="%s" placeholder="%s" class="%s" %s>',
! empty( $args['type'] ) ? esc_attr( $args['type'] ) : 'text',
$input_id,
$field_name,
esc_attr( $value ),
$placeholder,
$input_class,
$data_attr
);
break;
// Textarea.
case 'textarea':
$output = sprintf(
'<textarea id="%s" name="%s" rows="%d" placeholder="%s" class="%s" %s>%s</textarea>',
$input_id,
$field_name,
! empty( $args['rows'] ) ? (int) $args['rows'] : '3',
$placeholder,
$input_class,
$data_attr,
esc_textarea( $value )
);
break;
// TinyMCE.
case 'tinymce':
$id = str_replace( '-', '_', $input_id );
$args['tinymce']['textarea_name'] = $field_name;
$args['tinymce']['teeny'] = true;
$args['tinymce'] = wp_parse_args(
$args['tinymce'],
[
'media_buttons' => false,
'teeny' => true,
]
);
ob_start();
wp_editor( $value, $id, $args['tinymce'] );
$output = ob_get_clean();
break;
// Checkbox.
case 'checkbox':
$output = sprintf(
'<input type="checkbox" id="%s" name="%s" value="1" class="%s" %s %s>',
$input_id,
$field_name,
$input_class,
checked( '1', $value, false ),
$data_attr
);
$output .= sprintf(
'<label for="%s" class="inline">%s',
$input_id,
$label
);
if ( ! empty( $args['before_tooltip'] ) ) {
$output .= $args['before_tooltip'];
}
if ( ! empty( $args['tooltip'] ) ) {
$output .= sprintf( '<i class="fa fa-question-circle-o wpforms-help-tooltip" title="%s"></i>', esc_attr( $args['tooltip'] ) );
}
$output .= '</label>';
break;
// Toggle.
case 'toggle':
$toggle_args = $args;
$toggle_args['input-class'] = $input_class;
$output = wpforms_panel_field_toggle_control( $toggle_args, $input_id, $field_name, $label, $value, $data_attr );
break;
// Radio.
case 'radio':
$options = $args['options'];
$radio_counter = 1;
$output = '';
foreach ( $options as $key => $item ) {
if ( empty( $item['label'] ) ) {
continue;
}
$item_value = ! empty( $item['value'] ) ? $item['value'] : $key;
$output .= '<span class="row">';
if ( ! empty( $item['pre_label'] ) ) {
$output .= '<label>' . $item['pre_label'];
}
$output .= sprintf(
'<input type="radio" id="%s-%d" name="%s" value="%s" class="%s" %s %s>',
$input_id,
$radio_counter,
$field_name,
$item_value,
$input_class,
checked( $item_value, $value, false ),
$data_attr
);
if ( empty( $item['pre_label'] ) ) {
$output .= sprintf(
'<label for="%s-%d" class="inline">%s',
$input_id,
$radio_counter,
$item['label']
);
} else {
$output .= '<span class="wpforms-panel-field-radio-label">' . $item['label'] . '</span>';
}
if ( ! empty( $item['tooltip'] ) ) {
$output .= sprintf( '<i class="fa fa-question-circle-o wpforms-help-tooltip" title="%s"></i>', esc_attr( $item['tooltip'] ) );
}
$output .= '</label></span>';
$radio_counter ++;
}
if ( ! empty( $output ) ) {
$output = '<div class="wpforms-panel-field-radio-container">' . $output . '</div>';
}
break;
// Select.
case 'select':
if ( empty( $args['options'] ) && empty( $args['field_map'] ) && empty( $args['multiple'] ) ) {
return '';
}
if ( ! empty( $args['field_map'] ) ) {
$options = [];
$available_fields = wpforms_get_form_fields( $form_data, $args['field_map'] );
if ( ! empty( $available_fields ) ) {
foreach ( $available_fields as $id => $available_field ) {
$options[ $id ] = ! empty( $available_field['label'] )
? esc_attr( $available_field['label'] )
: sprintf( /* translators: %d - field ID. */
esc_html__( 'Field #%d', 'wpforms-lite' ),
absint( $id )
);
}
}
$input_class .= ' wpforms-field-map-select';
$data_attr .= ' data-field-map-allowed="' . implode( ' ', $args['field_map'] ) . '"';
if ( ! empty( $placeholder ) ) {
$data_attr .= ' data-field-map-placeholder="' . esc_attr( $placeholder ) . '"';
}
} else {
$options = $args['options'];
}
if ( array_key_exists( 'choicesjs', $args ) && is_array( $args['choicesjs'] ) ) {
$input_class .= ' choicesjs-select';
$data_attr .= ! empty( $args['choicesjs']['use_ajax'] ) ? ' data-choicesjs-use-ajax=1' : '';
$data_attr .= ! empty( $args['choicesjs']['callback_fn'] ) ? ' data-choicesjs-callback-fn="' . esc_attr( $args['choicesjs']['callback_fn'] ) . '"' : '';
}
if ( ! empty( $args['multiple'] ) ) {
$data_attr .= ' multiple';
}
$output = sprintf(
'<select id="%s" name="%s" class="%s" %s>',
$input_id,
$field_name,
esc_attr( $input_class ),
$data_attr
);
if ( ! empty( $placeholder ) ) {
$output .= '<option value="">' . $placeholder . '</option>';
}
// This argument is used to disable some options, it takes an array of option values.
// For instance, if you want to disable options with value '1' and '2', you should pass array( '1', '2' ).
$disabled_options = ! empty( $args['disabled_options'] ) ? (array) $args['disabled_options'] : [];
foreach ( $options as $key => $item ) {
// If the option is disabled, we add the disabled attribute.
$disabled = in_array( $key, $disabled_options, true ) ? 'disabled' : '';
// Disabled options cannot be selected, so we bail early.
if ( ! empty( $disabled ) ) {
$output .= sprintf(
'<option value="%s" %s>%s</option>',
esc_attr( $key ),
$disabled,
$item
);
continue;
}
if ( is_array( $value ) ) {
$selected = in_array( $key, $value, true ) ? 'selected' : '';
} else {
$selected = selected( $key, $value, false );
}
$output .= sprintf(
'<option value="%s" %s>%s</option>',
esc_attr( $key ),
$selected,
$item
);
}
$output .= '</select>';
break;
case 'color':
$class .= ' wpforms-panel-field-colorpicker';
$input_class .= ' wpforms-color-picker';
$output = sprintf(
'<input type="text" id="%s" name="%s" value="%s" class="%s" %s>',
$input_id,
$field_name,
esc_attr( $value ),
wpforms_sanitize_classes( $input_class, false ),
$data_attr
);
break;
}
// Put the pieces together.
$field_open = sprintf(
'<div id="%s-wrap" class="wpforms-panel-field %s %s">',
$input_id,
$class,
'wpforms-panel-field-' . sanitize_html_class( $option )
);
$field_open .= ! empty( $args['before'] ) ? $args['before'] : '';
if ( $option !== 'toggle' && $option !== 'checkbox' && ! empty( $label ) ) {
$field_label = sprintf(
'<label for="%s">%s',
$input_id,
$label
);
if ( ! empty( $args['tooltip'] ) ) {
$field_label .= sprintf( '<i class="fa fa-question-circle-o wpforms-help-tooltip" title="%s"></i>', esc_attr( $args['tooltip'] ) );
}
if ( ! empty( $args['after_tooltip'] ) ) {
$field_label .= $args['after_tooltip'];
}
if ( $smarttags_toggle && ! ( $option === 'textarea' && ! empty( $args['tinymce'] ) ) ) {
$field_label .= $smarttags_toggle;
}
$field_label .= '</label>';
if ( ! empty( $args['after_label'] ) ) {
$field_label .= $args['after_label'];
}
} else {
$field_label = '';
}
$field_close = '';
if ( $smarttags_toggle && $option === 'textarea' && ! empty( $args['tinymce'] ) ) {
$field_close .= $smarttags_toggle;
}
$field_close .= ! empty( $args['after'] ) ? $args['after'] : '';
$field_close .= '</div>';
$output = $field_open . $field_label . $output . $field_close;
// Wash our hands.
if ( $echo ) {
echo $output;
} else {
return $output;
}
}
/**
* Create toggle control.
*
* It's like a regular checkbox but with a modern visual appearance.
*
* @since 1.6.8
*
* @param array $args Arguments array.
*
* @type bool $status If `true`, control will display the current status next to the toggle.
* @type string $status-on Status `On` text. By default `On`.
* @type string $status-off Status `Off` text. By default `Off`.
* @type bool $label-hide If `true` then label will not display.
* @type string $tooltip Tooltip text.
* @type string $input-class CSS class for the hidden `<input type=checkbox>`.
* @type string $control-class CSS class for the wrapper `<span>`.
*
* @param string $input_id Input ID.
* @param string $field_name Field name.
* @param string $label Label text. Can contain HTML in order to display additional badges.
* @param mixed $value Value.
* @param string $data_attr Attributes.
*
* @return string
*/
function wpforms_panel_field_toggle_control( $args, $input_id, $field_name, $label, $value, $data_attr ) {
$checked = checked( true, (bool) $value, false );
$status = '';
if ( ! empty( $args['status'] ) ) {
$status_on = ! empty( $args['status-on'] ) ? $args['status-on'] : esc_html__( 'On', 'wpforms-lite' );
$status_off = ! empty( $args['status-off'] ) ? $args['status-off'] : esc_html__( 'Off', 'wpforms-lite' );
$status = sprintf(
'<label
for="%s"
class="wpforms-toggle-control-status"
data-on="%s"
data-off="%s">
%s
</label>',
esc_attr( $input_id ),
esc_attr( $status_on ),
esc_attr( $status_off ),
esc_html( $value ? $status_on : $status_off )
);
}
$label_html = empty( $args['label-hide'] ) && ! empty( $label ) ?
sprintf(
'<label for="%s" class="wpforms-toggle-control-label">%s</label>',
esc_attr( $input_id ),
$label
) : '';
$label_html .= isset( $args['tooltip'] ) ?
sprintf(
'<i class="fa fa-question-circle-o wpforms-help-tooltip" title="%s"></i>',
esc_attr( $args['tooltip'] )
) : '';
$label_left = ! empty( $args['label-left'] ) ? $label_html . $status : '';
$label_right = empty( $args['label-left'] ) ? $status . $label_html : '';
$title = isset( $args['title'] ) ? ' title="' . esc_attr( $args['title'] ) . '"' : '';
$control_class = ! empty( $args['control-class'] ) ? $args['control-class'] : '';
$input_class = ! empty( $args['input-class'] ) ? $args['input-class'] : '';
return sprintf(
'<span class="wpforms-toggle-control %8$s" %9$s>
%1$s
<input type="checkbox" id="%2$s" name="%3$s" class="%7$s" value="1" %4$s %5$s>
<label class="wpforms-toggle-control-icon" for="%2$s"></label>
%6$s
</span>',
$label_left,
esc_attr( $input_id ),
esc_attr( $field_name ),
$checked,
$data_attr,
$label_right,
wpforms_sanitize_classes( $input_class ),
wpforms_sanitize_classes( $control_class ),
$title
);
}
/**
* Get settings block state, whether it's opened or closed.
*
* @since 1.4.8
*
* @param int $form_id
* @param int $block_id
* @param string $block_type
*
* @return string
*/
function wpforms_builder_settings_block_get_state( $form_id, $block_id, $block_type ) {
$form_id = absint( $form_id );
$block_id = absint( $block_id );
$block_type = sanitize_key( $block_type );
$state = 'opened';
$all_states = get_user_meta( get_current_user_id(), 'wpforms_builder_settings_collapsable_block_states', true );
if ( empty( $all_states ) ) {
return $state;
}
if (
is_array( $all_states ) &&
! empty( $all_states[ $form_id ][ $block_type ][ $block_id ] ) &&
'closed' === $all_states[ $form_id ][ $block_type ][ $block_id ]
) {
$state = 'closed';
}
// Backward compatibility for notifications.
if ( 'notification' === $block_type && 'closed' !== $state ) {
$notification_states = get_user_meta( get_current_user_id(), 'wpforms_builder_notification_states', true );
}
if (
! empty( $notification_states[ $form_id ][ $block_id ] ) &&
'closed' === $notification_states[ $form_id ][ $block_id ]
) {
$state = 'closed';
}
if ( 'notification' === $block_type ) {
// Backward compatibility for notifications.
return apply_filters( 'wpforms_builder_notification_get_state', $state, $form_id, $block_id );
}
return apply_filters( 'wpforms_builder_settings_block_get_state', $state, $form_id, $block_id, $block_type );
}
/**
* Get the list of allowed tags, used in pair with wp_kses() function.
* This allows getting rid of all potentially harmful HTML tags and attributes.
*
* @since 1.5.9
*
* @return array Allowed Tags.
*/
function wpforms_builder_preview_get_allowed_tags() {
static $allowed_tags;
if ( ! empty( $allowed_tags ) ) {
return $allowed_tags;
}
$atts = [ 'align', 'class', 'type', 'id', 'for', 'style', 'src', 'rel', 'href', 'target', 'value', 'width', 'height' ];
$tags = [ 'label', 'iframe', 'style', 'button', 'strong', 'small', 'table', 'span', 'abbr', 'code', 'pre', 'div', 'img', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'ol', 'ul', 'li', 'em', 'hr', 'br', 'th', 'tr', 'td', 'p', 'a', 'b', 'i' ];
$allowed_atts = array_fill_keys( $atts, [] );
$allowed_tags = array_fill_keys( $tags, $allowed_atts );
return $allowed_tags;
}
/**
* Output builder panel fields group wrapper.
*
* @since 1.6.6
*
* @param string $inner Inner HTML to wrap.
* @param array $args Array of arguments.
* @param bool $echo Flag to display.
*
* @return string
*/
function wpforms_panel_fields_group( $inner, $args = [], $echo = true ) {
$group = ! empty( $args['group'] ) ? $args['group'] : '';
$unfoldable = ! empty( $args['unfoldable'] );
$default = ( ! empty( $args['default'] ) && $args['default'] === 'opened' ) ? ' opened' : '';
$opened = ! empty( $_COOKIE[ 'wpforms_fields_group_' . $group ] ) && $_COOKIE[ 'wpforms_fields_group_' . $group ] === 'true' ? ' opened' : $default;
$class = ! empty( $args['class'] ) ? wpforms_sanitize_classes( $args['class'] ) : '';
$output = sprintf(
'<div class="wpforms-panel-fields-group %1$s%2$s"%3$s>',
$class,
$unfoldable ? ' unfoldable' . $opened : '',
$unfoldable ? ' data-group="' . $group . '"' : ''
);
if ( ! empty( $args['borders'] ) && in_array( 'top', $args['borders'], true ) ) {
$output .= '<div class="wpforms-panel-fields-group-border-top"></div>';
}
if ( ! empty( $args['title'] ) ) {
$chevron = $unfoldable ? '<i class="fa fa-chevron-circle-right"></i>' : '';
$output .= '<div class="wpforms-panel-fields-group-title">' . esc_html( $args['title'] ) . $chevron . '</div>';
}
if ( ! empty( $args['description'] ) ) {
$output .= '<div class="wpforms-panel-fields-group-description">' . wp_kses_post( $args['description'] ) . '</div>';
}
$output .= sprintf(
'<div class="wpforms-panel-fields-group-inner"%s>%s</div>',
empty( $opened ) && $unfoldable ? ' style="display: none;"' : '',
$inner
);
if ( ! empty( $args['borders'] ) && in_array( 'bottom', $args['borders'], true ) ) {
$output .= '<div class="wpforms-panel-fields-group-border-bottom"></div>';
}
$output .= '</div>';
if ( ! $echo ) {
return $output;
}
echo $output; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
/**
* Get the pages for the "Show Page" dropdown selection in Confirmations Settings in Builder.
*
* @since 1.7.9
*
* @param array $form_data Form data.
* @param int $confirmation_id Confirmation ID.
*
* @return array
*/
function wpforms_builder_form_settings_confirmation_get_pages( $form_data, $confirmation_id ) {
$pre_selected_page_id = empty( $form_data['settings']['confirmations'][ $confirmation_id ]['page'] ) ? 0 : absint( $form_data['settings']['confirmations'][ $confirmation_id ]['page'] );
$pages = wp_list_pluck( wpforms_search_posts(), 'post_title', 'ID' );
if ( empty( $pre_selected_page_id ) || isset( $pages[ $pre_selected_page_id ] ) ) {
return $pages;
}
// If the pre-selected page isn't in `$pages`, we manually fetch it include it in `$pages`.
$pre_selected_page = get_post( $pre_selected_page_id );
if ( empty( $pre_selected_page ) ) {
return $pages;
}
$pages[ $pre_selected_page->ID ] = wpforms_get_post_title( $pre_selected_page );
return $pages;
}

View File

@@ -0,0 +1,300 @@
<?php
/**
* Base panel class.
*
* @since 1.0.0
*/
abstract class WPForms_Builder_Panel {
/**
* Full name of the panel.
*
* @since 1.0.0
*
* @var string
*/
public $name;
/**
* Slug.
*
* @since 1.0.0
*
* @var string
*/
public $slug;
/**
* Font Awesome Icon used for the editor button, eg "fa-list".
*
* @since 1.0.0
*
* @var mixed
*/
public $icon = false;
/**
* Priority order the field button should show inside the "Add Fields" tab.
*
* @since 1.0.0
*
* @var int
*/
public $order = 50;
/**
* If panel contains a sidebar element or is full width.
*
* @since 1.0.0
*
* @var bool
*/
public $sidebar = false;
/**
* Contain form object if we have one.
*
* @since 1.0.0
*
* @var object
*/
public $form;
/**
* Contain array of the form data (post_content).
*
* @since 1.0.0
*
* @var array
*/
public $form_data;
/**
* Class instance.
*
* @since 1.7.7
*
* @var static
*/
private static $instance;
/**
* Primary class constructor.
*
* @since 1.0.0
*/
public function __construct() {
// Load form if found.
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
$form_id = isset( $_GET['form_id'] ) ? absint( $_GET['form_id'] ) : false;
$this->form = wpforms()->get( 'form' )->get( $form_id );
$this->form_data = $this->form ? wpforms_decode( $this->form->post_content ) : false;
// Get current revision, if available.
$revision = wpforms()->get( 'revisions' )->get_revision();
// If we're viewing a valid revision, replace the form data so the Form Builder shows correct state.
if ( $revision && isset( $revision->post_content ) ) {
$this->form_data = wpforms_decode( $revision->post_content );
}
// Bootstrap.
$this->init();
// Load panel specific enqueues.
add_action( 'admin_enqueue_scripts', [ $this, 'enqueues' ], 15 );
// Primary panel button.
add_action( 'wpforms_builder_panel_buttons', [ $this, 'button' ], $this->order, 2 );
// Output.
add_action( 'wpforms_builder_panels', [ $this, 'panel_output' ], $this->order, 2 );
// Save instance.
self::$instance = $this;
}
/**
* Get class instance.
*
* @since 1.7.7
*
* @return static
*/
public static function instance() {
if ( self::$instance === null || ! self::$instance instanceof static ) {
self::$instance = new static();
}
return self::$instance;
}
/**
* All systems go. Used by children.
*
* @since 1.0.0
*/
public function init() {
}
/**
* Enqueue assets for the builder. Used by children.
*
* @since 1.0.0
*/
public function enqueues() {
}
/**
* Primary panel button in the left panel navigation.
*
* @since 1.0.0
*
* @param mixed $form
* @param string $view
*/
public function button( $form, $view ) {
$active = $view === $this->slug ? 'active' : '';
?>
<button class="wpforms-panel-<?php echo esc_attr( $this->slug ); ?>-button <?php echo $active; ?>" data-panel="<?php echo esc_attr( $this->slug ); ?>">
<i class="fa <?php echo esc_attr( $this->icon ); ?>"></i>
<span><?php echo esc_html( $this->name ); ?></span>
</button>
<?php
}
/**
* Output the contents of the panel.
*
* @since 1.0.0
*
* @param object $form Current form object.
* @param string $view Active Form Builder view (panel).
*/
public function panel_output( $form, $view ) {
$wrap = $this->sidebar ? 'wpforms-panel-sidebar-content' : 'wpforms-panel-full-content';
$classes = [ 'wpforms-panel' ];
if ( in_array( $this->slug, [ 'fields', 'revisions' ], true ) ) {
$classes[] = 'wpforms-panel-fields';
}
if ( $view === $this->slug ) {
$classes[] = 'active';
}
printf( '<div class="%s" id="wpforms-panel-%s">', wpforms_sanitize_classes( $classes, true ), esc_attr( $this->slug ) );
printf( '<div class="%s">', $wrap );
if ( true === $this->sidebar ) {
if ( $this->slug === 'fields' ) {
echo '<div class="wpforms-panel-sidebar-toggle"><div class="wpforms-panel-sidebar-toggle-vertical-line"></div><div class="wpforms-panel-sidebar-toggle-icon"><i class="fa fa-angle-left"></i></div></div>';
}
echo '<div class="wpforms-panel-sidebar">';
do_action( 'wpforms_builder_before_panel_sidebar', $this->form, $this->slug );
$this->panel_sidebar();
do_action( 'wpforms_builder_after_panel_sidebar', $this->form, $this->slug );
echo '</div>';
}
echo '<div class="wpforms-panel-content-wrap">';
echo '<div class="wpforms-panel-content">';
do_action( 'wpforms_builder_before_panel_content', $this->form, $this->slug );
$this->panel_content();
do_action( 'wpforms_builder_after_panel_content', $this->form, $this->slug );
echo '</div>';
echo '</div>';
echo '</div>';
echo '</div>';
}
/**
* Output the panel's sidebar if we have one.
*
* @since 1.0.0
*/
public function panel_sidebar() {
}
/**
* Output panel sidebar sections.
*
* @since 1.0.0
*
* @param string $name Sidebar section name.
* @param string $slug Sidebar section slug.
* @param string $icon Sidebar section icon.
*/
public function panel_sidebar_section( $name, $slug, $icon = '' ) {
$default_classes = [
'wpforms-panel-sidebar-section',
'wpforms-panel-sidebar-section-' . $slug,
];
if ( $slug === 'default' ) {
$default_classes[] = 'default';
}
if ( ! empty( $icon ) ) {
$default_classes[] = 'icon';
}
/**
* Allow adding custom CSS classes to a sidebar section in the Form Builder.
*
* @since 1.7.7.2
*
* @param array $classes Sidebar section classes.
* @param string $name Sidebar section name.
* @param string $slug Sidebar section slug.
* @param string $icon Sidebar section icon.
*/
$classes = (array) apply_filters( 'wpforms_builder_panel_sidebar_section_classes', [], $name, $slug, $icon );
$classes = array_merge( $default_classes, $classes );
echo '<a href="#" class="' . wpforms_sanitize_classes( $classes, true ) . '" data-section="' . esc_attr( $slug ) . '">';
if ( ! empty( $icon ) ) {
echo '<img src="' . esc_url( $icon ) . '">';
}
echo esc_html( $name );
echo '<i class="fa fa-angle-right wpforms-toggle-arrow"></i>';
echo '</a>';
}
/**
* Output the panel's primary content.
*
* @since 1.0.0
*/
public function panel_content() {
}
}

View File

@@ -0,0 +1,716 @@
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Fields management panel.
*
* @since 1.0.0
*/
class WPForms_Builder_Panel_Fields extends WPForms_Builder_Panel {
/**
* All systems go.
*
* @since 1.0.0
*/
public function init() {
// Define panel information.
$this->name = esc_html__( 'Fields', 'wpforms-lite' );
$this->slug = 'fields';
$this->icon = 'fa-list-alt';
$this->order = 10;
$this->sidebar = true;
if ( $this->form ) {
add_action( 'wpforms_builder_fields', [ $this, 'search' ], 5 );
add_action( 'wpforms_builder_fields', [ $this, 'fields' ] );
add_action( 'wpforms_builder_fields_options', [ $this, 'fields_options' ] );
add_action( 'wpforms_builder_preview', [ $this, 'preview' ] );
// Template for form builder previews.
add_action( 'wpforms_builder_print_footer_scripts', [ $this, 'field_preview_templates' ] );
add_action( 'wpforms_builder_print_footer_scripts', [ $this, 'choices_limit_message_template' ] );
add_action( 'wpforms_builder_print_footer_scripts', [ $this, 'choices_empty_message_template' ] );
}
}
/**
* Enqueue assets for the Fields panel.
*
* @since 1.0.0
* @since 1.6.8 All the builder stylesheets enqueues moved to the `\WPForms_Builder::enqueues()`.
*/
public function enqueues() {
$min = wpforms_get_min_suffix();
wp_enqueue_script(
'wpforms-builder-drag-fields',
WPFORMS_PLUGIN_URL . "assets/js/components/admin/builder/drag-fields{$min}.js",
[ 'wpforms-builder' ],
WPFORMS_VERSION,
true
);
wp_enqueue_script(
'wpforms-builder-search-fields',
WPFORMS_PLUGIN_URL . "assets/js/components/admin/builder/search-fields{$min}.js",
[ 'wpforms-builder' ],
WPFORMS_VERSION,
true
);
}
/**
* Output the Field panel sidebar.
*
* @since 1.0.0
*/
public function panel_sidebar() {
// Sidebar contents are not valid unless we have a form.
if ( ! $this->form ) {
return;
}
?>
<ul class="wpforms-tabs wpforms-clear">
<li class="wpforms-tab" id="add-fields">
<a href="#" class="active">
<i class="fa fa-list-alt"></i><?php esc_html_e( 'Add Fields', 'wpforms-lite' ); ?>
</a>
</li>
<li class="wpforms-tab" id="field-options">
<a href="#">
<i class="fa fa-sliders"></i><?php esc_html_e( 'Field Options', 'wpforms-lite' ); ?>
</a>
</li>
</ul>
<div class="wpforms-add-fields wpforms-tab-content">
<?php do_action( 'wpforms_builder_fields', $this->form ); ?>
</div>
<div id="wpforms-field-options" class="wpforms-field-options wpforms-tab-content">
<?php do_action( 'wpforms_builder_fields_options', $this->form ); ?>
</div>
<?php
}
/**
* Output the Field panel primary content.
*
* @since 1.0.0
*/
public function panel_content() {
// Check if there is a form created.
if ( ! $this->form ) {
echo '<div class="wpforms-alert wpforms-alert-info">';
echo wp_kses(
__( 'You need to <a href="#" class="wpforms-panel-switch" data-panel="setup">setup your form</a> before you can manage the fields.', 'wpforms-lite' ),
[
'a' => [
'href' => [],
'class' => [],
'data-panel' => [],
],
]
);
echo '</div>';
return;
}
?>
<div class="wpforms-preview-wrap">
<div class="wpforms-preview">
<div class="wpforms-title-desc">
<div class="wpforms-title-desc-inner">
<h2 class="wpforms-form-name">
<?php echo esc_html( isset( $this->form_data['settings']['form_title'] ) ? $this->form_data['settings']['form_title'] : $this->form->post_title ); ?>
</h2>
<span class="wpforms-form-desc">
<?php
echo wp_kses(
isset( $this->form_data['settings']['form_desc'] ) ? $this->form_data['settings']['form_desc'] : $this->form->post_excerpt,
wpforms_builder_preview_get_allowed_tags()
);
?>
</span>
</div>
</div>
<div class="wpforms-no-fields-holder wpforms-hidden">
<?php $this->no_fields_options(); ?>
<?php $this->no_fields_preview(); ?>
</div>
<div class="wpforms-field-wrap">
<?php do_action( 'wpforms_builder_preview', $this->form ); ?>
</div>
<?php
$captcha_settings = wpforms_get_captcha_settings();
$extra_class = 'is-' . $captcha_settings['provider'];
?>
<div class="wpforms-field-recaptcha <?php echo sanitize_html_class( $extra_class ); ?>">
<div class="wpforms-field-recaptcha-wrap">
<div class="wpforms-field-recaptcha-wrap-l">
<svg class="wpforms-field-hcaptcha-icon" fill="none" viewBox="0 0 83 90"><path opacity=".5" d="M60.012 69.998H50.01V80h10.002V69.998z" fill="#0074BF"/><path opacity=".7" d="M50.01 69.998H40.008V80H50.01V69.998zM40.008 69.998H30.006V80h10.002V69.998z" fill="#0074BF"/><path opacity=".5" d="M30.006 69.998H20.004V80h10.002V69.998z" fill="#0074BF"/><path opacity=".7" d="M70.014 60.013H60.014v10.002h10.002V60.012z" fill="#0082BF"/><path opacity=".8" d="M60.012 60.013H50.01v10.002h10.002V60.012z" fill="#0082BF"/><path d="M50.01 60.013H40.008v10.002H50.01V60.012zM40.008 60.013H30.006v10.002h10.002V60.012z" fill="#0082BF"/><path opacity=".8" d="M30.006 60.013H20.004v10.002h10.002V60.012z" fill="#0082BF"/><path opacity=".7" d="M20.004 60.013H10.002v10.002h10.002V60.012z" fill="#0082BF"/><path opacity=".5" d="M80 50.01H69.998v10.002H80V50.01z" fill="#008FBF"/><path opacity=".8" d="M70.014 50.01H60.014v10.002h10.002V50.01z" fill="#008FBF"/><path d="M60.012 50.01H50.01v10.002h10.002V50.01zM50.01 50.01H40.008v10.002H50.01V50.01zM40.008 50.01H30.006v10.002h10.002V50.01zM30.006 50.01H20.004v10.002h10.002V50.01z" fill="#008FBF"/><path opacity=".8" d="M20.004 50.01H10.002v10.002h10.002V50.01z" fill="#008FBF"/><path opacity=".5" d="M10.002 50.01H0v10.002h10.002V50.01z" fill="#008FBF"/><path opacity=".7" d="M80 40.008H69.998V50.01H80V40.008z" fill="#009DBF"/><path d="M70.014 40.008H60.014V50.01h10.002V40.008zM60.012 40.008H50.01V50.01h10.002V40.008zM50.01 40.008H40.008V50.01H50.01V40.008zM40.008 40.008H30.006V50.01h10.002V40.008zM30.006 40.008H20.004V50.01h10.002V40.008zM20.004 40.008H10.002V50.01h10.002V40.008z" fill="#009DBF"/><path opacity=".7" d="M10.002 40.008H0V50.01h10.002V40.008z" fill="#009DBF"/><path opacity=".7" d="M80 30.006H69.998v10.002H80V30.006z" fill="#00ABBF"/><path d="M70.014 30.006H60.014v10.002h10.002V30.006zM60.012 30.006H50.01v10.002h10.002V30.006zM50.01 30.006H40.008v10.002H50.01V30.006zM40.008 30.006H30.006v10.002h10.002V30.006zM30.006 30.006H20.004v10.002h10.002V30.006zM20.004 30.006H10.002v10.002h10.002V30.006z" fill="#00ABBF"/><path opacity=".7" d="M10.002 30.006H0v10.002h10.002V30.006z" fill="#00ABBF"/><path opacity=".5" d="M80 20.004H69.998v10.002H80V20.004z" fill="#00B9BF"/><path opacity=".8" d="M70.014 20.004H60.014v10.002h10.002V20.004z" fill="#00B9BF"/><path d="M60.012 20.004H50.01v10.002h10.002V20.004zM50.01 20.004H40.008v10.002H50.01V20.004zM40.008 20.004H30.006v10.002h10.002V20.004zM30.006 20.004H20.004v10.002h10.002V20.004z" fill="#00B9BF"/><path opacity=".8" d="M20.004 20.004H10.002v10.002h10.002V20.004z" fill="#00B9BF"/><path opacity=".5" d="M10.002 20.004H0v10.002h10.002V20.004z" fill="#00B9BF"/><path opacity=".7" d="M70.014 10.002H60.014v10.002h10.002V10.002z" fill="#00C6BF"/><path opacity=".8" d="M60.012 10.002H50.01v10.002h10.002V10.002z" fill="#00C6BF"/><path d="M50.01 10.002H40.008v10.002H50.01V10.002zM40.008 10.002H30.006v10.002h10.002V10.002z" fill="#00C6BF"/><path opacity=".8" d="M30.006 10.002H20.004v10.002h10.002V10.002z" fill="#00C6BF"/><path opacity=".7" d="M20.004 10.002H10.002v10.002h10.002V10.002z" fill="#00C6BF"/><path opacity=".5" d="M60.012 0H50.01v10.002h10.002V0z" fill="#00D4BF"/><path opacity=".7" d="M50.01 0H40.008v10.002H50.01V0zM40.008 0H30.006v10.002h10.002V0z" fill="#00D4BF"/><path opacity=".5" d="M30.006 0H20.004v10.002h10.002V0z" fill="#00D4BF"/><path d="M26.34 36.84l2.787-6.237c1.012-1.592.88-3.55-.232-4.66a3.6 3.6 0 00-.481-.399 3.053 3.053 0 00-2.571-.298 4.246 4.246 0 00-2.322 1.791s-3.816 8.907-5.242 12.905c-1.426 3.998-.863 11.346 4.611 16.836 5.806 5.806 14.215 7.132 19.573 3.102.232-.116.431-.25.63-.415l16.521-13.8c.797-.664 1.99-2.024.93-3.583-1.046-1.526-3.003-.481-3.816.033l-9.504 6.917a.421.421 0 01-.597-.05s0-.017-.017-.017c-.249-.298-.282-1.078.1-1.393l14.58-12.374c1.26-1.128 1.426-2.787.414-3.915-.995-1.11-2.57-1.078-3.848.067l-13.12 10.267a.578.578 0 01-.813-.083c0-.016-.017-.016-.017-.033-.265-.298-.365-.78-.066-1.078l14.862-14.414c1.178-1.095 1.244-2.936.15-4.097a2.824 2.824 0 00-2.024-.863 2.905 2.905 0 00-2.09.83L39.544 36.144c-.365.364-1.078 0-1.161-.432a.474.474 0 01.132-.431l11.628-13.237a2.86 2.86 0 00.15-4.047 2.86 2.86 0 00-4.048-.15c-.05.05-.1.084-.133.133L28.447 37.47c-.63.63-1.56.664-2.007.299a.657.657 0 01-.1-.929z" fill="#fff"/></svg>
<svg class="wpforms-field-recaptcha-icon" viewBox="0 0 28 27.918"><path d="M28 13.943l-.016-.607V2l-3.133 3.134a13.983 13.983 0 00-21.964.394l5.134 5.183a6.766 6.766 0 012.083-2.329A6.171 6.171 0 0114.025 7.1a1.778 1.778 0 01.492.066 6.719 6.719 0 015.17 3.119l-3.625 3.641 11.941.016" fill="#1c3aa9"/><path d="M13.943 0l-.607.016H2.018l3.133 3.133a13.969 13.969 0 00.377 21.964l5.183-5.134A6.766 6.766 0 018.382 17.9 6.171 6.171 0 017.1 13.975a1.778 1.778 0 01.066-.492 6.719 6.719 0 013.117-5.167l3.641 3.641L13.943 0" fill="#4285f4"/><path d="M0 13.975l.016.607v11.334l3.133-3.133a13.983 13.983 0 0021.964-.394l-5.134-5.183a6.766 6.766 0 01-2.079 2.33 6.171 6.171 0 01-3.92 1.279 1.778 1.778 0 01-.492-.066 6.719 6.719 0 01-5.167-3.117l3.641-3.641c-4.626 0-9.825.016-11.958-.016" fill="#ababab"/></svg>
<svg class="wpforms-field-turnstile-icon" fill="none" viewBox="0 0 106 106"> <g clip-path="url(#a)"> <path fill="#F4801F" d="m72.375 76.265.541-1.877c.643-2.231.405-4.29-.678-5.808-1.011-1.397-2.66-2.216-4.68-2.312l-38.213-.486a.743.743 0 0 1-.683-1.012 1.012 1.012 0 0 1 .885-.678l38.583-.506c4.554-.207 9.532-3.92 11.267-8.454l2.196-5.748a1.354 1.354 0 0 0 .061-.779 25.13 25.13 0 0 0-48.312-2.6 11.307 11.307 0 0 0-17.708 11.849A16.054 16.054 0 0 0 .172 76.28a.744.744 0 0 0 .734.643H71.48a.927.927 0 0 0 .895-.658Z"/> <path fill="#F9AB41" d="M85.11 49.82c-.338 0-.692.01-1.063.03a.444.444 0 0 0-.162.035.59.59 0 0 0-.384.405l-1.518 5.191c-.648 2.231-.41 4.29.678 5.808a5.895 5.895 0 0 0 4.675 2.313l8.15.505a.728.728 0 0 1 .577.314.759.759 0 0 1 .086.693 1.012 1.012 0 0 1-.885.678l-8.465.506c-4.599.213-9.552 3.921-11.287 8.45l-.612 1.598a.455.455 0 0 0 .4.617h29.157a.782.782 0 0 0 .779-.592 20.92 20.92 0 0 0-10.822-24.36 20.916 20.916 0 0 0-9.294-2.191h-.01Z"/> </g> <defs> <clipPath id="a"> <path fill="#fff" d="M0 0h106v106H0z"/> </clipPath> </defs> </svg>
</div>
<div class="wpforms-field-recaptcha-wrap-r">
<p class="wpforms-field-hcaptcha-title">hCaptcha</p>
<p class="wpforms-field-recaptcha-title">reCAPTCHA</p>
<p class="wpforms-field-turnstile-title">Turnstile</p>
<p class="wpforms-field-recaptcha-desc">
<span class="wpforms-field-recaptcha-desc-txt"><?php esc_html_e( 'Enabled', 'wpforms-lite' ); ?></span><svg class="wpforms-field-recaptcha-desc-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M512 256c0-37.7-23.7-69.9-57.1-82.4 14.7-32.4 8.8-71.9-17.9-98.6-26.7-26.7-66.2-32.6-98.6-17.9C325.9 23.7 293.7 0 256 0s-69.9 23.7-82.4 57.1c-32.4-14.7-72-8.8-98.6 17.9-26.7 26.7-32.6 66.2-17.9 98.6C23.7 186.1 0 218.3 0 256s23.7 69.9 57.1 82.4c-14.7 32.4-8.8 72 17.9 98.6 26.6 26.6 66.1 32.7 98.6 17.9 12.5 33.3 44.7 57.1 82.4 57.1s69.9-23.7 82.4-57.1c32.6 14.8 72 8.7 98.6-17.9 26.7-26.7 32.6-66.2 17.9-98.6 33.4-12.5 57.1-44.7 57.1-82.4zm-144.8-44.25L236.16 341.74c-4.31 4.28-11.28 4.25-15.55-.06l-75.72-76.33c-4.28-4.31-4.25-11.28.06-15.56l26.03-25.82c4.31-4.28 11.28-4.25 15.56.06l42.15 42.49 97.2-96.42c4.31-4.28 11.28-4.25 15.55.06l25.82 26.03c4.28 4.32 4.26 11.29-.06 15.56z"></path></svg>
</p>
</div>
</div>
</div>
<?php
$submit = ! empty( $this->form_data['settings']['submit_text'] ) ? $this->form_data['settings']['submit_text'] : esc_html__( 'Submit', 'wpforms-lite' );
$submit_style = empty( $this->form_data['fields'] ) ? 'display: none;' : '';
printf( '<p class="wpforms-field-submit" style="%1$s"><input type="submit" value="%2$s" class="wpforms-field-submit-button"></p>', esc_attr( $submit_style ), esc_attr( $submit ) );
/** This action is documented in includes/class-frontend.php. */
do_action( 'wpforms_display_submit_after', $this->form_data, 'submit' ); // phpcs:ignore WPForms.PHP.ValidateHooks.InvalidHookName
?>
<?php wpforms_debug_data( $this->form_data ); ?>
</div>
</div>
<?php
}
/**
* Builder field buttons.
*
* @since 1.0.0
*/
public function fields() {
$fields = wpforms_get_builder_fields();
// Output the buttons.
foreach ( $fields as $id => $group ) {
usort( $group['fields'], [ $this, 'field_order' ] );
echo '<div class="wpforms-add-fields-group">';
echo '<a href="#" class="wpforms-add-fields-heading" data-group="' . esc_attr( $id ) . '">';
echo '<span>' . esc_html( $group['group_name'] ) . '</span>';
echo '<i class="fa fa-angle-down"></i>';
echo '</a>';
echo '<div class="wpforms-add-fields-buttons">';
foreach ( $group['fields'] as $field ) {
/**
* Attributes of the form field button on the Add Fields tab in the Form Builder.
*
* @since 1.5.1
*
* @param array $attributes Field attributes.
* @param array $field Field data.
* @param array $form_data Form data.
*/
$atts = apply_filters(
'wpforms_builder_field_button_attributes',
[
'id' => 'wpforms-add-fields-' . $field['type'],
'class' => [ 'wpforms-add-fields-button' ],
'data' => [
'field-type' => $field['type'],
],
'atts' => [],
],
$field,
$this->form_data
);
if ( ! empty( $field['keywords'] ) ) {
$atts['data']['field-keywords'] = $field['keywords'];
}
if ( ! empty( $field['class'] ) ) {
$atts['class'][] = $field['class'];
}
echo '<button ' . wpforms_html_attributes( $atts['id'], $atts['class'], $atts['data'], $atts['atts'] ) . '>';
if ( $field['icon'] ) {
echo '<i class="fa ' . esc_attr( $field['icon'] ) . '"></i> ';
}
echo esc_html( $field['name'] );
echo '</button>';
}
echo '</div>';
echo '</div>';
}
}
/**
* Editor Field Options.
*
* @since 1.0.0
*/
public function fields_options() {
// Check to make sure the form actually has fields created already.
if ( empty( $this->form_data['fields'] ) ) {
$this->no_fields_options();
return;
}
$fields = $this->form_data['fields'];
foreach ( $fields as $field ) {
$class = apply_filters( 'wpforms_builder_field_option_class', '', $field );
printf( '<div class="wpforms-field-option wpforms-field-option-%s %s" id="wpforms-field-option-%d" data-field-id="%d">', sanitize_html_class( $field['type'] ), wpforms_sanitize_classes( $class ), (int) $field['id'], (int) $field['id'] );
printf( '<input type="hidden" name="fields[%d][id]" value="%d" class="wpforms-field-option-hidden-id">', (int) $field['id'], (int) $field['id'] );
printf( '<input type="hidden" name="fields[%d][type]" value="%s" class="wpforms-field-option-hidden-type">', (int) $field['id'], esc_attr( $field['type'] ) );
do_action( "wpforms_builder_fields_options_{$field['type']}", $field );
echo '</div>';
}
}
/**
* Editor preview (right pane).
*
* @since 1.0.0
*/
public function preview() {
// Check to make sure the form actually has fields created already.
if ( empty( $this->form_data['fields'] ) ) {
$this->no_fields_preview();
return;
}
/**
* Filters the fields which must be displayed on the base level on the preview panel in the Form Builder.
*
* @since 1.7.7
*
* @param array $fields Form fields data.
*/
$fields = (array) apply_filters( 'wpforms_builder_panel_fields_preview_fields', $this->form_data['fields'] );
foreach ( $fields as $field ) {
$this->preview_single_field(
$field,
[]
);
}
}
/**
* Preview single field.
*
* @since 1.7.7
*
* @param array $field Field data.
* @param array $args Additional arguments.
*/
public function preview_single_field( $field, $args ) {
$class = ! empty( $field['size'] ) ? 'size-' . esc_attr( $field['size'] ) : '';
$class .= ! empty( $field['label_hide'] ) ? ' label_hide' : '';
$class .= isset( $field['label'] ) && empty( $field['label'] ) && $field['type'] !== 'html' ? ' label_empty' : '';
$class .= ! empty( $field['sublabel_hide'] ) ? ' sublabel_hide' : '';
$class .= ! empty( $field['required'] ) ? ' required' : '';
$class .= isset( $field['meta']['delete'] ) && $field['meta']['delete'] === false ? ' no-delete' : '';
$class .= isset( $field['meta']['duplicate'] ) && $field['meta']['duplicate'] === false ? ' no-duplicate' : '';
if ( ! empty( $field['input_columns'] ) ) {
$class .= $field['input_columns'] === '2' ? ' wpforms-list-2-columns' : '';
$class .= $field['input_columns'] === '3' ? ' wpforms-list-3-columns' : '';
$class .= $field['input_columns'] === 'inline' ? ' wpforms-list-inline' : '';
}
/**
* Filters class attribute of the field preview container in the Form Builder.
*
* @since 1.4.0
*
* @param string $css Field preview class.
* @param array $field Field data.
*/
$class = apply_filters( 'wpforms_field_preview_class', $class, $field ); // phpcs:ignore WPForms.PHP.ValidateHooks.InvalidHookName
if ( ! has_action( "wpforms_display_field_{$field['type']}" ) ) {
$this->unavailable_fields_preview( $field );
return;
}
printf(
'<div class="wpforms-field wpforms-field-%1$s %2$s" id="wpforms-field-%3$d" data-field-id="%3$d" data-field-type="%1$s">',
esc_attr( $field['type'] ),
esc_attr( $class ),
absint( $field['id'] )
);
/**
* Filters display field duplicate button flag.
*
* @since 1.5.6.2
*
* @param bool $display_duplicate_button Display field duplicate button flag.
* @param array $field Field data.
* @param array $form_data Form data.
*/
if ( apply_filters( 'wpforms_field_preview_display_duplicate_button', true, $field, $this->form_data ) ) { // phpcs:ignore WPForms.PHP.ValidateHooks.InvalidHookName
printf(
'<a href="#" class="wpforms-field-duplicate" title="%s"><i class="fa fa-files-o" aria-hidden="true"></i></a>',
esc_attr__( 'Duplicate Field', 'wpforms-lite' )
);
}
printf(
'<a href="#" class="wpforms-field-delete" title="%s"><i class="fa fa-trash-o" aria-hidden="true"></i></a>',
esc_attr__( 'Delete Field', 'wpforms-lite' )
);
if ( empty( $_COOKIE['wpforms_field_helper_hide'] ) ) {
printf(
'<div class="wpforms-field-helper">
<span class="wpforms-field-helper-edit">%s</span>
<span class="wpforms-field-helper-drag">%s</span>
<span class="wpforms-field-helper-hide" title="%s">
<i class="fa fa-times-circle" aria-hidden="true"></i>
</span>
</div>',
esc_html__( 'Click to Edit', 'wpforms-lite' ),
esc_html__( 'Drag to Reorder', 'wpforms-lite' ),
esc_attr__( 'Hide Helper', 'wpforms-lite' )
);
}
/**
* Fires after the field preview output in the Form Builder.
*
* @since 1.0.0
*
* @param array $field Field data.
*/
do_action( "wpforms_builder_fields_previews_{$field['type']}", $field ); // phpcs:ignore WPForms.PHP.ValidateHooks.InvalidHookName
echo '</div>';
}
/**
* Generate HTML for hidden inputs from given data.
*
* @since 1.6.7
*
* @param array $data Field array data.
* @param string $name Input name prefix.
*/
private function generate_hidden_inputs( $data = [], $name = '' ) {
if ( ! is_array( $data ) || empty( $data ) ) {
return;
}
foreach ( $data as $key => $value ) {
if ( $key === 'id' ) {
continue;
}
$key = ! empty( $data['id'] ) ? sprintf( '[%s][%s]', $data['id'], $key ) : sprintf( '[%s]', $key );
if ( ! empty( $name ) ) {
$key = trim( $name ) . $key;
}
if ( is_array( $value ) ) {
$this->generate_hidden_inputs( $value, $key );
} else {
printf( "<input type='hidden' name='%s' value='%s' />", esc_attr( $key ), esc_attr( $value ) );
}
}
}
/**
* Unavailable builder field display.
*
* @since 1.6.7
*
* @param array $field Field array data.
*/
public function unavailable_fields_preview( $field ) {
// Using ucwords() for certain fields may generate incorrect words.
switch ( $field['type'] ) {
case 'url':
$field_type = 'URL';
break;
case 'html':
$field_type = 'HTML';
break;
case 'gdpr-checkbox':
$field_type = 'GDPR Checkbox';
break;
default:
$field_type = ucwords( preg_replace( '/[_-]/', ' ', $field['type'] ) );
}
$warning_message = sprintf( /* translators: %s - unavailable field name. */
esc_html__( 'Unfortunately, the %s field is not available and will be ignored on the front end.', 'wpforms-lite' ),
'<b>' . $field_type . '</b>'
);
$field_id = isset( $field['id'] ) ? $field['id'] : 0;
printf(
'<div class="wpforms-alert wpforms-alert-warning wpforms-alert-dismissible wpforms-alert-field-not-available" data-field-id="%d" data-field-type="unavailable">',
absint( $field_id )
);
printf(
'<div class="wpforms-alert-message">
<p>%1$s</p>
</div>
<div class="wpforms-alert-buttons">
<a href="%2$s" target="_blank" rel="noopener noreferrer" class="wpforms-btn wpforms-btn-md wpforms-btn-light-grey">%3$s</a>
<button type="button" class="wpforms-dismiss-button" title="%4$s" data-field-id="%5$d" />
</div>',
$warning_message, // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
'https://wpforms.com/docs/how-to-import-and-export-wpforms/#field-missing',
esc_html__( 'Learn More', 'wpforms-lite' ),
esc_attr__( 'Dismiss this message. The field will be deleted as well.', 'wpforms-lite' ),
absint( $field_id )
);
// Save unavailable fields data in hidden inputs.
$this->generate_hidden_inputs( $field, 'fields' );
echo '</div>';
}
/**
* No fields options markup.
*
* @since 1.6.0
*/
public function no_fields_options() {
printf(
'<p class="no-fields wpforms-alert wpforms-alert-warning">%s</p>',
esc_html__( 'You don\'t have any fields yet.', 'wpforms-lite' )
);
}
/**
* No fields preview placeholder markup.
*
* @since 1.6.0
*/
public function no_fields_preview() {
printf(
'<div class="no-fields-preview">
<h4>%1$s</h4>
<p>%2$s</p>
</div>',
esc_html__( 'You don\'t have any fields yet. Add some!', 'wpforms-lite' ),
esc_html__( 'Take your pick from our wide variety of fields and start building out your form!', 'wpforms-lite' )
);
}
/**
* Sort Add Field buttons by order provided.
*
* @since 1.0.0
*
* @param array $a First item.
* @param array $b Second item.
*
* @return array
*/
public function field_order( $a, $b ) {
return $a['order'] - $b['order'];
}
/**
* Template for form builder preview.
*
* @since 1.4.5
*/
public function field_preview_templates() {
// Checkbox, Radio, and Payment Multiple/Checkbox field choices.
?>
<script type="text/html" id="tmpl-wpforms-field-preview-checkbox-radio-payment-multiple">
<# if ( data.settings.choices_images ) { #>
<ul class="primary-input wpforms-image-choices wpforms-image-choices-{{ data.settings.choices_images_style }}">
<# _.each( data.order, function( choiceID, key ) { #>
<li class="wpforms-image-choices-item<# if ( 1 === data.settings.choices[choiceID].default ) { print( ' wpforms-selected' ); } #>">
<label>
<span class="wpforms-image-choices-image">
<# if ( ! _.isEmpty( data.settings.choices[choiceID].image ) ) { #>
<img src="{{ data.settings.choices[choiceID].image }}" alt="{{ data.settings.choices[choiceID].label }}" title="{{ data.settings.choices[choiceID].label }}">
<# } else { #>
<img src="{{ wpforms_builder.image_placeholder }}" alt="{{ data.settings.choices[choiceID].label }}" title="{{ data.settings.choices[choiceID].label }}">
<# } #>
</span>
<# if ( 'none' === data.settings.choices_images_style ) { #>
<br>
<input type="{{ data.type }}" readonly<# if ( 1 === data.settings.choices[choiceID].default ) { print( ' checked' ); } #>>
<# } else { #>
<input class="wpforms-screen-reader-element" type="{{ data.type }}" readonly<# if ( 1 === data.settings.choices[choiceID].default ) { print( ' checked' ); } #>>
<# } #>
<span class="wpforms-image-choices-label">
{{{ WPFormsBuilder.fieldChoiceLabel( data, choiceID ) }}}
</span>
</label>
</li>
<# }) #>
</ul>
<# } else if ( data.settings.choices_icons ) { #>
<ul class='primary-input wpforms-icon-choices wpforms-icon-choices-{{ data.settings.choices_icons_style }} wpforms-icon-choices-{{ data.settings.choices_icons_size }}' style="--wpforms-icon-choices-color: {{ data.settings.choices_icons_color }};">
<# _.each( data.order, function( choiceID, key ) { #>
<li class="wpforms-icon-choices-item<# if ( 1 === data.settings.choices[choiceID].default ) { print( ' wpforms-selected' ); } #>">
<label>
<span class="wpforms-icon-choices-icon">
<i class="ic-fa-{{ data.settings.choices[choiceID].icon_style }} ic-fa-{{ data.settings.choices[choiceID].icon }}"></i>
<span class="wpforms-icon-choices-icon-bg"></span>
</span>
<# if ( 'none' === data.settings.choices_icons_style ) { #>
<input type='{{ data.type }}' readonly<# if ( 1 === data.settings.choices[choiceID].default ) { print( ' checked' ); } #>>
<# } else { #>
<input class='wpforms-screen-reader-element' type='{{ data.type }}' readonly<# if ( 1 === data.settings.choices[choiceID].default ) { print( ' checked' ); } #>>
<# } #>
<span class='wpforms-icon-choices-label'>
{{{ WPFormsBuilder.fieldChoiceLabel( data, choiceID ) }}}
</span>
</label>
</li>
<# }) #>
</ul>
<# } else { #>
<ul class="primary-input">
<# _.each( data.order, function( choiceID, key ) { #>
<li>
<input type="{{ data.type }}" readonly<# if ( 1 === data.settings.choices[choiceID].default ) { print( ' checked' ); } #>>
{{{ WPFormsBuilder.fieldChoiceLabel( data, choiceID ) }}}
</li>
<# }) #>
</ul>
<# } #>
</script>
<?php
}
/**
* Template for form builder preview.
*
* @since 1.6.9
*/
public function choices_limit_message_template() {
?>
<script type="text/html" id="tmpl-wpforms-choices-limit-message">
<div class="wpforms-alert-dynamic wpforms-alert wpforms-alert-warning">
<?php
printf(
wp_kses( /* translators: %s - total amount of choices. */
__( 'Showing the first 20 choices.<br> All %s choices will be displayed when viewing the form.', 'wpforms-lite' ),
[
'br' => [],
]
),
'{{ data.total }}'
);
?>
</div>
</script>
<?php
}
/**
* Template for empty choices message.
*
* @since 1.8.2
*
* @return void
*/
public function choices_empty_message_template() {
?>
<script type="text/html" id="tmpl-wpforms-empty-choice-message">
<div class="wpforms-notice-dynamic-empty wpforms-alert wpforms-alert-warning">
{{ data.message }}
</div>
</script>
<?php
}
/**
* Builder fields search.
*
* @since 1.8.3
*/
public function search() {
?>
<div class="wpforms-search-fields-wrapper">
<div class="wpforms-search-fields-input-wrapper">
<label for="wpforms-search-fields-input" class="wpforms-screen-reader-element"><?php esc_html_e( 'Search fields:', 'wpforms-lite' ); ?></label>
<input type="search" id="wpforms-search-fields-input" placeholder="<?php echo esc_attr__( 'Search fields...', 'wpforms-lite' ); ?>" autocomplete="off">
<i class="fa fa-times wpforms-search-fields-input-close" aria-hidden="true"></i>
</div>
<div class="wpforms-search-fields-list">
<div class="wpforms-add-fields-group">
<div class="wpforms-add-fields-buttons"></div>
</div>
</div>
<div class="wpforms-search-fields-no-results">
<p>
<?php esc_html_e( 'Sorry, we didn\'t find any fields that match your criteria.', 'wpforms-lite' ); ?>
</p>
</div>
</div>
<?php
}
}
new WPForms_Builder_Panel_Fields();

View File

@@ -0,0 +1,112 @@
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Payments panel.
*
* @since 1.0.0
*/
class WPForms_Builder_Panel_Payments extends WPForms_Builder_Panel {
/**
* All systems go.
*
* @since 1.0.0
*/
public function init() {
// Define panel information.
$this->name = esc_html__( 'Payments', 'wpforms-lite' );
$this->slug = 'payments';
$this->icon = 'fa-usd';
$this->order = 10;
$this->sidebar = true;
}
/**
* Output the Payments panel sidebar.
*
* @since 1.0.0
*/
public function panel_sidebar() {
// Sidebar contents are not valid unless we have a form.
if ( ! $this->form ) {
return;
}
$this->panel_sidebar_section( esc_html__( 'Default', 'wpforms-lite' ), 'default' );
do_action( 'wpforms_payments_panel_sidebar', $this->form );
}
/**
* Output the Payments panel primary content.
*
* @since 1.0.0
*/
public function panel_content() {
// An array of all the active provider addons.
$payments_active = apply_filters( 'wpforms_payments_available', [] );
if ( ! $this->form ) {
// Check if there is a form created. When no form has been created
// yet let the user know we need a form to setup a payment.
echo '<div class="wpforms-alert wpforms-alert-info">';
echo wp_kses(
__( 'You need to <a href="#" class="wpforms-panel-switch" data-panel="setup">setup your form</a> before you can manage these settings.', 'wpforms-lite' ),
[
'a' => [
'href' => [],
'class' => [],
'data-panel' => [],
],
]
);
echo '</div>';
return;
}
if ( empty( $payments_active ) ) {
// Check for active payment addons. When no payment addons are
// activated let the user know they need to install/activate an
// addon to setup a payment.
echo '<div class="wpforms-panel-content-section wpforms-panel-content-section-default">
<div class="illustration illustration-payments"></div>
<h5>' . esc_html__( 'Install Your Payment Integration', 'wpforms-lite' ) . '</h5>
<p>' . sprintf(
wp_kses(
/* translators: %s - addons page URL. */
__( 'It seems you do not have any payment addons activated. You can head over to the <a href="%s">Addons page</a> to install and activate the addon for your payment service.', 'wpforms-lite' ),
[
'a' => [
'href' => [],
],
]
),
esc_url( admin_url( 'admin.php?page=wpforms-addons' ) )
) .
'</p>
</div>';
} else {
// Everything is good - display default instructions.
echo '<div class="wpforms-panel-content-section wpforms-panel-content-section-default">
<div class="illustration illustration-payments"></div>
<h5>' . esc_html__( 'Install Your Payment Integration', 'wpforms-lite' ) . '</h5>
<p>' . esc_html__( 'It seems you don\'t have any payment addons activated. Click one of the available addons and start accepting payments today!', 'wpforms-lite' ) . '</p>
</div>';
}
do_action( 'wpforms_payments_panel_content', $this->form );
}
}
new WPForms_Builder_Panel_Payments();

View File

@@ -0,0 +1,146 @@
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Providers panel.
*
* @since 1.0.0
*/
class WPForms_Builder_Panel_Providers extends WPForms_Builder_Panel {
/**
* All systems go.
*
* @since 1.0.0
*/
public function init() {
// Define panel information.
$this->name = esc_html__( 'Marketing', 'wpforms-lite' );
$this->slug = 'providers';
$this->icon = 'fa-bullhorn';
$this->order = 10;
$this->sidebar = true;
}
/**
* Enqueue assets for the Providers panel.
*
* @since 1.0.0
* @since 1.6.8 All the builder stylesheets enqueues moved to the `\WPForms_Builder::enqueues()`.
*/
public function enqueues() {
$min = wpforms_get_min_suffix();
wp_enqueue_script(
'wpforms-builder-providers',
WPFORMS_PLUGIN_URL . "assets/js/admin-builder-providers{$min}.js",
[ 'jquery' ],
WPFORMS_VERSION,
false
);
wp_localize_script(
'wpforms-builder-providers',
'wpforms_builder_providers',
[
'url' => esc_url( remove_query_arg( 'newform', add_query_arg( [ 'view' => 'providers' ] ) ) ),
'confirm_save' => esc_html__( 'We need to save your progress to continue to the Marketing panel. Is that OK?', 'wpforms-lite' ),
'confirm_connection' => esc_html__( 'Are you sure you want to delete this connection?', 'wpforms-lite' ),
/* translators: %s - connection type. */
'prompt_connection' => esc_html( sprintf( __( 'Enter a %s nickname', 'wpforms-lite' ), '%type%' ) ),
'prompt_placeholder' => esc_html__( 'Eg: Newsletter Optin', 'wpforms-lite' ),
'error_name' => esc_html__( 'You must provide a connection nickname.', 'wpforms-lite' ),
'required_field' => esc_html__( 'Field required', 'wpforms-lite' ),
]
);
}
/**
* Output the Provider panel sidebar.
*
* @since 1.0.0
*/
public function panel_sidebar() {
// Sidebar contents are not valid unless we have a form.
if ( ! $this->form ) {
return;
}
$this->panel_sidebar_section( esc_html__( 'Default', 'wpforms-lite' ), 'default' );
do_action( 'wpforms_providers_panel_sidebar', $this->form );
}
/**
* Output the Provider panel primary content.
*
* @since 1.0.0
*/
public function panel_content() {
if ( ! $this->form ) {
// Check if there is a form created. When no form has been created
// yet let the user know we need a form to setup a provider.
echo '<div class="wpforms-alert wpforms-alert-info">';
echo wp_kses(
__( 'You need to <a href="#" class="wpforms-panel-switch" data-panel="setup">setup your form</a> before you can manage these settings.', 'wpforms-lite' ),
[
'a' => [
'href' => [],
'class' => [],
'data-panel' => [],
],
]
);
echo '</div>';
return;
}
// An array of all the active provider addons.
$providers_active = wpforms_get_providers_available();
if ( empty( $providers_active ) ) {
// Check for active provider addons. When no provider addons are
// activated let the user know they need to install/activate an
// addon to setup a provider.
echo '<div class="wpforms-panel-content-section wpforms-panel-content-section-info">';
echo '<h5>' . esc_html__( 'Install Your Marketing Integration', 'wpforms-lite' ) . '</h5>';
echo '<p>' .
sprintf(
wp_kses(
/* translators: %s - plugin admin area Addons page. */
__( 'It seems you do not have any marketing addons activated. You can head over to the <a href="%s">Addons page</a> to install and activate the addon for your provider.', 'wpforms-lite' ),
[
'a' => [
'href' => [],
],
]
),
esc_url( admin_url( 'admin.php?page=wpforms-addons' ) )
) .
'</p>';
echo '</div>';
} else {
// Everything is good - display default instructions.
echo '<div class="wpforms-panel-content-section wpforms-panel-content-section-default">
<div class="illustration illustration-marketing"></div>
<h5>' . esc_html__( 'Select Your Marketing Integration', 'wpforms-lite' ) . '</h5>
<p>' . esc_html__( 'Select your email marketing service provider or CRM from the options on the left. If you don\'t see your email marketing service listed, then let us know and we\'ll do our best to get it added as fast as possible.', 'wpforms-lite' ) . '</p>
</div>';
}
do_action( 'wpforms_providers_panel_content', $this->form );
}
}
new WPForms_Builder_Panel_Providers();

View File

@@ -0,0 +1,192 @@
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Revisions management panel.
*
* @since 1.7.3
*/
class WPForms_Builder_Panel_Revisions extends WPForms_Builder_Panel {
/**
* All systems go.
*
* @since 1.7.3
*/
public function init() {
// Define panel information.
$this->name = esc_html__( 'Revisions', 'wpforms-lite' );
$this->slug = 'revisions';
$this->icon = 'fa-history';
$this->order = 10;
$this->sidebar = true;
$this->hooks();
}
/**
* Hook into WordPress lifecycle.
*
* @since 1.7.3
*/
private function hooks() {
// Add a notice above all panels if revision is loaded.
add_action( 'wpforms_builder_panels', [ $this, 'panel_notice' ], 100, 2 );
}
/**
* Primary panel button in the left panel navigation.
*
* @since 1.7.3
*
* @param mixed $form The form object.
* @param string $view Current view/panel.
*/
public function button( $form, $view ) {
$classes = 'wpforms-panel-revisions-button';
if ( $view === $this->slug ) {
$classes .= ' active';
}
$badge = '';
if ( $this->form && ! wp_revisions_enabled( $this->form ) && ! wpforms()->get( 'revisions' )->panel_viewed() ) {
$badge = '
<span class="badge-exclamation">
<svg width="4" height="10" fill="none">
<path fill="#fff" fill-rule="evenodd" d="M3.5 8.1c0-.8-.7-1.5-1.5-1.5S.5 7.3.5 8.1 1.2 9.6 2 9.6 3.5 8.9 3.5 8ZM1 .9c-.3 0-.5.2-.4.4l.2 4.4c0 .2.2.3.4.3h1.6c.2 0 .3-.1.4-.3l.2-4.4c0-.2-.2-.4-.4-.4H1Z" clip-rule="evenodd"/>
</svg>
</span>';
}
printf(
'<div class="wpforms-panel-revisions-button-spacer"></div>
<button class="%1$s" data-panel="%2$s" title="%6$s">
%3$s
<i class="fa %4$s"></i>
<span class="screen-reader-text">%5$s</span>
</button>',
esc_attr( $classes ),
esc_attr( $this->slug ),
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
$badge,
esc_attr( $this->icon ),
esc_html( $this->name ),
esc_html__( 'Form Revisions', 'wpforms-lite' )
);
}
/**
* Output the Settings panel sidebar.
*
* @since 1.7.3
*/
public function panel_sidebar() {
// Sidebar contents are not valid unless we have a form.
if ( ! $this->form ) {
return;
}
printf(
'<div class="wpforms-revisions-header">
<h3>%s</h3>
<p>%s</p>
</div>',
esc_html__( 'Form Revisions', 'wpforms-lite' ),
esc_html__( 'Select a revision to roll back to that version. All changes, including settings, will be reverted.', 'wpforms-lite' )
);
// Render a list of form revisions, including current version. All data is safe, escaped in the template.
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
echo wpforms()->get( 'revisions' )->render_revisions_list();
$revisions_to_keep = wp_revisions_to_keep( $this->form );
if ( $revisions_to_keep === 0 ) {
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
echo wpforms_render( 'builder/revisions/notice-disabled' );
}
if ( $revisions_to_keep > 0 ) {
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
echo wpforms_render(
'builder/revisions/notice-limited',
[
'revisions_to_keep' => $revisions_to_keep,
],
true
);
}
}
/**
* Output revision notice above the panels.
*
* @since 1.7.3
*
* @return void
*/
public function panel_notice() {
$revision = wpforms()->get( 'revisions' )->get_revision();
if ( ! $revision ) {
return;
}
$restore_link = sprintf(
'<a href="%1$s">%2$s</a>',
esc_url(
wp_nonce_url(
wpforms()->get( 'revisions' )->get_url(
[
'revision_id' => $revision->ID,
'action' => 'restore_revision',
]
),
'restore_revision',
'wpforms_nonce'
)
),
__( 'Restore this revision', 'wpforms-lite' )
);
$back_link = sprintf(
'<a href="%1$s">%2$s</a>',
esc_url( wpforms()->get( 'revisions' )->get_url() ),
__( 'go back to the current version', 'wpforms-lite' )
);
$message = sprintf( /* translators: %1$s - revision date, %2$s - revision time, %3$s - "Restore this revision" link, %4$s - "go back to the current version" link. */
__( 'Youre currently viewing a form revision from %1$s at %2$s. %3$s or %4$s.', 'wpforms-lite' ),
wpforms()->get( 'revisions' )->get_formatted_datetime( $revision->post_modified_gmt ),
wpforms()->get( 'revisions' )->get_formatted_datetime( $revision->post_modified_gmt, 'time' ),
$restore_link,
$back_link
);
printf(
'<div class="wpforms-revision-notice">
<p><i class="fa fa-history"></i>%s</p>
</div>',
wp_kses(
$message,
[
'a' => [
'href' => [],
],
]
)
);
}
}
new WPForms_Builder_Panel_Revisions();

View File

@@ -0,0 +1,313 @@
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
use WPForms\Admin\Forms\Tags;
use WPForms\Forms\Akismet;
/**
* Settings management panel.
*
* @since 1.0.0
*/
class WPForms_Builder_Panel_Settings extends WPForms_Builder_Panel {
/**
* All systems go.
*
* @since 1.0.0
*/
public function init() {
// Define panel information.
$this->name = esc_html__( 'Settings', 'wpforms-lite' );
$this->slug = 'settings';
$this->icon = 'fa-sliders';
$this->order = 10;
$this->sidebar = true;
}
/**
* Output the Settings panel sidebar.
*
* @since 1.0.0
*/
public function panel_sidebar() {
// Sidebar contents are not valid unless we have a form.
if ( ! $this->form ) {
return;
}
$sections = [
'general' => esc_html__( 'General', 'wpforms-lite' ),
'anti_spam' => esc_html__( 'Spam Protection and Security', 'wpforms-lite' ),
'notifications' => esc_html__( 'Notifications', 'wpforms-lite' ),
'confirmation' => esc_html__( 'Confirmations', 'wpforms-lite' ),
];
$sections = apply_filters( 'wpforms_builder_settings_sections', $sections, $this->form_data );
foreach ( $sections as $slug => $section ) {
$this->panel_sidebar_section( $section, $slug );
}
}
/**
* Enqueue assets.
*
* @since 1.7.5
*/
public function enqueues() {
$min = wpforms_get_min_suffix();
wp_enqueue_script(
'wpforms-builder-settings',
WPFORMS_PLUGIN_URL . "assets/js/components/admin/builder/settings{$min}.js",
[ 'wpforms-builder' ],
WPFORMS_VERSION,
true
);
wp_localize_script(
'wpforms-builder-settings',
'wpforms_builder_settings',
[
'choicesjs_config' => $this->get_choicesjs_config(),
'all_tags_choices' => Tags::get_all_tags_choices(),
]
);
}
/**
* Get Choices.js configuration.
*
* @since 1.7.5
*
* @return array
*/
private function get_choicesjs_config() {
$config = Tags::get_choicesjs_config();
$config['noResultsText'] = esc_html__( 'Press Enter or "," key to add new tag', 'wpforms-lite' );
return $config;
}
/**
* Output the Settings panel primary content.
*
* @since 1.0.0
*/
public function panel_content() {
// Check if there is a form created.
if ( ! $this->form ) {
echo '<div class="wpforms-alert wpforms-alert-info">';
echo wp_kses(
__( 'You need to <a href="#" class="wpforms-panel-switch" data-panel="setup">setup your form</a> before you can manage the settings.', 'wpforms-lite' ),
[
'a' => [
'href' => [],
'class' => [],
'data-panel' => [],
],
]
);
echo '</div>';
return;
}
/*
* General.
*/
echo '<div class="wpforms-panel-content-section wpforms-panel-content-section-general">';
echo '<div class="wpforms-panel-content-section-title">';
esc_html_e( 'General', 'wpforms-lite' );
echo '</div>';
wpforms_panel_field(
'text',
'settings',
'form_title',
$this->form_data,
esc_html__( 'Form Name', 'wpforms-lite' ),
[
'default' => $this->form->post_title,
]
);
wpforms_panel_field(
'textarea',
'settings',
'form_desc',
$this->form_data,
esc_html__( 'Form Description', 'wpforms-lite' )
);
$this->general_setting_tags();
wpforms_panel_field(
'text',
'settings',
'submit_text',
$this->form_data,
esc_html__( 'Submit Button Text', 'wpforms-lite' ),
[
'default' => esc_html__( 'Submit', 'wpforms-lite' ),
]
);
wpforms_panel_field(
'text',
'settings',
'submit_text_processing',
$this->form_data,
esc_html__( 'Submit Button Processing Text', 'wpforms-lite' ),
[
'tooltip' => esc_html__( 'Enter the submit button text you would like the button display while the form submit is processing.', 'wpforms-lite' ),
]
);
$this->general_setting_advanced();
echo '</div>';
/*
* Notifications.
*/
echo '<div class="wpforms-panel-content-section wpforms-panel-content-section-notifications" data-panel="notifications">';
do_action( 'wpforms_form_settings_notifications', $this );
echo '</div>';
/*
* Confirmations.
*/
echo '<div class="wpforms-panel-content-section wpforms-panel-content-section-confirmation" data-panel="confirmations">';
do_action( 'wpforms_form_settings_confirmations', $this );
echo '</div>';
/*
* Custom panels can be added below.
*/
do_action( 'wpforms_form_settings_panel_content', $this );
}
/**
* Output the Tags setting.
*
* @since 1.7.5
*/
private function general_setting_tags() {
$form_tags = [];
if ( ! empty( $this->form_data['settings']['form_tags'] ) ) {
$form_tags = get_terms(
[
'taxonomy' => WPForms_Form_Handler::TAGS_TAXONOMY,
'name' => $this->form_data['settings']['form_tags'],
'hide_empty' => false,
]
);
$form_tags = is_wp_error( $form_tags ) ? [] : (array) $form_tags;
}
$tags_value = wp_list_pluck( $form_tags, 'term_id' );
$tags_options = wp_list_pluck( $form_tags, 'name', 'term_id' );
wpforms_panel_field(
'select',
'settings',
'form_tags',
$this->form_data,
esc_html__( 'Tags', 'wpforms-lite' ),
[
'options' => $tags_options,
'value' => $tags_value,
'multiple' => true,
'tooltip' => esc_html__( 'Mark form with the tags. To create a new tag, simply type it and press Enter.', 'wpforms-lite' ),
]
);
}
/**
* Output the *CAPTCHA settings.
*
* @since 1.6.8
*/
private function general_setting_advanced() {
ob_start();
wpforms_panel_field(
'text',
'settings',
'form_class',
$this->form_data,
esc_html__( 'Form CSS Class', 'wpforms-lite' ),
[
'tooltip' => esc_html__( 'Enter CSS class names for the form wrapper. Multiple class names should be separated with spaces.', 'wpforms-lite' ),
]
);
wpforms_panel_field(
'text',
'settings',
'submit_class',
$this->form_data,
esc_html__( 'Submit Button CSS Class', 'wpforms-lite' ),
[
'tooltip' => esc_html__( 'Enter CSS class names for the form submit button. Multiple names should be separated with spaces.', 'wpforms-lite' ),
]
);
wpforms_panel_field(
'toggle',
'settings',
'dynamic_population',
$this->form_data,
esc_html__( 'Enable Prefill by URL', 'wpforms-lite' ),
[
'tooltip' => sprintf(
'<a href="%1$s" target="_blank" rel="noopener noreferrer">%2$s</a>',
wpforms_utm_link( 'https://wpforms.com/developers/how-to-enable-dynamic-field-population/', 'Builder Settings', 'Prefill by URL Tooltip' ),
esc_html__( 'How to use Prefill by URL', 'wpforms-lite' )
),
]
);
wpforms_panel_field(
'toggle',
'settings',
'ajax_submit',
$this->form_data,
esc_html__( 'Enable AJAX form submission', 'wpforms-lite' ),
[
'tooltip' => esc_html__( 'Enables form submission without page reload.', 'wpforms-lite' ),
]
);
do_action( 'wpforms_form_settings_general', $this );
// Wrap advanced settings to the unfoldable group.
wpforms_panel_fields_group(
ob_get_clean(),
[
'borders' => [ 'top' ],
'unfoldable' => true,
'group' => 'settings_advanced',
'title' => esc_html__( 'Advanced', 'wpforms-lite' ),
],
true
);
}
}
new WPForms_Builder_Panel_Settings();

View File

@@ -0,0 +1,103 @@
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Setup panel.
*
* @since 1.0.0
* @since 1.6.8 Form Builder Refresh.
*/
class WPForms_Builder_Panel_Setup extends WPForms_Builder_Panel {
use \WPForms\Admin\Traits\FormTemplates;
/**
* All systems go.
*
* @since 1.0.0
*/
public function init() {
// Define panel information.
$this->name = esc_html__( 'Setup', 'wpforms-lite' );
$this->slug = 'setup';
$this->icon = 'fa-cog';
$this->order = 5;
$this->addons_obj = wpforms()->get( 'addons' );
}
/**
* Enqueue assets for the Setup panel.
*
* @since 1.0.0
* @since 1.6.8 All the builder stylesheets enqueues moved to the `\WPForms_Builder::enqueues()`.
*/
public function enqueues() {
$min = wpforms_get_min_suffix();
wp_enqueue_script(
'wpforms-builder-setup',
WPFORMS_PLUGIN_URL . "assets/js/components/admin/builder/setup{$min}.js",
[ 'wpforms-builder', 'listjs' ],
WPFORMS_VERSION,
true
);
}
/**
* Output the Settings panel primary content.
*
* @since 1.0.0
*/
public function panel_content() {
?>
<div id="wpforms-setup-form-name">
<label for="wpforms-setup-name"><?php esc_html_e( 'Name Your Form', 'wpforms-lite' ); ?></label>
<input type="text" id="wpforms-setup-name" placeholder="<?php esc_attr_e( 'Enter your form name here&hellip;', 'wpforms-lite' ); ?>">
</div>
<div class="wpforms-setup-title">
<?php esc_html_e( 'Select a Template', 'wpforms-lite' ); ?>
<span class="wpforms-setup-title-after"></span>
</div>
<p class="wpforms-setup-desc secondary-text">
<?php
printf(
wp_kses( /* translators: %1$s - create template doc link, %2$s - Contact us page link. */
__( 'To speed up the process you can select from one of our pre-made templates, start with a <a href="#" class="wpforms-trigger-blank">blank form</a> or <a href="%1$s" target="_blank" rel="noopener noreferrer">create your own</a>. Have a suggestion for a new template? <a href="%2$s" target="_blank" rel="noopener noreferrer">Wed love to hear it</a>!', 'wpforms-lite' ),
[
'strong' => [],
'a' => [
'href' => [],
'class' => [],
'target' => [],
'rel' => [],
],
]
),
esc_url( wpforms_utm_link( 'https://wpforms.com/docs/how-to-create-a-custom-form-template/', 'builder-templates', 'Create Your Own Template Documentation' ) ),
esc_url( wpforms_utm_link( 'https://wpforms.com/form-template-suggestion/', 'builder-templates', 'Suggest a Template' ) )
);
?>
</p>
<?php
$this->output_templates_content();
/**
* Fires after WPForms builder setup panel.
*
* @since 1.0.6
*/
do_action( 'wpforms_setup_panel_after' ); // phpcs:ignore WPForms.PHP.ValidateHooks.InvalidHookName
}
}
new WPForms_Builder_Panel_Setup();

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,383 @@
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Functionality related to the admin TinyMCE editor.
*
* @since 1.0.0
*/
class WPForms_Admin_Editor {
/**
* Primary class constructor.
*
* @since 1.0.0
*/
public function __construct() {
add_action( 'media_buttons', [ $this, 'media_button' ], 15 );
}
/**
* Allow easy shortcode insertion via a custom media button.
*
* @since 1.0.0
*
* @param string $editor_id Editor Id.
*/
public function media_button( $editor_id ) {
if ( ! \wpforms_current_user_can( 'view_forms' ) ) {
return;
}
// Provide the ability to conditionally disable the button, so it can be
// disabled for custom fields or front-end use such as bbPress. We default
// to only showing within the post editor page.
if ( ! apply_filters( 'wpforms_display_media_button', $this->is_post_editor_page(), $editor_id ) ) {
return;
}
// Setup the icon - currently using a dashicon.
$icon = '<span class="wp-media-buttons-icon wpforms-menu-icon" style="font-size:16px;margin-top:-2px;"><svg width="18" height="18" viewBox="0 0 1792 1792" xmlns="http://www.w3.org/2000/svg"><path d="M643 911v128h-252v-128h252zm0-255v127h-252v-127h252zm758 511v128h-341v-128h341zm0-256v128h-672v-128h672zm0-255v127h-672v-127h672zm135 860v-1240q0-8-6-14t-14-6h-32l-378 256-210-171-210 171-378-256h-32q-8 0-14 6t-6 14v1240q0 8 6 14t14 6h1240q8 0 14-6t6-14zm-855-1110l185-150h-406zm430 0l221-150h-406zm553-130v1240q0 62-43 105t-105 43h-1240q-62 0-105-43t-43-105v-1240q0-62 43-105t105-43h1240q62 0 105 43t43 105z" fill="#82878c"/></svg></span>';
printf(
'<a href="#" class="button wpforms-insert-form-button" data-editor="%s" title="%s">%s %s</a>',
esc_attr( $editor_id ),
esc_attr__( 'Add Form', 'wpforms-lite' ),
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
$icon,
esc_html__( 'Add Form', 'wpforms-lite' )
);
$min = wpforms_get_min_suffix();
// If we have made it this far then load the JS.
wp_enqueue_script(
'wpforms-editor',
WPFORMS_PLUGIN_URL . "assets/js/admin-editor{$min}.js",
[ 'jquery' ],
WPFORMS_VERSION,
true
);
add_action( 'admin_footer', [ $this, 'shortcode_modal' ] );
}
/**
* Check if we are on the post editor admin page.
*
* @since 1.6.2
*
* @returns boolean True if it is post editor admin page.
*/
public function is_post_editor_page() {
if ( ! is_admin() ) {
return false;
}
// get_current_screen() is loaded after 'admin_init' hook and may not exist yet.
if ( ! function_exists( 'get_current_screen' ) ) {
return false;
}
$screen = get_current_screen();
return $screen !== null && $screen->parent_base === 'edit';
}
/**
* Modal window for inserting the form shortcode into TinyMCE.
*
* Thickbox is old and busted so we don't use that. Creating a custom view in
* Backbone would make me pull my hair out. So instead we offer a small clean
* modal that is based off of the WordPress insert link modal.
*
* @since 1.0.0
*/
public function shortcode_modal() {
?>
<div id="wpforms-modal-backdrop" style="display: none"></div>
<div id="wpforms-modal-wrap" style="display: none">
<form id="wpforms-modal" tabindex="-1">
<div id="wpforms-modal-title">
<?php esc_html_e( 'Insert Form', 'wpforms-lite' ); ?>
<button type="button" id="wpforms-modal-close"><span class="screen-reader-text"><?php esc_html_e( 'Close', 'wpforms-lite' ); ?></span></button>
</div>
<div id="wpforms-modal-inner">
<div id="wpforms-modal-options">
<?php
echo '<p id="wpforms-modal-notice">';
printf(
wp_kses( /* translators: %s - WPForms documentation URL. */
__( 'Heads up! Don\'t forget to test your form. <a href="%s" target="_blank" rel="noopener noreferrer">Check out our complete guide</a>!', 'wpforms-lite' ),
[
'a' => [
'href' => [],
'rel' => [],
'target' => [],
],
]
),
'https://wpforms.com/docs/how-to-properly-test-your-wordpress-forms-before-launching-checklist/'
);
echo '</p>';
$args = apply_filters( 'wpforms_modal_select', [] );
$forms = wpforms()->form->get( '', $args );
if ( ! empty( $forms ) ) {
printf( '<p><label for="wpforms-modal-select-form">%s</label></p>', esc_html__( 'Select a form below to insert', 'wpforms-lite' ) );
echo '<select id="wpforms-modal-select-form">';
foreach ( $forms as $form ) {
printf( '<option value="%d">%s</option>', $form->ID, esc_html( $form->post_title ) );
}
echo '</select><br>';
printf( '<p class="wpforms-modal-inline"><input type="checkbox" id="wpforms-modal-checkbox-title"><label for="wpforms-modal-checkbox-title">%s</label></p>', esc_html__( 'Show form name', 'wpforms-lite' ) );
printf( '<p class="wpforms-modal-inline"><input type="checkbox" id="wpforms-modal-checkbox-description"><label for="wpforms-modal-checkbox-description">%s</label></p>', esc_html__( 'Show form description', 'wpforms-lite' ) );
} else {
echo '<p>';
printf(
wp_kses(
/* translators: %s - WPForms Builder page. */
__( 'Whoops, you haven\'t created a form yet. Want to <a href="%s">give it a go</a>?', 'wpforms-lite' ),
[
'a' => [
'href' => [],
],
]
),
admin_url( 'admin.php?page=wpforms-builder' )
);
echo '</p>';
}
?>
</div>
</div>
<div class="submitbox">
<div id="wpforms-modal-cancel">
<a class="submitdelete deletion" href="#"><?php esc_html_e( 'Cancel', 'wpforms-lite' ); ?></a>
</div>
<?php if ( ! empty( $forms ) ) : ?>
<div id="wpforms-modal-update">
<button class="button button-primary" id="wpforms-modal-submit"><?php esc_html_e( 'Add Form', 'wpforms-lite' ); ?></button>
</div>
<?php endif; ?>
</div>
</form>
</div>
<style type="text/css">
.wpforms-insert-form-button svg path {
fill: #0071a1;
}
.wpforms-insert-form-button:hover svg path {
fill: #016087;
}
#wpforms-modal-wrap {
display: none;
background-color: #fff;
-webkit-box-shadow: 0 3px 6px rgba(0, 0, 0, 0.3);
box-shadow: 0 3px 6px rgba(0, 0, 0, 0.3);
width: 500px;
height: 285px;
overflow: hidden;
margin-left: -250px;
margin-top: -125px;
position: fixed;
top: 50%;
left: 50%;
z-index: 100205;
-webkit-transition: height 0.2s, margin-top 0.2s;
transition: height 0.2s, margin-top 0.2s;
}
#wpforms-modal-backdrop {
display: none;
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
min-height: 360px;
background: #000;
opacity: 0.7;
filter: alpha(opacity=70);
z-index: 100200;
}
#wpforms-modal {
position: relative;
height: 100%;
}
#wpforms-modal-title {
background: #fcfcfc;
border-bottom: 1px solid #dfdfdf;
height: 36px;
font-size: 18px;
font-weight: 600;
line-height: 36px;
padding: 0 36px 0 16px;
top: 0;
right: 0;
left: 0;
}
#wpforms-modal-close {
color: #666;
padding: 0;
position: absolute;
top: 0;
right: 0;
width: 36px;
height: 36px;
text-align: center;
background: none;
border: none;
cursor: pointer;
}
#wpforms-modal-close:before {
font: normal 20px/36px 'dashicons';
vertical-align: top;
speak: none;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
width: 36px;
height: 36px;
content: '\f158';
}
#wpforms-modal-close:hover,
#wpforms-modal-close:focus {
color: #2ea2cc;
}
#wpforms-modal-close:focus {
outline: none;
-webkit-box-shadow: 0 0 0 1px #5b9dd9,
0 0 2px 1px rgba(30, 140, 190, .8);
box-shadow: 0 0 0 1px #5b9dd9,
0 0 2px 1px rgba(30, 140, 190, .8);
}
#wpforms-modal-inner {
padding: 0 16px 50px;
}
#wpforms-modal-search-toggle:after {
display: inline-block;
font: normal 20px/1 'dashicons';
vertical-align: top;
speak: none;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
content: '\f140';
}
#wpforms-modal-notice {
background-color: #d9edf7;
border: 1px solid #bce8f1;
color: #31708f;
padding: 10px;
}
#wpforms-modal #wpforms-modal-options {
padding: 8px 0 12px;
}
#wpforms-modal #wpforms-modal-options .wpforms-modal-inline {
display: inline-block;
margin: 0;
padding: 0 20px 0 0;
}
#wpforms-modal-select-form {
margin-bottom: 1em;
max-width: 100%;
}
#wpforms-modal .submitbox {
padding: 8px 16px;
background: #fcfcfc;
border-top: 1px solid #dfdfdf;
position: absolute;
bottom: 0;
left: 0;
right: 0;
}
#wpforms-modal-cancel {
line-height: 25px;
float: left;
}
#wpforms-modal-update {
line-height: 23px;
float: right;
}
#wpforms-modal-submit {
float: right;
margin-bottom: 0;
}
@media screen and ( max-width: 782px ) {
#wpforms-modal-wrap {
height: 280px;
margin-top: -140px;
}
#wpforms-modal-inner {
padding: 0 16px 60px;
}
#wpforms-modal-cancel {
line-height: 32px;
}
}
@media screen and ( max-width: 520px ) {
#wpforms-modal-wrap {
width: auto;
margin-left: 0;
left: 10px;
right: 10px;
max-width: 500px;
}
}
@media screen and ( max-height: 520px ) {
#wpforms-modal-wrap {
-webkit-transition: none;
transition: none;
}
}
@media screen and ( max-height: 290px ) {
#wpforms-modal-wrap {
height: auto;
margin-top: 0;
top: 10px;
bottom: 10px;
}
#wpforms-modal-inner {
overflow: auto;
height: -webkit-calc(100% - 92px);
height: calc(100% - 92px);
padding-bottom: 2px;
}
}
</style>
<?php
}
}
new WPForms_Admin_Editor();

View File

@@ -0,0 +1,27 @@
<?php
use WPForms\Helpers\PluginSilentUpgraderSkin;
/**
* Skin for on-the-fly addon installations.
*
* @since 1.0.0
* @since 1.5.6.1 Extend PluginSilentUpgraderSkin and clean up the class.
*/
class WPForms_Install_Skin extends PluginSilentUpgraderSkin {
/**
* Instead of outputting HTML for errors, json_encode the errors and send them
* back to the Ajax script for processing.
*
* @since 1.0.0
*
* @param array $errors Array of errors with the install process.
*/
public function error( $errors ) {
if ( ! empty( $errors ) ) {
wp_send_json_error( $errors );
}
}
}

View File

@@ -0,0 +1,435 @@
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Register menu elements and do other global tasks.
*
* @since 1.0.0
*/
class WPForms_Admin_Menu {
/**
* Primary class constructor.
*
* @since 1.0.0
*/
public function __construct() {
// Let's make some menus.
add_action( 'admin_menu', [ $this, 'register_menus' ], 9 );
add_action( 'admin_head', [ $this, 'hide_wpforms_submenu_items' ] );
add_action( 'admin_head', [ $this, 'adjust_pro_menu_item' ] );
add_action( 'admin_head', [ $this, 'admin_menu_styles' ], 11 );
// Plugins page settings link.
add_filter( 'plugin_action_links_' . plugin_basename( WPFORMS_PLUGIN_DIR . 'wpforms.php' ), [ $this, 'settings_link' ], 10, 4 );
}
/**
* Register our menus.
*
* @since 1.0.0
*/
public function register_menus() {
$manage_cap = wpforms_get_capability_manage_options();
$access = wpforms()->get( 'access' );
if ( ! method_exists( $access, 'get_menu_cap' ) ) {
return;
}
// Default Forms top level menu item.
add_menu_page(
esc_html__( 'WPForms', 'wpforms-lite' ),
esc_html__( 'WPForms', 'wpforms-lite' ),
$access->get_menu_cap( 'view_forms' ),
'wpforms-overview',
[ $this, 'admin_page' ],
'data:image/svg+xml;base64,' . base64_encode( '<svg width="1792" height="1792" viewBox="0 0 1792 1792" xmlns="http://www.w3.org/2000/svg"><path fill="#9ea3a8" d="M643 911v128h-252v-128h252zm0-255v127h-252v-127h252zm758 511v128h-341v-128h341zm0-256v128h-672v-128h672zm0-255v127h-672v-127h672zm135 860v-1240q0-8-6-14t-14-6h-32l-378 256-210-171-210 171-378-256h-32q-8 0-14 6t-6 14v1240q0 8 6 14t14 6h1240q8 0 14-6t6-14zm-855-1110l185-150h-406zm430 0l221-150h-406zm553-130v1240q0 62-43 105t-105 43h-1240q-62 0-105-43t-43-105v-1240q0-62 43-105t105-43h1240q62 0 105 43t43 105z"/></svg>' ),
apply_filters( 'wpforms_menu_position', '58.9' )
);
// All Forms sub menu item.
add_submenu_page(
'wpforms-overview',
esc_html__( 'WPForms', 'wpforms-lite' ),
esc_html__( 'All Forms', 'wpforms-lite' ),
$access->get_menu_cap( 'view_forms' ),
'wpforms-overview',
[ $this, 'admin_page' ]
);
// Add New sub menu item.
add_submenu_page(
'wpforms-overview',
esc_html__( 'WPForms Builder', 'wpforms-lite' ),
esc_html__( 'Add New', 'wpforms-lite' ),
$access->get_menu_cap( [ 'create_forms', 'edit_forms' ] ),
'wpforms-builder',
[ $this, 'admin_page' ]
);
// Entries sub menu item.
add_submenu_page(
'wpforms-overview',
esc_html__( 'Form Entries', 'wpforms-lite' ),
esc_html__( 'Entries', 'wpforms-lite' ),
$access->get_menu_cap( 'view_entries' ),
'wpforms-entries',
[ $this, 'admin_page' ]
);
// Payments sub menu item.
add_submenu_page(
'wpforms-overview',
esc_html__( 'Payments', 'wpforms-lite' ),
esc_html__( 'Payments', 'wpforms-lite' ) . $this->get_new_badge_html(),
$manage_cap,
WPForms\Admin\Payments\Payments::SLUG,
[ $this, 'admin_page' ]
);
do_action_deprecated(
'wpform_admin_menu',
[ $this ],
'1.5.5 of the WPForms plugin',
'wpforms_admin_menu'
);
do_action( 'wpforms_admin_menu', $this );
// Templates sub menu item.
add_submenu_page(
'wpforms-overview',
esc_html__( 'WPForms Templates', 'wpforms-lite' ),
esc_html__( 'Form Templates', 'wpforms-lite' ),
$access->get_menu_cap( 'create_forms' ),
'wpforms-templates',
[ $this, 'admin_page' ]
);
// Settings sub menu item.
add_submenu_page(
'wpforms-overview',
esc_html__( 'WPForms Settings', 'wpforms-lite' ),
esc_html__( 'Settings', 'wpforms-lite' ),
$manage_cap,
'wpforms-settings',
[ $this, 'admin_page' ]
);
// Tools sub menu item.
add_submenu_page(
'wpforms-overview',
esc_html__( 'WPForms Tools', 'wpforms-lite' ),
esc_html__( 'Tools', 'wpforms-lite' ),
$access->get_menu_cap( [ 'create_forms', 'view_forms', 'view_entries' ] ),
'wpforms-tools',
[ $this, 'admin_page' ]
);
// Hidden placeholder paged used for misc content.
add_submenu_page(
'wpforms-settings',
esc_html__( 'WPForms', 'wpforms-lite' ),
esc_html__( 'Info', 'wpforms-lite' ),
$access->get_menu_cap( 'any' ),
'wpforms-page',
[ $this, 'admin_page' ]
);
// Addons submenu page.
add_submenu_page(
'wpforms-overview',
esc_html__( 'WPForms Addons', 'wpforms-lite' ),
'<span style="color:#f18500">' . esc_html__( 'Addons', 'wpforms-lite' ) . '</span>',
$manage_cap,
'wpforms-addons',
[ $this, 'admin_page' ]
);
// Analytics submenu page.
add_submenu_page(
'wpforms-overview',
esc_html__( 'Analytics', 'wpforms-lite' ),
esc_html__( 'Analytics', 'wpforms-lite' ),
$manage_cap,
WPForms\Admin\Pages\Analytics::SLUG,
[ $this, 'admin_page' ]
);
// SMTP submenu page.
add_submenu_page(
'wpforms-overview',
esc_html__( 'SMTP', 'wpforms-lite' ),
esc_html__( 'SMTP', 'wpforms-lite' ),
$manage_cap,
WPForms\Admin\Pages\SMTP::SLUG,
[ $this, 'admin_page' ]
);
// About submenu page.
add_submenu_page(
'wpforms-overview',
esc_html__( 'About WPForms', 'wpforms-lite' ),
esc_html__( 'About Us', 'wpforms-lite' ),
$access->get_menu_cap( 'any' ),
WPForms_About::SLUG,
[ $this, 'admin_page' ]
);
// Community submenu page.
add_submenu_page(
'wpforms-overview',
esc_html__( 'Community', 'wpforms-lite' ),
esc_html__( 'Community', 'wpforms-lite' ),
$manage_cap,
WPForms\Admin\Pages\Community::SLUG,
[ $this, 'admin_page' ]
);
if ( ! wpforms()->is_pro() ) {
add_submenu_page(
'wpforms-overview',
esc_html__( 'Upgrade to Pro', 'wpforms-lite' ),
esc_html__( 'Upgrade to Pro', 'wpforms-lite' ),
$manage_cap,
wpforms_admin_upgrade_link( 'admin-menu' )
);
}
}
/**
* Hide "Add New" admin menu item if a user can't create forms.
*
* @since 1.5.8
*/
public function hide_wpforms_submenu_items() {
if ( wpforms_current_user_can( 'create_forms' ) ) {
return;
}
global $submenu;
if ( ! isset( $submenu['wpforms-overview'] ) ) {
return;
}
foreach ( $submenu['wpforms-overview'] as $key => $item ) {
if ( isset( $item[2] ) && 'wpforms-builder' === $item[2] ) {
unset( $submenu['wpforms-overview'][ $key ] );
break;
}
}
$this->hide_wpforms_menu_item();
}
/**
* Hide "WPForms" admin menu if it has no submenu items.
*
* @since 1.5.8
*/
public function hide_wpforms_menu_item() {
global $submenu, $menu;
if ( ! empty( $submenu['wpforms-overview'] ) ) {
return;
}
unset( $submenu['wpforms-overview'] );
foreach ( $menu as $key => $item ) {
if ( isset( $item[2] ) && 'wpforms-overview' === $item[2] ) {
unset( $menu[ $key ] );
break;
}
}
}
/**
* Alias method for backward compatibility.
*
* @since 1.7.4
* @deprecated 1.7.8
*/
public function style_upgrade_pro_link() {
_deprecated_function( __METHOD__, '1.7.8 of the WPForms plugin', __CLASS__ . '::adjust_pro_menu_item()' );
$this->adjust_pro_menu_item();
}
/**
* Add the PRO badge to left sidebar menu item.
*
* @since 1.7.8
* @deprecated 1.8.1
*/
public function adjust_pro_menu_item_class() {
_deprecated_function( __METHOD__, '1.8.1 of the WPForms plugin', __CLASS__ . '::adjust_pro_menu_item()' );
$this->adjust_pro_menu_item();
}
/**
* Make changes to the PRO menu item.
*
* @since 1.8.1
*/
public function adjust_pro_menu_item() {
global $submenu;
// Bail if plugin menu is not registered.
if ( ! isset( $submenu['wpforms-overview'] ) ) {
return;
}
$upgrade_link_position = key(
array_filter(
$submenu['wpforms-overview'],
static function( $item ) {
return strpos( $item[2], 'https://wpforms.com/lite-upgrade' ) !== false;
}
)
);
// Bail if "Upgrade to Pro" menu item is not registered.
if ( $upgrade_link_position === null ) {
return;
}
// Add the PRO badge to the menu item.
// phpcs:disable WordPress.WP.GlobalVariablesOverride.Prohibited
if ( isset( $submenu['wpforms-overview'][ $upgrade_link_position ][4] ) ) {
$submenu['wpforms-overview'][ $upgrade_link_position ][4] .= ' wpforms-sidebar-upgrade-pro';
} else {
$submenu['wpforms-overview'][ $upgrade_link_position ][] = 'wpforms-sidebar-upgrade-pro';
}
$current_screen = get_current_screen();
$upgrade_utm_content = $current_screen === null ? 'Upgrade to Pro' : 'Upgrade to Pro - ' . $current_screen->base;
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
$upgrade_utm_content = empty( $_GET['view'] ) ? $upgrade_utm_content : $upgrade_utm_content . ': ' . sanitize_key( $_GET['view'] );
// Add utm_content to the menu item.
$submenu['wpforms-overview'][ $upgrade_link_position ][2] = esc_url(
add_query_arg(
'utm_content',
$upgrade_utm_content,
$submenu['wpforms-overview'][ $upgrade_link_position ][2]
)
);
// phpcs:enable WordPress.WP.GlobalVariablesOverride.Prohibited
}
/**
* Wrapper for the hook to render our custom settings pages.
*
* @since 1.0.0
*/
public function admin_page() {
do_action( 'wpforms_admin_page' );
}
/**
* Add settings link to the Plugins page.
*
* @since 1.3.9
*
* @param array $links Plugin row links.
* @param string $plugin_file Path to the plugin file relative to the plugins directory.
* @param array $plugin_data An array of plugin data. See `get_plugin_data()`.
* @param string $context The plugin context.
*
* @return array $links
*/
public function settings_link( $links, $plugin_file, $plugin_data, $context ) {
$custom['pro'] = sprintf(
'<a href="%1$s" aria-label="%2$s" target="_blank" rel="noopener noreferrer"
style="color: #00a32a; font-weight: 700;"
onmouseover="this.style.color=\'#008a20\';"
onmouseout="this.style.color=\'#00a32a\';"
>%3$s</a>',
esc_url(
wpforms_admin_upgrade_link(
'all-plugins',
'Get WPForms Pro'
)
),
esc_attr__( 'Upgrade to WPForms Pro', 'wpforms-lite' ),
esc_html__( 'Get WPForms Pro', 'wpforms-lite' )
);
$custom['settings'] = sprintf(
'<a href="%s" aria-label="%s">%s</a>',
esc_url(
add_query_arg(
[ 'page' => 'wpforms-settings' ],
admin_url( 'admin.php' )
)
),
esc_attr__( 'Go to WPForms Settings page', 'wpforms-lite' ),
esc_html__( 'Settings', 'wpforms-lite' )
);
$custom['docs'] = sprintf(
'<a href="%1$s" aria-label="%2$s" target="_blank" rel="noopener noreferrer">%3$s</a>',
esc_url(
add_query_arg(
[
'utm_content' => 'Documentation',
'utm_campaign' => 'liteplugin',
'utm_medium' => 'all-plugins',
'utm_source' => 'WordPress',
],
'https://wpforms.com/docs/'
)
),
esc_attr__( 'Read the documentation', 'wpforms-lite' ),
esc_html__( 'Docs', 'wpforms-lite' )
);
return array_merge( $custom, (array) $links );
}
/**
* Get the HTML for the "NEW!" badge.
*
* @since 1.7.8
*
* @return string
*/
private function get_new_badge_html() {
return '<span class="wpforms-menu-new">&nbsp;NEW!</span>';
}
/**
* Output inline styles for the admin menu.
*
* @since 1.7.8
*/
public function admin_menu_styles() {
$styles = '#adminmenu .wpforms-menu-new { color: #f18500; vertical-align: super; font-size: 9px; font-weight: 600; padding-left: 2px; }';
if ( ! wpforms()->is_pro() ) {
$styles .= 'a.wpforms-sidebar-upgrade-pro { background-color: #00a32a !important; color: #fff !important; font-weight: 600 !important; }';
}
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
printf( '<style>%s</style>', $styles );
}
}
new WPForms_Admin_Menu();

View File

@@ -0,0 +1,138 @@
<?php
/**
* Admin notices, on the fly.
*
* @example
* WPForms_Admin_Notice::success( 'All is good!' );
*
* @example
* WPForms_Admin_Notice::warning( 'Do something please.' );
*
* @since 1.3.9
* @deprecated 1.7.2
*/
class WPForms_Admin_Notice {
/**
* Single instance holder.
*
* @since 1.3.9
* @var mixed
*/
private static $_instance = null;
/**
* Added notices.
*
* @since 1.3.9
* @var array
*/
public $notices = [];
/**
* Get the instance.
*
* @since 1.3.9
* @return WPForms_Admin_Notice
*/
public static function getInstance() {
if ( self::$_instance === null ) {
self::$_instance = new WPForms_Admin_Notice();
}
return self::$_instance;
}
/**
* Hook when called.
*
* @since 1.3.9
*/
public function __construct() {
_deprecated_function( __METHOD__, '1.7.2 of the WPForms plugin' );
add_action( 'admin_notices', [ &$this, 'display' ] );
}
/**
* Display the notices.
*
* @since 1.3.9
*/
public function display() {
// At least one WPForms capability is needed to see admin notices.
if ( ! wpforms_current_user_can( 'any' ) ) {
return;
}
echo implode( ' ', $this->notices );
}
/**
* Add notice to instance property.
*
* @since 1.3.9
*
* @param string $message Message to display.
* @param string $type Type of the notice (default: '').
*/
public static function add( $message, $type = '' ) {
_deprecated_function( __METHOD__, '1.7.2 of the WPForms plugin' );
$instance = self::getInstance();
$id = 'wpforms-notice-' . ( count( $instance->notices ) + 1 );
$type = ! empty( $type ) ? 'notice-' . $type : '';
$notice = sprintf( '<div class="notice wpforms-notice %s" id="%s">%s</div>', $type, $id, wpautop( $message ) );
$instance->notices[] = $notice;
}
/**
* Add Info notice.
*
* @since 1.3.9
*
* @param string $message Message to display.
*/
public static function info( $message ) {
self::add( $message, 'info' );
}
/**
* Add Error notice.
*
* @since 1.3.9
*
* @param string $message Message to display.
*/
public static function error( $message ) {
self::add( $message, 'error' );
}
/**
* Add Success notice.
*
* @since 1.3.9
*
* @param string $message Message to display.
*/
public static function success( $message ) {
self::add( $message, 'success' );
}
/**
* Add Warning notice.
*
* @since 1.3.9
*
* @param string $message Message to display.
*/
public static function warning( $message ) {
self::add( $message, 'warning' );
}
}

View File

@@ -0,0 +1,320 @@
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Ask for some love.
*
* @since 1.3.2
*/
class WPForms_Review {
/**
* Primary class constructor.
*
* @since 1.3.2
*/
public function __construct() {
// Admin notice requesting review.
add_action( 'admin_init', [ $this, 'review_request' ] );
add_action( 'wp_ajax_wpforms_review_dismiss', [ $this, 'review_dismiss' ] );
// Admin footer text.
add_filter( 'admin_footer_text', [ $this, 'admin_footer' ], 1, 2 );
add_action( 'in_admin_footer', [ $this, 'promote_wpforms' ] );
}
/**
* Add admin notices as needed for reviews.
*
* @since 1.3.2
*/
public function review_request() {
// Only consider showing the review request to admin users.
if ( ! is_super_admin() ) {
return;
}
// If the user has opted out of product announcement notifications, don't
// display the review request.
if ( wpforms_setting( 'hide-announcements', false ) ) {
return;
}
// Verify that we can do a check for reviews.
$notices = (array) get_option( 'wpforms_admin_notices', [] );
$time = time();
$load = false;
if ( empty( $notices['review_request'] ) ) {
$notices['review_request'] = [
'time' => $time,
'dismissed' => false,
];
update_option( 'wpforms_admin_notices', $notices );
return;
}
// Check if it has been dismissed or not.
if (
( isset( $notices['review_request']['dismissed'] ) &&
! $notices['review_request']['dismissed'] ) &&
(
isset( $notices['review_request']['time'] ) &&
( ( $notices['review_request']['time'] + DAY_IN_SECONDS ) <= $time )
)
) {
$load = true;
}
// If we cannot load, return early.
if ( ! $load ) {
return;
}
// Logic is slightly different depending on what's at our disposal.
if ( wpforms()->is_pro() && class_exists( 'WPForms_Entry_Handler', false ) ) {
$this->review();
} else {
$this->review_lite();
}
}
/**
* Maybe show review request.
*
* @since 1.3.9
*/
public function review() {
// Fetch total entries.
$entries = wpforms()->entry->get_entries( [ 'number' => 50 ], true );
// Only show review request if the site has collected at least 50 entries.
if ( empty( $entries ) || $entries < 50 ) {
return;
}
ob_start();
// We have a candidate! Output a review message.
?>
<p><?php esc_html_e( 'Hey, I noticed you collected over 50 entries from WPForms - thats awesome! Could you please do me a BIG favor and give it a 5-star rating on WordPress to help us spread the word and boost our motivation?', 'wpforms-lite' ); ?></p>
<p><strong><?php echo wp_kses( __( '~ Syed Balkhi<br>Co-Founder of WPForms', 'wpforms-lite' ), [ 'br' => [] ] ); ?></strong></p>
<p>
<a href="https://wordpress.org/support/plugin/wpforms-lite/reviews/?filter=5#new-post" class="wpforms-notice-dismiss wpforms-review-out" target="_blank" rel="noopener"><?php esc_html_e( 'Ok, you deserve it', 'wpforms-lite' ); ?></a><br>
<a href="#" class="wpforms-notice-dismiss" target="_blank" rel="noopener noreferrer"><?php esc_html_e( 'Nope, maybe later', 'wpforms-lite' ); ?></a><br>
<a href="#" class="wpforms-notice-dismiss" target="_blank" rel="noopener noreferrer"><?php esc_html_e( 'I already did', 'wpforms-lite' ); ?></a>
</p>
<?php
\WPForms\Admin\Notice::info(
ob_get_clean(),
[
'dismiss' => \WPForms\Admin\Notice::DISMISS_GLOBAL,
'slug' => 'review_request',
'autop' => false,
'class' => 'wpforms-review-notice',
]
);
}
/**
* Maybe show Lite review request.
*
* @since 1.3.9
*/
public function review_lite() {
// Fetch when plugin was initially installed.
$activated = get_option( 'wpforms_activated', [] );
if ( ! empty( $activated['lite'] ) ) {
// Only continue if plugin has been installed for at least 14 days.
if ( ( $activated['lite'] + ( DAY_IN_SECONDS * 14 ) ) > time() ) {
return;
}
} else {
$activated['lite'] = time();
update_option( 'wpforms_activated', $activated );
return;
}
// Only proceed with displaying if the user created at least one form.
$form_count = wp_count_posts( 'wpforms' );
if ( empty( $form_count->publish ) ) {
return;
}
// Check if the Constant Contact notice is displaying.
$cc = get_option( 'wpforms_constant_contact', false );
// If it's displaying don't ask for review until they configure CC or
// dismiss the notice.
if ( $cc ) {
return;
}
ob_start();
// We have a candidate! Output a review message.
?>
<p><?php esc_html_e( 'Hey, I noticed you created a contact form with WPForms - thats awesome! Could you please do me a BIG favor and give it a 5-star rating on WordPress to help us spread the word and boost our motivation?', 'wpforms-lite' ); ?></p>
<p><strong><?php echo wp_kses( __( '~ Syed Balkhi<br>Co-Founder of WPForms', 'wpforms-lite' ), [ 'br' => [] ] ); ?></strong></p>
<p>
<a href="https://wordpress.org/support/plugin/wpforms-lite/reviews/?filter=5#new-post" class="wpforms-notice-dismiss wpforms-review-out" target="_blank" rel="noopener noreferrer"><?php esc_html_e( 'Ok, you deserve it', 'wpforms-lite' ); ?></a><br>
<a href="#" class="wpforms-notice-dismiss" target="_blank" rel="noopener noreferrer"><?php esc_html_e( 'Nope, maybe later', 'wpforms-lite' ); ?></a><br>
<a href="#" class="wpforms-notice-dismiss" target="_blank" rel="noopener noreferrer"><?php esc_html_e( 'I already did', 'wpforms-lite' ); ?></a>
</p>
<?php
\WPForms\Admin\Notice::info(
ob_get_clean(),
[
'dismiss' => \WPForms\Admin\Notice::DISMISS_GLOBAL,
'slug' => 'review_lite_request',
'autop' => false,
'class' => 'wpforms-review-notice',
]
);
}
/**
* Dismiss the review admin notice.
*
* @deprecated 1.6.7.1
*
* @since 1.3.2
*/
public function review_dismiss() {
_deprecated_function( __METHOD__, '1.6.7.1 of the WPForms plugin' );
$review = get_option( 'wpforms_review', [] );
$review['time'] = time();
$review['dismissed'] = true;
update_option( 'wpforms_review', $review );
die;
}
/**
* When user is on a WPForms related admin page, display footer text
* that graciously asks them to rate us.
*
* @since 1.3.2
*
* @param string $text Footer text.
*
* @return string
*/
public function admin_footer( $text ) {
global $current_screen;
if ( ! empty( $current_screen->id ) && strpos( $current_screen->id, 'wpforms' ) !== false ) {
$url = 'https://wordpress.org/support/plugin/wpforms-lite/reviews/?filter=5#new-post';
$text = sprintf(
wp_kses( /* translators: $1$s - WPForms plugin name, $2$s - WP.org review link, $3$s - WP.org review link. */
__( 'Please rate %1$s <a href="%2$s" target="_blank" rel="noopener noreferrer">&#9733;&#9733;&#9733;&#9733;&#9733;</a> on <a href="%3$s" target="_blank" rel="noopener">WordPress.org</a> to help us spread the word.', 'wpforms-lite' ),
[
'a' => [
'href' => [],
'target' => [],
'rel' => [],
],
]
),
'<strong>WPForms</strong>',
$url,
$url
);
}
return $text;
}
/**
* Pre-footer promotion block, displayed on all WPForms admin pages except Form Builder.
*
* @since 1.8.0
*/
public function promote_wpforms() {
// Some 3rd-party addons may use page slugs that start with `wpforms-` (e.g. WPForms Views),
// so we should define exact pages we want the footer to be displayed on instead
// of targeting any page that looks like a WPForms page.
$plugin_pages = [
'wpforms-about',
'wpforms-addons',
'wpforms-analytics',
'wpforms-community',
'wpforms-entries',
'wpforms-overview',
'wpforms-payments',
'wpforms-settings',
'wpforms-smtp',
'wpforms-templates',
'wpforms-tools',
];
// phpcs:ignore WordPress.Security.NonceVerification
$current_page = isset( $_REQUEST['page'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['page'] ) ) : '';
if ( ! in_array( $current_page, $plugin_pages, true ) ) {
return;
}
$links = [
[
'url' => wpforms()->is_pro() ?
wpforms_utm_link(
'https://wpforms.com/account/support/',
'Plugin Footer',
'Contact Support'
) : 'https://wordpress.org/support/plugin/wpforms-lite/',
'text' => __( 'Support', 'wpforms-lite' ),
'target' => '_blank',
],
[
'url' => wpforms_utm_link(
'https://wpforms.com/docs/',
'Plugin Footer',
'Plugin Documentation'
),
'text' => __( 'Docs', 'wpforms-lite' ),
'target' => '_blank',
],
[
'url' => 'https://www.facebook.com/groups/wpformsvip/',
'text' => __( 'VIP Circle', 'wpforms-lite' ),
'target' => '_blank',
],
[
'url' => admin_url( 'admin.php?page=wpforms-about' ),
'text' => __( 'Free Plugins', 'wpforms-lite' ),
],
];
echo wpforms_render( // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
'admin/promotion',
[
'title' => __( 'Made with ♥ by the WPForms Team', 'wpforms-lite' ),
'links' => $links,
],
true
);
}
}
new WPForms_Review();

View File

@@ -0,0 +1,753 @@
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
use WPForms\Admin\Notice;
use WPForms\Migrations\Migrations as LiteMigration;
use WPForms\Pro\Migrations\Migrations;
/**
* Settings class.
*
* @since 1.0.0
*/
class WPForms_Settings {
/**
* The current active tab.
*
* @since 1.3.9
*
* @var string
*/
public $view;
/**
* Primary class constructor.
*
* @since 1.0.0
*/
public function __construct() {
$this->hooks();
}
/**
* Hooks.
*
* @since 1.8.5.4
*/
private function hooks() {
// Maybe load settings page.
add_action( 'admin_init', [ $this, 'init' ] );
}
/**
* Determine if the user is viewing the settings page, if so, party on.
*
* @since 1.0.0
*/
public function init() { // phpcs:ignore WPForms.PHP.HooksMethod.InvalidPlaceForAddingHooks
// Only load if we are actually on the settings page.
if ( ! wpforms_is_admin_page( 'settings' ) ) {
return;
}
// Include API callbacks and functions.
require_once WPFORMS_PLUGIN_DIR . 'includes/admin/settings-api.php';
// Show downgraded notice.
$this->maybe_display_downgraded_notice();
// Watch for triggered save.
$this->save_settings();
// Determine the current active settings tab.
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
$this->view = isset( $_GET['view'] ) ? sanitize_key( wp_unslash( $_GET['view'] ) ) : 'general';
$this->modify_url();
add_action( 'admin_enqueue_scripts', [ $this, 'enqueues' ] );
add_action( 'wpforms_admin_page', [ $this, 'output' ] );
// Monitor custom tables.
$this->monitor_custom_tables();
// Hook for addons.
do_action( 'wpforms_settings_init', $this );
}
/**
* Remove `wpforms-integration` query arg from URL.
* The `wpforms-integration` query arg is used to highlight a specific provider on the Integrations page.
*
* @since 1.8.5.4
*/
private function modify_url() {
if ( $this->view !== 'integrations' ) {
return;
}
$_SERVER['REQUEST_URI'] = remove_query_arg( 'wpforms-integration' );
}
/**
* Display admin notice about using a downgraded version of WPForms.
*
* @since 1.8.5.4
*/
private function maybe_display_downgraded_notice() {
if ( ! $this->is_downgraded_version() ) {
return;
}
$notice = sprintf(
wp_kses( /* translators: %1$s - WPForms.com doc page URL; %2$s - button text. */
__(
'It looks like you\'ve downgraded to an older version of WPForms. We recommend always using the latest version as some features may not function as expected in older versions. <a href="%1$s" target="_blank" rel="noopener">%2$s</a>',
'wpforms-lite'
),
[
'a' => [
'href' => [],
'target' => [],
'rel' => [],
],
]
),
esc_url( wpforms_utm_link( 'https://wpforms.com/docs/why-you-should-always-use-the-latest-version-of-wpforms/', 'Settings', 'Downgrade notice' ) ),
esc_html__( 'Learn More', 'wpforms-lite' )
);
Notice::warning(
$notice,
[
'dismiss' => Notice::DISMISS_GLOBAL,
'slug' => 'wpforms_is_downgraded',
]
);
}
/**
* Check if plugin was downgraded.
*
* @since 1.8.5.4
*
* @return bool
*/
private function is_downgraded_version(): bool {
// Get all installed versions.
$installed_versions = wpforms()->is_pro() ? (array) get_option( Migrations::MIGRATED_OPTION_NAME, [] ) : (array) get_option( LiteMigration::MIGRATED_OPTION_NAME, [] );
// Get the most recent installed version.
$db_latest = array_keys( $installed_versions )[ count( $installed_versions ) - 1 ];
// Check if downgrade happened.
return version_compare( $db_latest, WPFORMS_VERSION, '>' );
}
/**
* Sanitize and save settings.
*
* @since 1.3.9
*/
public function save_settings() { // phpcs:ignore Generic.Metrics.CyclomaticComplexity.MaxExceeded, Generic.Metrics.NestingLevel.MaxExceeded
// Check nonce and other various security checks.
if ( ! isset( $_POST['wpforms-settings-submit'] ) || empty( $_POST['nonce'] ) ) {
return;
}
if ( ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['nonce'] ) ), 'wpforms-settings-nonce' ) ) {
return;
}
if ( ! wpforms_current_user_can() ) {
return;
}
if ( empty( $_POST['view'] ) ) {
return;
}
$current_view = sanitize_key( $_POST['view'] );
// Get registered fields and current settings.
$fields = $this->get_registered_settings( $current_view );
$settings = get_option( 'wpforms_settings', [] );
// Views excluded from saving list.
$exclude_views = apply_filters( 'wpforms_settings_exclude_view', [], $fields, $settings );
if ( is_array( $exclude_views ) && in_array( $current_view, $exclude_views, true ) ) {
// Run a custom save processing for excluded views.
do_action( 'wpforms_settings_custom_process', $current_view, $fields, $settings );
return;
}
if ( empty( $fields ) || ! is_array( $fields ) ) {
return;
}
// Sanitize and prep each field.
foreach ( $fields as $id => $field ) {
// Certain field types are not valid for saving and are skipped.
$exclude = apply_filters( 'wpforms_settings_exclude_type', [ 'content', 'license', 'providers' ] );
if ( empty( $field['type'] ) || in_array( $field['type'], $exclude, true ) ) {
continue;
}
// phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
$value = isset( $_POST[ $id ] ) ? wp_unslash( $_POST[ $id ] ) : false;
$value_prev = isset( $settings[ $id ] ) ? $settings[ $id ] : false;
// Trim all string values.
if ( is_string( $value ) ) {
$value = trim( $value );
}
// Custom filter can be provided for sanitizing, otherwise use defaults.
if ( ! empty( $field['filter'] ) && is_callable( $field['filter'] ) ) {
$value = call_user_func( $field['filter'], $value, $id, $field, $value_prev );
} else {
switch ( $field['type'] ) {
case 'checkbox':
case 'toggle':
$value = (bool) $value;
break;
case 'image':
$value = esc_url_raw( $value );
break;
case 'color':
$value = wpforms_sanitize_hex_color( $value );
break;
case 'color_scheme':
$value = array_map( 'wpforms_sanitize_hex_color', $value );
break;
case 'number':
$value = (float) $value;
break;
case 'radio':
case 'select':
$value = $this->validate_field_with_options( $field, $value, $value_prev );
break;
case 'text':
default:
$value = sanitize_text_field( $value );
break;
}
}
// Add to settings.
$settings[ $id ] = $value;
}
// Save settings.
wpforms_update_settings( $settings );
Notice::success( esc_html__( 'Settings were successfully saved.', 'wpforms-lite' ) );
}
/**
* Enqueue assets for the settings page.
*
* @since 1.0.0
*/
public function enqueues() {
do_action( 'wpforms_settings_enqueue' );
}
/**
* Return registered settings tabs.
*
* @since 1.3.9
*
* @return array
*/
public function get_tabs() {
$tabs = [
'general' => [
'name' => esc_html__( 'General', 'wpforms-lite' ),
'form' => true,
'submit' => esc_html__( 'Save Settings', 'wpforms-lite' ),
],
'validation' => [
'name' => esc_html__( 'Validation', 'wpforms-lite' ),
'form' => true,
'submit' => esc_html__( 'Save Settings', 'wpforms-lite' ),
],
'integrations' => [
'name' => esc_html__( 'Integrations', 'wpforms-lite' ),
'form' => false,
'submit' => false,
],
'geolocation' => [
'name' => esc_html__( 'Geolocation', 'wpforms-lite' ),
'form' => false,
'submit' => false,
],
'misc' => [
'name' => esc_html__( 'Misc', 'wpforms-lite' ),
'form' => true,
'submit' => esc_html__( 'Save Settings', 'wpforms-lite' ),
],
];
return apply_filters( 'wpforms_settings_tabs', $tabs );
}
/**
* Output tab navigation area.
*
* @since 1.3.9
*/
public function tabs() {
$tabs = $this->get_tabs();
echo '<ul class="wpforms-admin-tabs">';
foreach ( $tabs as $id => $tab ) {
$active = $id === $this->view ? 'active' : '';
$link = add_query_arg( 'view', $id, admin_url( 'admin.php?page=wpforms-settings' ) );
echo '<li><a href="' . esc_url_raw( $link ) . '" class="' . esc_attr( $active ) . '">' . esc_html( $tab['name'] ) . '</a></li>';
}
echo '</ul>';
}
/**
* Return all the default registered settings fields.
*
* @since 1.3.9
*
* @param string $view The current view (tab) on Settings page.
*
* @return array
*/
public function get_registered_settings( $view = '' ) {
$defaults = [
// General Settings tab.
'general' => [
'license-heading' => [
'id' => 'license-heading',
'content' => '<h4>' . esc_html__( 'License', 'wpforms-lite' ) . '</h4><p>' . esc_html__( 'Your license key provides access to updates and addons.', 'wpforms-lite' ) . '</p>',
'type' => 'content',
'no_label' => true,
'class' => [ 'section-heading' ],
],
'license-key' => [
'id' => 'license-key',
'name' => esc_html__( 'License Key', 'wpforms-lite' ),
'type' => 'license',
],
'general-heading' => [
'id' => 'general-heading',
'content' => '<h4>' . esc_html__( 'General', 'wpforms-lite' ) . '</h4>',
'type' => 'content',
'no_label' => true,
'class' => [ 'section-heading', 'no-desc' ],
],
'disable-css' => [
'id' => 'disable-css',
'name' => esc_html__( 'Include Form Styling', 'wpforms-lite' ),
'desc' => sprintf(
wp_kses( /* translators: %s - WPForms.com form styling setting URL. */
__( 'Determines which CSS files to load and use for the site. "Base and Form Theme Styling" is recommended, unless you are experienced with CSS or instructed by support to change settings. <a href="%s" target="_blank" rel="noopener noreferrer" class="wpforms-learn-more">Learn More</a>', 'wpforms-lite' ),
[
'a' => [
'href' => [],
'target' => [],
'rel' => [],
'class' => [],
],
]
),
esc_url( wpforms_utm_link( 'https://wpforms.com/docs/how-to-choose-an-include-form-styling-setting/', 'settings-license', 'Form Styling Documentation' ) )
),
'type' => 'select',
'choicesjs' => true,
'default' => 1,
'options' => [
1 => esc_html__( 'Base and form theme styling', 'wpforms-lite' ),
2 => esc_html__( 'Base styling only', 'wpforms-lite' ),
3 => esc_html__( 'No styling', 'wpforms-lite' ),
],
],
'global-assets' => [
'id' => 'global-assets',
'name' => esc_html__( 'Load Assets Globally', 'wpforms-lite' ),
'desc' => esc_html__( 'Load WPForms assets site-wide. Only check if your site is having compatibility issues or instructed to by support.', 'wpforms-lite' ),
'type' => 'toggle',
'status' => true,
],
'gdpr-heading' => [
'id' => 'GDPR',
'content' => '<h4>' . esc_html__( 'GDPR', 'wpforms-lite' ) . '</h4>',
'type' => 'content',
'no_label' => true,
'class' => [ 'section-heading', 'no-desc' ],
],
'gdpr' => [
'id' => 'gdpr',
'name' => esc_html__( 'GDPR Enhancements', 'wpforms-lite' ),
'desc' => sprintf(
wp_kses( /* translators: %s - WPForms.com GDPR documentation URL. */
__( 'Enable GDPR related features and enhancements. <a href="%s" target="_blank" rel="noopener noreferrer" class="wpforms-learn-more">Learn More</a>', 'wpforms-lite' ),
[
'a' => [
'href' => [],
'target' => [],
'rel' => [],
'class' => [],
],
]
),
esc_url( wpforms_utm_link( 'https://wpforms.com/docs/how-to-create-gdpr-compliant-forms/', 'settings-license', 'GDPR Documentation' ) )
),
'type' => 'toggle',
'status' => true,
],
],
// Validation messages settings tab.
'validation' => [
'validation-heading' => [
'id' => 'validation-heading',
'content' => sprintf( /* translators: %s - WPForms.com smart tags documentation URL. */
esc_html__( '%1$s These messages are displayed to the users as they fill out a form in real-time. Messages can include plain text and/or %2$sSmart Tags%3$s.', 'wpforms-lite' ),
'<h4>' . esc_html__( 'Validation Messages', 'wpforms-lite' )
. '</h4><p>',
'<a href="' . esc_url( wpforms_utm_link( 'https://wpforms.com/docs/how-to-use-smart-tags-in-wpforms/#smart-tags', 'Settings - Validation', 'Smart Tag Documentation' ) ) . '" target="_blank" rel="noopener noreferrer">',
'</a>'
),
'type' => 'content',
'no_label' => true,
'class' => [ 'section-heading' ],
],
'validation-required' => [
'id' => 'validation-required',
'name' => esc_html__( 'Required', 'wpforms-lite' ),
'type' => 'text',
'default' => esc_html__( 'This field is required.', 'wpforms-lite' ),
],
'validation-email' => [
'id' => 'validation-email',
'name' => esc_html__( 'Email', 'wpforms-lite' ),
'type' => 'text',
'default' => esc_html__( 'Please enter a valid email address.', 'wpforms-lite' ),
],
'validation-email-suggestion' => [
'id' => 'validation-email-suggestion',
'name' => esc_html__( 'Email Suggestion', 'wpforms-lite' ),
'type' => 'text',
'default' => sprintf( /* translators: %s - suggested email address. */
esc_html__( 'Did you mean %s?', 'wpforms-lite' ),
'{suggestion}'
),
],
'validation-email-restricted' => [
'id' => 'validation-email-restricted',
'name' => esc_html__( 'Email Restricted', 'wpforms-lite' ),
'type' => 'text',
'default' => esc_html__( 'This email address is not allowed.', 'wpforms-lite' ),
],
'validation-number' => [
'id' => 'validation-number',
'name' => esc_html__( 'Number', 'wpforms-lite' ),
'type' => 'text',
'default' => esc_html__( 'Please enter a valid number.', 'wpforms-lite' ),
],
'validation-number-positive' => [
'id' => 'validation-number-positive',
'name' => esc_html__( 'Number Positive', 'wpforms-lite' ),
'type' => 'text',
'default' => esc_html__( 'Please enter a valid positive number.', 'wpforms-lite' ),
],
'validation-minimum-price' => [
'id' => 'validation-minimum-price',
'name' => esc_html__( 'Minimum Price', 'wpforms-lite' ),
'type' => 'text',
'default' => esc_html__( 'Amount entered is less than the required minimum.', 'wpforms-lite' ),
],
'validation-confirm' => [
'id' => 'validation-confirm',
'name' => esc_html__( 'Confirm Value', 'wpforms-lite' ),
'type' => 'text',
'default' => esc_html__( 'Field values do not match.', 'wpforms-lite' ),
],
'validation-inputmask-incomplete' => [
'id' => 'validation-inputmask-incomplete',
'name' => esc_html__( 'Input Mask Incomplete', 'wpforms-lite' ),
'type' => 'text',
'default' => esc_html__( 'Please fill out the field in required format.', 'wpforms-lite' ),
],
'validation-check-limit' => [
'id' => 'validation-check-limit',
'name' => esc_html__( 'Checkbox Selection Limit', 'wpforms-lite' ),
'type' => 'text',
'default' => esc_html__( 'You have exceeded the number of allowed selections: {#}.', 'wpforms-lite' ),
],
'validation-character-limit' => [
'id' => 'validation-character-limit',
'name' => esc_html__( 'Character Limit', 'wpforms-lite' ),
'type' => 'text',
'default' => sprintf( /* translators: %1$s - characters limit, %2$s - number of characters left. */
esc_html__( 'Limit is %1$s characters. Characters remaining: %2$s.', 'wpforms-lite' ),
'{limit}',
'{remaining}'
),
],
'validation-word-limit' => [
'id' => 'validation-word-limit',
'name' => esc_html__( 'Word Limit', 'wpforms-lite' ),
'type' => 'text',
'default' => sprintf( /* translators: %1$s - words limit, %2$s - number of words left. */
esc_html__( 'Limit is %1$s words. Words remaining: %2$s.', 'wpforms-lite' ),
'{limit}',
'{remaining}'
),
],
],
// Provider integrations settings tab.
'integrations' => [
'integrations-heading' => [
'id' => 'integrations-heading',
'content' => '<h4>' . esc_html__( 'Integrations', 'wpforms-lite' ) . '</h4><p>' . esc_html__( 'Manage integrations with popular providers such as Constant Contact, Mailchimp, Zapier, and more.', 'wpforms-lite' ) . '</p>',
'type' => 'content',
'no_label' => true,
'class' => [ 'section-heading' ],
],
'integrations-providers' => [
'id' => 'integrations-providers',
'content' => '<h4>' . esc_html__( 'Integrations', 'wpforms-lite' ) . '</h4><p>' . esc_html__( 'Manage integrations with popular providers such as Constant Contact, Mailchimp, Zapier, and more.', 'wpforms-lite' ) . '</p>',
'type' => 'providers',
'wrap' => 'none',
],
],
// Misc. settings tab.
'misc' => [
'misc-heading' => [
'id' => 'misc-heading',
'content' => '<h4>' . esc_html__( 'Miscellaneous', 'wpforms-lite' ) . '</h4>',
'type' => 'content',
'no_label' => true,
'class' => [ 'section-heading', 'no-desc' ],
],
'hide-announcements' => [
'id' => 'hide-announcements',
'name' => esc_html__( 'Hide Announcements', 'wpforms-lite' ),
'desc' => esc_html__( 'Hide plugin announcements and update details.', 'wpforms-lite' ),
'type' => 'toggle',
'status' => true,
],
'hide-admin-bar' => [
'id' => 'hide-admin-bar',
'name' => esc_html__( 'Hide Admin Bar Menu', 'wpforms-lite' ),
'desc' => esc_html__( 'Hide the WPForms admin bar menu.', 'wpforms-lite' ),
'type' => 'toggle',
'status' => true,
],
'uninstall-data' => [
'id' => 'uninstall-data',
'name' => esc_html__( 'Uninstall WPForms', 'wpforms-lite' ),
'desc' => $this->get_uninstall_desc(),
'type' => 'toggle',
'status' => true,
],
],
];
$defaults = apply_filters( 'wpforms_settings_defaults', $defaults );
// Take care of invalid views.
if ( ! empty( $view ) && ! array_key_exists( $view, $defaults ) ) {
$this->view = key( $defaults );
return reset( $defaults );
}
return empty( $view ) ? $defaults : $defaults[ $view ];
}
/**
* Get uninstall description.
*
* @since 1.8.4
*
* @return string
*/
private function get_uninstall_desc() {
$desc = esc_html__( 'Remove ALL WPForms data upon plugin deletion.', 'wpforms-lite' );
$warning = esc_html__( 'All forms and settings will be unrecoverable.', 'wpforms-lite' );
if ( wpforms()->is_pro() ) {
$desc = esc_html__( 'Remove ALL WPForms data upon plugin deletion.', 'wpforms-lite' );
$warning = esc_html__( 'All forms, entries, and uploaded files will be unrecoverable.', 'wpforms-lite' );
}
return sprintf( '%s <span class="wpforms-settings-warning">%s</span>', $desc, $warning );
}
/**
* Return array containing markup for all the appropriate settings fields.
*
* @since 1.3.9
*
* @param string $view View slug.
*
* @return array
*/
public function get_settings_fields( $view = '' ) {
$fields = [];
$settings = $this->get_registered_settings( $view );
foreach ( $settings as $id => $args ) {
$fields[ $id ] = wpforms_settings_output_field( $args );
}
return apply_filters( 'wpforms_settings_fields', $fields, $view );
}
/**
* Build the output for the plugin settings page.
*
* @since 1.0.0
*/
public function output() {
$tabs = $this->get_tabs();
$fields = $this->get_settings_fields( $this->view );
?>
<div id="wpforms-settings" class="wrap wpforms-admin-wrap">
<?php $this->tabs(); ?>
<h1 class="wpforms-h1-placeholder"></h1>
<?php
if ( wpforms()->is_pro() && class_exists( 'WPForms_License', false ) ) {
wpforms()->get( 'license' )->notices( true );
}
?>
<div class="wpforms-admin-content wpforms-admin-settings wpforms-admin-content-<?php echo esc_attr( $this->view ); ?> wpforms-admin-settings-<?php echo esc_attr( $this->view ); ?>">
<?php
// Some tabs rely on AJAX and do not contain a form, such as Integrations.
if ( ! empty( $tabs[ $this->view ]['form'] ) ) :
?>
<form class="wpforms-admin-settings-form" method="post">
<input type="hidden" name="action" value="update-settings">
<input type="hidden" name="view" value="<?php echo esc_attr( $this->view ); ?>">
<input type="hidden" name="nonce" value="<?php echo esc_attr( wp_create_nonce( 'wpforms-settings-nonce' ) ); ?>">
<?php endif; ?>
<?php do_action( 'wpforms_admin_settings_before', $this->view, $fields ); ?>
<?php
foreach ( $fields as $field ) {
echo $field; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
?>
<?php if ( ! empty( $tabs[ $this->view ]['submit'] ) ) : ?>
<p class="submit">
<button type="submit" class="wpforms-btn wpforms-btn-md wpforms-btn-orange" name="wpforms-settings-submit">
<?php
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
echo $tabs[ $this->view ]['submit'];
?>
</button>
</p>
<?php endif; ?>
<?php do_action( 'wpforms_admin_settings_after', $this->view, $fields ); ?>
<?php if ( ! empty( $tabs[ $this->view ]['form'] ) ) : ?>
</form>
<?php endif; ?>
</div>
</div>
<?php
}
/**
* Monitor that all custom tables exist and recreate if missing.
* This logic works on Settings > General page only.
*
* @since 1.6.2
*/
public function monitor_custom_tables() {
// Proceed on Settings plugin admin area page only.
if ( $this->view !== 'general' ) {
return;
}
/*
* Tasks Meta table.
*/
$meta = new \WPForms\Tasks\Meta();
if ( $meta->table_exists() ) {
return;
}
$meta->create_table();
}
/**
* Validate radio and select fields.
*
* @since 1.7.5.5
*
* @param array $field Field.
* @param mixed $value Value.
* @param mixed $value_prev Previous value.
*
* @return mixed
*/
private function validate_field_with_options( $field, $value, $value_prev ) {
$value = sanitize_text_field( $value );
if ( isset( $field['options'] ) && array_key_exists( $value, $field['options'] ) ) {
return $value;
}
return isset( $field['default'] ) ? $field['default'] : $value_prev;
}
}
new WPForms_Settings();

View File

@@ -0,0 +1,354 @@
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Welcome page class.
*
* This page is shown when the plugin is activated.
*
* @since 1.0.0
*/
class WPForms_Welcome {
/**
* Hidden welcome page slug.
*
* @since 1.5.6
*/
const SLUG = 'wpforms-getting-started';
/**
* Primary class constructor.
*
* @since 1.0.0
*/
public function __construct() {
add_action( 'plugins_loaded', [ $this, 'hooks' ] );
}
/**
* Register all WP hooks.
*
* @since 1.5.6
*/
public function hooks() {
// If user is in admin ajax or doing cron, return.
if ( wp_doing_ajax() || wp_doing_cron() ) {
return;
}
// If user cannot manage_options, return.
if ( ! wpforms_current_user_can() ) {
return;
}
add_action( 'admin_menu', [ $this, 'register' ] );
add_action( 'admin_head', [ $this, 'hide_menu' ] );
add_action( 'admin_init', [ $this, 'redirect' ], 9999 );
}
/**
* Register the pages to be used for the Welcome screen (and tabs).
*
* These pages will be removed from the Dashboard menu, so they will
* not actually show. Sneaky, sneaky.
*
* @since 1.0.0
*/
public function register() {
// Getting started - shows after installation.
add_dashboard_page(
esc_html__( 'Welcome to WPForms', 'wpforms-lite' ),
esc_html__( 'Welcome to WPForms', 'wpforms-lite' ),
apply_filters( 'wpforms_welcome_cap', wpforms_get_capability_manage_options() ),
self::SLUG,
[ $this, 'output' ]
);
}
/**
* Removed the dashboard pages from the admin menu.
*
* This means the pages are still available to us, but hidden.
*
* @since 1.0.0
*/
public function hide_menu() {
remove_submenu_page( 'index.php', self::SLUG );
}
/**
* Welcome screen redirect.
*
* This function checks if a new install or update has just occurred. If so,
* then we redirect the user to the appropriate page.
*
* @since 1.0.0
*/
public function redirect() {
// Check if we should consider redirection.
if ( ! get_transient( 'wpforms_activation_redirect' ) ) {
return;
}
// If we are redirecting, clear the transient so it only happens once.
delete_transient( 'wpforms_activation_redirect' );
// Check option to disable welcome redirect.
if ( get_option( 'wpforms_activation_redirect', false ) ) {
return;
}
// Only do this for single site installs.
if ( isset( $_GET['activate-multi'] ) || is_network_admin() ) { // WPCS: CSRF ok.
return;
}
// Check if this is an update or first install.
$upgrade = get_option( 'wpforms_version_upgraded_from' );
if ( ! $upgrade ) {
// Initial install.
wp_safe_redirect( admin_url( 'index.php?page=' . self::SLUG ) );
exit;
}
}
/**
* Getting Started screen. Shows after first install.
*
* @since 1.0.0
*/
public function output() {
$class = wpforms()->is_pro() ? 'pro' : 'lite';
?>
<div id="wpforms-welcome" class="<?php echo sanitize_html_class( $class ); ?>">
<div class="container">
<div class="intro">
<div class="sullie">
<img src="<?php echo WPFORMS_PLUGIN_URL; ?>assets/images/sullie.png" alt="<?php esc_attr_e( 'Sullie the WPForms mascot', 'wpforms-lite' ); ?>">
</div>
<div class="block">
<h1><?php esc_html_e( 'Welcome to WPForms', 'wpforms-lite' ); ?></h1>
<h6><?php esc_html_e( 'Thank you for choosing WPForms - the most powerful drag & drop WordPress form builder in the market.', 'wpforms-lite' ); ?></h6>
</div>
<a href="#" class="play-video" title="<?php esc_attr_e( 'Watch how to create your first form', 'wpforms-lite' ); ?>">
<img src="<?php echo WPFORMS_PLUGIN_URL; ?>assets/images/welcome-video.png" alt="<?php esc_attr_e( 'Watch how to create your first form', 'wpforms-lite' ); ?>" class="video-thumbnail">
</a>
<div class="block">
<h6><?php esc_html_e( 'WPForms makes it easy to create forms in WordPress. You can watch the video tutorial or read our guide on how create your first form.', 'wpforms-lite' ); ?></h6>
<div class="button-wrap wpforms-clear">
<div class="left">
<a href="<?php echo esc_url( admin_url( 'admin.php?page=wpforms-builder' ) ); ?>" class="wpforms-btn wpforms-btn-block wpforms-btn-lg wpforms-btn-orange">
<?php esc_html_e( 'Create Your First Form', 'wpforms-lite' ); ?>
</a>
</div>
<div class="right">
<a href="<?php echo esc_url( wpforms_utm_link( 'https://wpforms.com/docs/creating-first-form/', 'welcome-page', 'Read the Full Guide' ) ); ?>"
class="wpforms-btn wpforms-btn-block wpforms-btn-lg wpforms-btn-grey" target="_blank" rel="noopener noreferrer">
<?php esc_html_e( 'Read the Full Guide', 'wpforms-lite' ); ?>
</a>
</div>
</div>
</div>
</div><!-- /.intro -->
<?php do_action( 'wpforms_welcome_intro_after' ); ?>
<div class="features">
<div class="block">
<h1><?php esc_html_e( 'WPForms Features &amp; Addons', 'wpforms-lite' ); ?></h1>
<h6><?php esc_html_e( 'WPForms is both easy to use and extremely powerful. We have tons of helpful features that allow us to give you everything you need from a form builder.', 'wpforms-lite' ); ?></h6>
<div class="feature-list wpforms-clear">
<div class="feature-block first">
<img src="<?php echo WPFORMS_PLUGIN_URL; ?>assets/images/welcome-feature-icon-1.png">
<h5><?php esc_html_e( 'Drag &amp; Drop Form Builder', 'wpforms-lite' ); ?></h5>
<p><?php esc_html_e( 'Easily create an amazing form in just a few minutes without writing any code.', 'wpforms-lite' ); ?></p>
</div>
<div class="feature-block last">
<img src="<?php echo WPFORMS_PLUGIN_URL; ?>assets/images/welcome-feature-icon-2.png">
<h5><?php esc_html_e( 'Form Templates', 'wpforms-lite' ); ?></h5>
<p><?php esc_html_e( 'Start with pre-built form templates to save even more time.', 'wpforms-lite' ); ?></p>
</div>
<div class="feature-block first">
<img src="<?php echo WPFORMS_PLUGIN_URL; ?>assets/images/welcome-feature-icon-3.png">
<h5><?php esc_html_e( 'Responsive Mobile Friendly', 'wpforms-lite' ); ?></h5>
<p><?php esc_html_e( 'WPForms is 100% responsive meaning it works on mobile, tablets & desktop.', 'wpforms-lite' ); ?></p>
</div>
<div class="feature-block last">
<img src="<?php echo WPFORMS_PLUGIN_URL; ?>assets/images/welcome-feature-icon-4.png">
<h5><?php esc_html_e( 'Smart Conditional Logic', 'wpforms-lite' ); ?></h5>
<p><?php esc_html_e( 'Easily create high performance forms with our smart conditional logic.', 'wpforms-lite' ); ?></p>
</div>
<div class="feature-block first">
<img src="<?php echo WPFORMS_PLUGIN_URL; ?>assets/images/welcome-feature-icon-5.png">
<h5><?php esc_html_e( 'Instant Notifications', 'wpforms-lite' ); ?></h5>
<p><?php esc_html_e( 'Respond to leads quickly with our instant form notification feature for your team.', 'wpforms-lite' ); ?></p>
</div>
<div class="feature-block last">
<img src="<?php echo WPFORMS_PLUGIN_URL; ?>assets/images/welcome-feature-icon-6.png">
<h5><?php esc_html_e( 'Entry Management', 'wpforms-lite' ); ?></h5>
<p><?php esc_html_e( 'View all your leads in one place to streamline your workflow.', 'wpforms-lite' ); ?></p>
</div>
<div class="feature-block first">
<img src="<?php echo WPFORMS_PLUGIN_URL; ?>assets/images/welcome-feature-icon-7.png">
<h5><?php esc_html_e( 'Payments Made Easy', 'wpforms-lite' ); ?></h5>
<p><?php esc_html_e( 'Easily collect payments, donations, and online orders without hiring a developer.', 'wpforms-lite' ); ?></p>
</div>
<div class="feature-block last">
<img src="<?php echo WPFORMS_PLUGIN_URL; ?>assets/images/welcome-feature-icon-8.png">
<h5><?php esc_html_e( 'Marketing &amp; Subscriptions', 'wpforms-lite' ); ?></h5>
<p><?php esc_html_e( 'Create subscription forms and connect with your email marketing service.', 'wpforms-lite' ); ?></p>
</div>
<div class="feature-block first">
<img src="<?php echo WPFORMS_PLUGIN_URL; ?>assets/images/welcome-feature-icon-9.png">
<h5><?php esc_html_e( 'Easy to Embed', 'wpforms-lite' ); ?></h5>
<p><?php esc_html_e( 'Easily embed your forms in blog posts, pages, sidebar widgets, footer, etc.', 'wpforms-lite' ); ?></p>
</div>
<div class="feature-block last">
<img src="<?php echo WPFORMS_PLUGIN_URL; ?>assets/images/welcome-feature-icon-10.png">
<h5><?php esc_html_e( 'Spam Protection', 'wpforms-lite' ); ?></h5>
<p><?php esc_html_e( 'Our smart captcha and spam protection automatically prevents spam submissions.', 'wpforms-lite' ); ?></p>
</div>
</div>
<div class="button-wrap">
<a href="<?php echo esc_url( wpforms_utm_link( 'https://wpforms.com/features/', 'welcome-page', 'See All Features' ) ); ?>"
class="wpforms-btn wpforms-btn-lg wpforms-btn-grey" rel="noopener noreferrer" target="_blank">
<?php esc_html_e( 'See All Features', 'wpforms-lite' ); ?>
</a>
</div>
</div>
</div><!-- /.features -->
<div class="upgrade-cta upgrade">
<div class="block wpforms-clear">
<div class="left">
<h2><?php esc_html_e( 'Upgrade to PRO', 'wpforms-lite' ); ?></h2>
<ul>
<li><span class="dashicons dashicons-yes"></span> <?php esc_html_e( 'Advanced Fields', 'wpforms-lite' ); ?></li>
<li><span class="dashicons dashicons-yes"></span> <?php esc_html_e( 'Conditional Logic', 'wpforms-lite' ); ?></li>
<li><span class="dashicons dashicons-yes"></span> <?php esc_html_e( 'Payment Forms', 'wpforms-lite' ); ?></li>
<li><span class="dashicons dashicons-yes"></span> <?php esc_html_e( 'Surveys & Polls', 'wpforms-lite' ); ?></li>
<li><span class="dashicons dashicons-yes"></span> <?php esc_html_e( 'Signatures', 'wpforms-lite' ); ?></li>
<li><span class="dashicons dashicons-yes"></span> <?php esc_html_e( 'Form Abandonment', 'wpforms-lite' ); ?></li>
<li><span class="dashicons dashicons-yes"></span> <?php esc_html_e( 'Entry Management', 'wpforms-lite' ); ?></li>
<li><span class="dashicons dashicons-yes"></span> <?php esc_html_e( 'File Uploads', 'wpforms-lite' ); ?></li>
<li><span class="dashicons dashicons-yes"></span> <?php esc_html_e( 'Geolocation', 'wpforms-lite' ); ?></li>
<li><span class="dashicons dashicons-yes"></span> <?php esc_html_e( 'Conversational Forms', 'wpforms-lite' ); ?></li>
<li><span class="dashicons dashicons-yes"></span> <?php esc_html_e( 'User Registration', 'wpforms-lite' ); ?></li>
<li><span class="dashicons dashicons-yes"></span> <?php esc_html_e( 'Marketing Integrations', 'wpforms-lite' ); ?></li>
</ul>
</div>
<div class="right">
<h2><span>PRO</span></h2>
<div class="price">
<span class="amount">199</span><br>
<span class="term"><?php esc_html_e( 'per year', 'wpforms-lite' ); ?></span>
</div>
<a href="<?php echo esc_url( wpforms_admin_upgrade_link( 'welcome', 'Upgrade Now CTA Section' ) ); ?>" rel="noopener noreferrer" target="_blank"
class="wpforms-btn wpforms-btn-block wpforms-btn-lg wpforms-btn-orange wpforms-upgrade-modal">
<?php esc_html_e( 'Upgrade Now', 'wpforms-lite' ); ?>
</a>
</div>
</div>
</div>
<div class="testimonials upgrade">
<div class="block">
<h1><?php esc_html_e( 'Testimonials', 'wpforms-lite' ); ?></h1>
<div class="testimonial-block wpforms-clear">
<img src="<?php echo WPFORMS_PLUGIN_URL; ?>assets/images/welcome-testimonial-bill.jpg">
<p><?php esc_html_e( 'WPForms is by far the easiest form plugin to use. My clients love it its one of the few plugins they can use without any training. As a developer I appreciate how fast, modern, clean and extensible it is.', 'wpforms-lite' ); ?>
<p>
<p><strong>Bill Erickson</strong>, Erickson Web Consulting</p>
</div>
<div class="testimonial-block wpforms-clear">
<img src="<?php echo WPFORMS_PLUGIN_URL; ?>assets/images/welcome-testimonial-david.jpg">
<p><?php esc_html_e( 'As a business owner, time is my most valuable asset. WPForms allow me to create smart online forms with just a few clicks. With their pre-built form templates and the drag & drop builder, I can create a new form that works in less than 2 minutes without writing a single line of code. Well worth the investment.', 'wpforms-lite' ); ?>
<p>
<p><strong>David Henzel</strong>, MaxCDN</p>
</div>
</div>
</div><!-- /.testimonials -->
<div class="footer">
<div class="block wpforms-clear">
<div class="button-wrap wpforms-clear">
<div class="left">
<a href="<?php echo esc_url( admin_url( 'admin.php?page=wpforms-builder' ) ); ?>"
class="wpforms-btn wpforms-btn-block wpforms-btn-lg wpforms-btn-orange">
<?php esc_html_e( 'Create Your First Form', 'wpforms-lite' ); ?>
</a>
</div>
<div class="right">
<a href="<?php echo esc_url( wpforms_admin_upgrade_link( 'welcome', 'Upgrade to WPForms Pro' ) ); ?>" target="_blank" rel="noopener noreferrer"
class="wpforms-btn wpforms-btn-block wpforms-btn-lg wpforms-btn-trans-green wpforms-upgrade-modal">
<span class="underline">
<?php esc_html_e( 'Upgrade to WPForms Pro', 'wpforms-lite' ); ?> <span class="dashicons dashicons-arrow-right"></span>
</span>
</a>
</div>
</div>
</div>
</div><!-- /.footer -->
</div><!-- /.container -->
</div><!-- /#wpforms-welcome -->
<?php
}
}
new WPForms_Welcome();

View File

@@ -0,0 +1,600 @@
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
use WPForms\Forms\Locator;
use WPForms\Integrations\LiteConnect\LiteConnect;
use WPForms\Integrations\LiteConnect\Integration as LiteConnectIntegration;
/**
* Generate the table on the plugin overview page.
*
* @since 1.0.0
*/
class WPForms_Overview_Table extends WP_List_Table {
/**
* Number of forms to show per page.
*
* @since 1.0.0
*
* @var int
*/
public $per_page;
/**
* Number of forms in different views.
*
* @since 1.7.2
*
* @var array
*/
private $count;
/**
* Current view.
*
* @since 1.7.3
*
* @var string
*/
private $view;
/**
* Primary class constructor.
*
* @since 1.0.0
*/
public function __construct() {
// Utilize the parent constructor to build the main class properties.
parent::__construct(
[
'singular' => 'form',
'plural' => 'forms',
'ajax' => false,
]
);
add_filter( 'default_hidden_columns', [ $this, 'default_hidden_columns' ], 10, 2 );
// Determine the current view.
$this->view = wpforms()->get( 'forms_views' )->get_current_view();
// Default number of forms to show per page.
$this->per_page = (int) apply_filters( 'wpforms_overview_per_page', 20 );
}
/**
* Get the instance of a class and store it in itself.
*
* @since 1.7.5
*/
public static function get_instance() {
static $instance;
if ( ! $instance ) {
$instance = new self();
}
return $instance;
}
/**
* Retrieve the table columns.
*
* @since 1.0.0
*
* @return array $columns Array of all the list table columns.
*/
public function get_columns() {
$columns = [
'cb' => '<input type="checkbox" />',
'name' => esc_html__( 'Name', 'wpforms-lite' ),
'tags' => esc_html__( 'Tags', 'wpforms-lite' ),
'author' => esc_html__( 'Author', 'wpforms-lite' ),
'shortcode' => esc_html__( 'Shortcode', 'wpforms-lite' ),
'created' => esc_html__( 'Date', 'wpforms-lite' ),
];
if ( LiteConnect::is_allowed() && LiteConnect::is_enabled() ) {
$columns['entries'] = esc_html__( 'Entries', 'wpforms-lite' );
}
return apply_filters( 'wpforms_overview_table_columns', $columns );
}
/**
* Render the checkbox column.
*
* @since 1.0.0
*
* @param WP_Post $form Form.
*
* @return string
*/
public function column_cb( $form ) {
return '<input type="checkbox" name="form_id[]" value="' . absint( $form->ID ) . '" />';
}
/**
* Render the columns.
*
* @since 1.0.0
*
* @param WP_Post $form CPT object as a form representation.
* @param string $column_name Column Name.
*
* @return string
*/
public function column_default( $form, $column_name ) {
switch ( $column_name ) {
case 'id':
$value = $form->ID;
break;
case 'shortcode':
$value = '[wpforms id="' . $form->ID . '"]';
break;
// This slug is not changed to 'date' for backward compatibility.
case 'created':
if ( gmdate( 'Ymd', strtotime( $form->post_date ) ) === gmdate( 'Ymd', strtotime( $form->post_modified ) ) ) {
$value = wp_kses(
sprintf( /* translators: %1$s - Post created date. */
__( 'Created<br/>%1$s', 'wpforms-lite' ),
esc_html( wpforms_datetime_format( $form->post_date ) )
),
[ 'br' => [] ]
);
} else {
$value = wp_kses(
sprintf( /* translators: %1$s - Post modified date. */
__( 'Last Modified<br/>%1$s', 'wpforms-lite' ),
esc_html( wpforms_datetime_format( $form->post_modified ) )
),
[ 'br' => [] ]
);
}
break;
case 'entries':
$value = sprintf(
'<span class="wpforms-lite-connect-entries-count"><a href="%s" data-title="%s">%s%d</a></span>',
esc_url( admin_url( 'admin.php?page=wpforms-entries' ) ),
esc_attr__( 'Entries are securely backed up in the cloud. Upgrade to restore.', 'wpforms-lite' ),
'<svg viewBox="0 0 16 12"><path d="M10.8 2c1.475 0 2.675 1.175 2.775 2.625C15 5.125 16 6.475 16 8a3.6 3.6 0 0 1-3.6 3.6H4a3.98 3.98 0 0 1-4-4 4.001 4.001 0 0 1 2.475-3.7A4.424 4.424 0 0 1 6.8.4c1.4 0 2.625.675 3.425 1.675C10.4 2.025 10.6 2 10.8 2ZM4 10.4h8.4a2.4 2.4 0 0 0 0-4.8.632.632 0 0 0-.113.013.678.678 0 0 1-.112.012c.125-.25.225-.525.225-.825 0-.875-.725-1.6-1.6-1.6a1.566 1.566 0 0 0-1.05.4 3.192 3.192 0 0 0-2.95-2 3.206 3.206 0 0 0-3.2 3.2v.05A2.757 2.757 0 0 0 1.2 7.6 2.795 2.795 0 0 0 4 10.4Zm6.752-4.624a.64.64 0 1 0-.905-.905L6.857 7.86 5.38 6.352a.64.64 0 1 0-.914.896l1.93 1.97a.64.64 0 0 0 .91.004l3.446-3.446Z"/></svg>',
LiteConnectIntegration::get_form_entries_count( $form->ID )
);
break;
case 'modified':
$value = get_post_modified_time( get_option( 'date_format' ), false, $form );
break;
case 'author':
$value = '';
$author = get_userdata( $form->post_author );
if ( ! $author ) {
break;
}
$value = $author->display_name;
$user_edit_url = get_edit_user_link( $author->ID );
if ( ! empty( $user_edit_url ) ) {
$value = '<a href="' . esc_url( $user_edit_url ) . '">' . esc_html( $value ) . '</a>';
}
break;
case 'php':
$value = '<code style="display:block;font-size:11px;">if( function_exists( \'wpforms_get\' ) ){ wpforms_get( ' . $form->ID . ' ); }</code>';
break;
default:
$value = '';
}
return apply_filters( 'wpforms_overview_table_column_value', $value, $form, $column_name );
}
/**
* Filter the default list of hidden columns.
*
* @since 1.7.2
*
* @param string[] $hidden Array of IDs of columns hidden by default.
* @param WP_Screen $screen WP_Screen object of the current screen.
*
* @return string[]
*/
public function default_hidden_columns( $hidden, $screen ) {
if ( $screen->id !== 'toplevel_page_wpforms-overview' ) {
return $hidden;
}
return [
'tags',
'author',
Locator::COLUMN_NAME,
];
}
/**
* Render the form name column with action links.
*
* @since 1.0.0
*
* @param WP_Post $form Form.
*
* @return string
*/
public function column_name( $form ) {
// Build the row action links and return the value.
return $this->get_column_name_title( $form ) . $this->get_column_name_row_actions( $form );
}
/**
* Render the form tags column.
*
* @since 1.7.5
*
* @param WP_Post $form Form.
*
* @return string
*/
public function column_tags( $form ) {
return wpforms()->get( 'forms_tags' )->column_tags( $form );
}
/**
* Get the form name HTML for the form name column.
*
* @since 1.5.8
*
* @param WP_Post $form Form object.
*
* @return string
*/
protected function get_column_name_title( $form ) {
$title = ! empty( $form->post_title ) ? $form->post_title : $form->post_name;
$name = sprintf(
'<span><strong>%s</strong></span>',
esc_html( $title )
);
if ( $this->view === 'trash' ) {
return $name;
}
if ( wpforms_current_user_can( 'view_form_single', $form->ID ) ) {
$name = sprintf(
'<a href="%s" title="%s" class="row-title" target="_blank" rel="noopener noreferrer"><strong>%s</strong></a>',
esc_url( wpforms_get_form_preview_url( $form->ID ) ),
esc_attr__( 'View preview', 'wpforms-lite' ),
esc_html( $title )
);
}
if ( wpforms_current_user_can( 'view_entries_form_single', $form->ID ) ) {
$name = sprintf(
'<a href="%s" title="%s"><strong>%s</strong></a>',
esc_url(
add_query_arg(
[
'view' => 'list',
'form_id' => $form->ID,
],
admin_url( 'admin.php?page=wpforms-entries' )
)
),
esc_attr__( 'View entries', 'wpforms-lite' ),
esc_html( $title )
);
}
if ( wpforms_current_user_can( 'edit_form_single', $form->ID ) ) {
$name = sprintf(
'<a href="%s" title="%s"><strong>%s</strong></a>',
esc_url(
add_query_arg(
[
'view' => 'fields',
'form_id' => $form->ID,
],
admin_url( 'admin.php?page=wpforms-builder' )
)
),
esc_attr__( 'Edit This Form', 'wpforms-lite' ),
esc_html( $title )
);
}
return $name;
}
/**
* Get the row actions HTML for the form name column.
*
* @since 1.5.8
*
* @param WP_Post $form Form object.
*
* @return string
*/
protected function get_column_name_row_actions( $form ) {
// phpcs:disable WPForms.Comments.PHPDocHooks.RequiredHookDocumentation, WPForms.PHP.ValidateHooks.InvalidHookName
/**
* Filters row action links on the 'All Forms' admin page.
*
* @since 1.0.0
*
* @param array $row_actions An array of action links for a given form.
* @param WP_Post $form Form object.
*/
return $this->row_actions( apply_filters( 'wpforms_overview_row_actions', [], $form ) );
// phpcs:enable
}
/**
* Define bulk actions available for our table listing.
*
* @since 1.0.0
*
* @return array
*/
public function get_bulk_actions() {
return wpforms()->get( 'forms_bulk_actions' )->get_dropdown_items();
}
/**
* Generate the table navigation above or below the table.
*
* @since 1.7.2
*
* @param string $which The location of the table navigation: 'top' or 'bottom'.
*/
protected function display_tablenav( $which ) {
// If there are some forms just call the parent method.
if ( $this->has_items() ) {
parent::display_tablenav( $which );
return;
}
// Otherwise, display bulk actions menu and "0 items" on the right (pagination).
?>
<div class="tablenav <?php echo esc_attr( $which ); ?>">
<div class="alignleft actions bulkactions">
<?php $this->bulk_actions( $which ); ?>
</div>
<?php
$this->extra_tablenav( $which );
if ( $which === 'top' ) {
$this->pagination( $which );
}
?>
<br class="clear" />
</div>
<?php
}
/**
* Extra controls to be displayed between bulk actions and pagination.
*
* @since 1.7.3
*
* @param string $which The location of the table navigation: 'top' or 'bottom'.
*/
protected function extra_tablenav( $which ) {
wpforms()->get( 'forms_tags' )->extra_tablenav( $which, $this );
wpforms()->get( 'forms_views' )->extra_tablenav( $which );
}
/**
* Message to be displayed when there are no forms.
*
* @since 1.0.0
*/
public function no_items() {
esc_html_e( 'No forms found.', 'wpforms-lite' );
}
/**
* Fetch and set up the final data for the table.
*
* @since 1.0.0
*/
public function prepare_items() {
// Set up the columns.
$columns = $this->get_columns();
// Hidden columns (none).
$hidden = get_hidden_columns( $this->screen );
// Define which columns can be sorted - form name, author, date.
$sortable = [
'name' => [ 'title', false ],
'author' => [ 'author', false ],
'created' => [ 'date', false ],
];
// Set column headers.
$this->_column_headers = [ $columns, $hidden, $sortable ];
// phpcs:disable WordPress.Security.NonceVerification.Recommended
$page = $this->get_pagenum();
$order = isset( $_GET['order'] ) && $_GET['order'] === 'asc' ? 'ASC' : 'DESC';
$orderby = isset( $_GET['orderby'] ) ? sanitize_key( $_GET['orderby'] ) : 'ID';
$per_page = $this->get_items_per_page( 'wpforms_forms_per_page', $this->per_page );
// phpcs:enable WordPress.Security.NonceVerification.Recommended
if ( $orderby === 'date' ) {
$orderby = [
'modified' => $order,
'date' => $order,
];
}
$args = [
'orderby' => $orderby,
'order' => $order,
'nopaging' => false,
'posts_per_page' => $per_page,
'paged' => $page,
'no_found_rows' => false,
'post_status' => 'publish',
];
/**
* Filters the `get_posts()` arguments while preparing items for the forms overview table.
*
* @since 1.7.3
*
* @param array $args Arguments array.
*/
$args = (array) apply_filters( 'wpforms_overview_table_prepare_items_args', $args );
// Giddy up.
$this->items = wpforms()->get( 'form' )->get( '', $args );
$per_page = isset( $args['posts_per_page'] ) ? $args['posts_per_page'] : $this->get_items_per_page( 'wpforms_forms_per_page', $this->per_page );
$this->update_count( $args );
$count_current_view = empty( $this->count[ $this->view ] ) ? 0 : $this->count[ $this->view ];
// Finalize pagination.
$this->set_pagination_args(
[
'total_items' => $count_current_view,
'per_page' => $per_page,
'total_pages' => ceil( $count_current_view / $per_page ),
]
);
}
/**
* Calculate and update form counts.
*
* @since 1.7.2
*
* @param array $args Get forms arguments.
*/
private function update_count( $args ) {
/**
* Allow counting forms filtered by a given search criteria.
*
* If result will not contain `all` key, count All Forms without filtering will be performed.
*
* @since 1.7.2
*
* @param array $count Contains counts of forms in different views.
* @param array $args Arguments of the `get_posts`.
*/
$this->count = (array) apply_filters( 'wpforms_overview_table_update_count', [], $args );
// We do not need to perform all forms count if we have the result already.
if ( isset( $this->count['all'] ) ) {
return;
}
// Count all forms.
$this->count['all'] = wpforms_current_user_can( 'wpforms_view_others_forms' )
? (int) wp_count_posts( 'wpforms' )->publish
: (int) count_user_posts( get_current_user_id(), 'wpforms', true );
/**
* Filters forms count data after counting all forms.
*
* This filter executes only if the result of `wpforms_overview_table_update_count` filter
* doesn't contain `all` key.
*
* @since 1.7.3
*
* @param array $count Contains counts of forms in different views.
* @param array $args Arguments of the `get_posts`.
*/
$this->count = (array) apply_filters( 'wpforms_overview_table_update_count_all', $this->count, $args );
}
/**
* Display the pagination.
*
* @since 1.7.2
*
* @param string $which The location of the table pagination: 'top' or 'bottom'.
*/
protected function pagination( $which ) {
if ( $this->has_items() ) {
parent::pagination( $which );
return;
}
printf(
'<div class="tablenav-pages one-page">
<span class="displaying-num">%s</span>
</div>',
esc_html__( '0 items', 'wpforms-lite' )
);
}
/**
* Extending the `display_rows()` method in order to add hooks.
*
* @since 1.5.6
*/
public function display_rows() {
do_action( 'wpforms_admin_overview_before_rows', $this );
parent::display_rows();
do_action( 'wpforms_admin_overview_after_rows', $this );
}
/**
* Forms search markup.
*
* @since 1.7.2
*
* @param string $text The 'submit' button label.
* @param string $input_id ID attribute value for the search input field.
*/
public function search_box( $text, $input_id ) {
wpforms()->get( 'forms_search' )->search_box( $text, $input_id );
}
/**
* Get the list of views available on forms overview table.
*
* @since 1.7.3
*/
protected function get_views() {
return wpforms()->get( 'forms_views' )->get_views();
}
}

View File

@@ -0,0 +1,283 @@
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Primary overview page inside the admin which lists all forms.
*
* @since 1.0.0
*/
class WPForms_Overview {
/**
* Overview Table instance.
*
* @since 1.7.2
*
* @var WPForms_Overview_Table
*/
private $overview_table;
/**
* Primary class constructor.
*
* @since 1.0.0
*/
public function __construct() {
// Maybe load overview page.
add_action( 'admin_init', [ $this, 'init' ] );
// Setup screen options. Needs to be here as admin_init hook it too late.
add_action( 'load-toplevel_page_wpforms-overview', [ $this, 'screen_options' ] );
add_filter( 'set-screen-option', [ $this, 'screen_options_set' ], 10, 3 );
add_filter( 'set_screen_option_wpforms_forms_per_page', [ $this, 'screen_options_set' ], 10, 3 );
}
/**
* Determine if the user is viewing the overview page, if so, party on.
*
* @since 1.0.0
*/
public function init() {
// Only load if we are actually on the overview page.
if ( ! wpforms_is_admin_page( 'overview' ) ) {
return;
}
// Avoid recursively include _wp_http_referer in the REQUEST_URI.
$this->remove_referer();
add_action( 'current_screen', [ $this, 'init_overview_table' ] );
// The overview page leverages WP_List_Table so we must load it.
if ( ! class_exists( 'WP_List_Table', false ) ) {
require_once ABSPATH . 'wp-admin/includes/class-wp-list-table.php';
}
add_action( 'admin_enqueue_scripts', [ $this, 'enqueues' ] );
add_action( 'wpforms_admin_page', [ $this, 'output' ] );
// Provide hook for addons.
do_action( 'wpforms_overview_init' );
}
/**
* Init overview table class.
*
* @since 1.7.2
*/
public function init_overview_table() {
// Load the class that builds the overview table.
require_once WPFORMS_PLUGIN_DIR . 'includes/admin/overview/class-overview-table.php';
$this->overview_table = WPForms_Overview_Table::get_instance();
}
/**
* Remove previous `_wp_http_referer` variable from the REQUEST_URI.
*
* @since 1.7.2
*/
private function remove_referer() {
if ( isset( $_SERVER['REQUEST_URI'] ) ) {
// phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
$_SERVER['REQUEST_URI'] = remove_query_arg( '_wp_http_referer', wp_unslash( $_SERVER['REQUEST_URI'] ) );
}
}
/**
* Add per-page screen option to the Forms table.
*
* @since 1.0.0
*/
public function screen_options() {
$screen = get_current_screen();
if ( $screen === null || $screen->id !== 'toplevel_page_wpforms-overview' ) {
return;
}
add_screen_option(
'per_page',
[
'label' => esc_html__( 'Number of forms per page:', 'wpforms-lite' ),
'option' => 'wpforms_forms_per_page',
'default' => apply_filters( 'wpforms_overview_per_page', 20 ),
]
);
}
/**
* Form table per-page screen option value.
*
* @since 1.0.0
*
* @param bool $keep Whether to save or skip saving the screen option value. Default false.
* @param string $option The option name.
* @param int $value The number of rows to use.
*
* @return mixed
*/
public function screen_options_set( $keep, $option, $value ) {
if ( $option === 'wpforms_forms_per_page' ) {
return $value;
}
return $keep;
}
/**
* Enqueue assets for the overview page.
*
* @since 1.0.0
*/
public function enqueues() {
$min = wpforms_get_min_suffix();
wp_enqueue_script(
'wpforms-admin-forms-overview',
WPFORMS_PLUGIN_URL . "assets/js/components/admin/forms/overview{$min}.js",
[ 'jquery' ],
WPFORMS_VERSION,
true
);
// Hook for addons.
do_action( 'wpforms_overview_enqueue' );
}
/**
* Determine if it is an empty state.
*
* @since 1.7.5
*/
private function is_empty_state() {
// phpcs:disable WordPress.Security.NonceVerification.Recommended
return empty( $this->overview_table->items ) &&
! isset( $_GET['search']['term'] ) &&
! isset( $_GET['status'] ) &&
! isset( $_GET['tags'] ) &&
array_sum( wpforms()->get( 'forms_views' )->get_count() ) === 0;
// phpcs:enable WordPress.Security.NonceVerification.Recommended
}
/**
* Build the output for the overview page.
*
* @since 1.0.0
*/
public function output() {
?>
<div id="wpforms-overview" class="wrap wpforms-admin-wrap">
<h1 class="page-title">
<?php esc_html_e( 'Forms Overview', 'wpforms-lite' ); ?>
<?php if ( wpforms_current_user_can( 'create_forms' ) ) : ?>
<a href="<?php echo esc_url( admin_url( 'admin.php?page=wpforms-builder&view=setup' ) ); ?>" class="page-title-action wpforms-btn add-new-h2 wpforms-btn-orange" data-action="add">
<svg viewBox="0 0 14 14" class="page-title-action-icon">
<path d="M14 5.385v3.23H8.615V14h-3.23V8.615H0v-3.23h5.385V0h3.23v5.385H14Z"/>
</svg>
<span class="page-title-action-text"><?php esc_html_e( 'Add New', 'wpforms-lite' ); ?></span>
</a>
<?php endif; ?>
</h1>
<div class="wpforms-admin-content">
<?php
$this->overview_table->prepare_items();
// phpcs:disable WPForms.PHP.ValidateHooks.InvalidHookName
/**
* Fires before forms overview list table output.
*
* @since 1.6.0.1
*/
do_action( 'wpforms_admin_overview_before_table' );
// phpcs:enable WPForms.PHP.ValidateHooks.InvalidHookName
if ( $this->is_empty_state() ) {
// Output no forms screen.
echo wpforms_render( 'admin/empty-states/no-forms' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
} else {
?>
<form id="wpforms-overview-table" method="get" action="<?php echo esc_url( admin_url( 'admin.php?page=wpforms-overview' ) ); ?>">
<input type="hidden" name="post_type" value="wpforms" />
<input type="hidden" name="page" value="wpforms-overview" />
<?php
$this->overview_table->search_box( esc_html__( 'Search Forms', 'wpforms-lite' ), 'wpforms-overview-search' );
$this->overview_table->views();
$this->overview_table->display();
?>
</form>
<?php } ?>
</div>
</div>
<?php
}
/**
* Admin notices.
*
* @since 1.5.7
* @deprecated 1.7.3
*/
public function notices() {
_deprecated_function( __METHOD__, '1.7.3 of the WPForms', "wpforms()->get( 'forms_bulk_actions' )->notices()" );
wpforms()->get( 'forms_bulk_actions' )->notices();
}
/**
* Process the bulk table actions.
*
* @since 1.5.7
* @deprecated 1.7.3
*/
public function process_bulk_actions() {
_deprecated_function( __METHOD__, '1.7.3 of the WPForms', "wpforms()->get( 'forms_bulk_actions' )->process()" );
wpforms()->get( 'forms_bulk_actions' )->process();
}
/**
* Remove certain arguments from a query string that WordPress should always hide for users.
*
* @since 1.5.7
* @deprecated 1.7.3
*
* @param array $removable_query_args An array of parameters to remove from the URL.
*
* @return array Extended/filtered array of parameters to remove from the URL.
*/
public function removable_query_args( $removable_query_args ) {
_deprecated_function( __METHOD__, '1.7.3 of the WPForms', "wpforms()->get( 'forms_bulk_actions' )->removable_query_args()" );
return wpforms()->get( 'forms_bulk_actions' )->removable_query_args( $removable_query_args );
}
}
new WPForms_Overview();

View File

@@ -0,0 +1,706 @@
<?php
/**
* Settings API.
*
* @since 1.3.7
*/
use WPForms\Admin\Education\Helpers as EducationHelpers;
/**
* Settings output wrapper.
*
* @since 1.3.9
*
* @param array $args
*
* @return string
*/
function wpforms_settings_output_field( $args ) {
// Define default callback for this field type.
$callback = ! empty( $args['type'] ) && function_exists( 'wpforms_settings_' . $args['type'] . '_callback' ) ? 'wpforms_settings_' . $args['type'] . '_callback' : 'wpforms_settings_missing_callback';
// Allow custom callback to be provided via arg.
if ( ! empty( $args['callback'] ) && function_exists( $args['callback'] ) ) {
$callback = $args['callback'];
}
// Store returned markup from callback.
$field = call_user_func( $callback, $args );
// Allow arg to bypass standard field wrap for custom display.
if ( ! empty( $args['wrap'] ) ) {
return $field;
}
// Default class names.
$class = [
'wpforms-setting-row',
"wpforms-setting-row-{$args['type']}",
'wpforms-clear',
];
// Row attributes.
$wrapper_attributes = wpforms_html_attributes(
'wpforms-setting-row-' . wpforms_sanitize_key( $args['id'] ),
! empty( $args['class'] ) ? array_merge( $class, (array) $args['class'] ) : $class,
! empty( $args['data_attributes'] ) && is_array( $args['data_attributes'] ) ? $args['data_attributes'] : [],
! empty( $args['is_hidden'] ) ? [ 'style' => 'display:none;' ] : []
);
// Build standard field markup and return.
$output = "<div {$wrapper_attributes}>";
if ( ! empty( $args['name'] ) && empty( $args['no_label'] ) ) {
$output .= '<span class="wpforms-setting-label">';
$output .= '<label for="wpforms-setting-' . wpforms_sanitize_key( $args['id'] ) . '">' . esc_html( $args['name'] );
// Add education badge, if needed.
// The badge should be added after the label text, but before the label closing tag.
if ( ! empty( $args['education_badge'] ) ) {
$output .= wp_kses( $args['education_badge'], [ 'span' => [ 'class' => [] ] ] );
}
$output .= '</label>';
$output .= '</span>';
}
$output .= '<span class="wpforms-setting-field">';
$output .= $field;
if ( ! empty( $args['desc_after'] ) ) {
$output .= '<div class="wpforms-clear">' . $args['desc_after'] . '</div>';
}
$output .= '</span>';
$output .= '</div>';
return $output;
}
/**
* Missing Callback.
*
* If a function is missing for settings callbacks alert the user.
*
* @since 1.3.9
*
* @param array $args Arguments passed by the setting.
*
* @return string
*/
function wpforms_settings_missing_callback( $args ) {
return sprintf(
/* translators: %s - ID of a setting. */
esc_html__( 'The callback function used for the %s setting is missing.', 'wpforms-lite' ),
'<strong>' . wpforms_sanitize_key( $args['id'] ) . '</strong>'
);
}
/**
* Settings content field callback.
*
* @since 1.3.9
*
* @param array $args
*
* @return string
*/
function wpforms_settings_content_callback( $args ) {
return ! empty( $args['content'] ) ? $args['content'] : '';
}
/**
* Settings license field callback.
*
* @since 1.3.9
*
* @param array $args Settings arguments.
*
* @return string
*/
function wpforms_settings_license_callback( $args ) {
$output = '<p>' . esc_html__( 'You\'re using WPForms Lite - no license needed. Enjoy!', 'wpforms-lite' ) . ' 🙂</p>';
$output .=
'<p>' .
sprintf(
wp_kses( /* translators: %s - WPForms.com upgrade URL. */
__( 'To unlock more features consider <strong><a href="%s" target="_blank" rel="noopener noreferrer" class="wpforms-upgrade-modal">upgrading to PRO</a></strong>.', 'wpforms-lite' ),
[
'a' => [
'href' => [],
'class' => [],
'target' => [],
'rel' => [],
],
'strong' => [],
]
),
esc_url( wpforms_admin_upgrade_link( 'settings-license', 'Upgrade to WPForms Pro text Link' ) )
) .
'</p>';
$output .=
'<p class="discount-note">' .
wp_kses(
__( 'As a valued WPForms Lite user you receive <strong>50% off</strong>, automatically applied at checkout!', 'wpforms-lite' ),
[
'strong' => [],
]
) .
'</p>';
$output .= '<hr><p>' . esc_html__( 'Already purchased? Simply enter your license key below to enable WPForms PRO!', 'wpforms-lite' ) . '</p>';
$output .= '<p>';
$output .= '<input type="password" spellcheck="false" id="wpforms-settings-upgrade-license-key" placeholder="' . esc_attr__( 'Paste license key here', 'wpforms-lite' ) . '" value="">';
$output .= '<button class="wpforms-btn wpforms-btn-md wpforms-btn-blue" id="wpforms-settings-connect-btn">' . esc_html__( 'Verify Key', 'wpforms-lite' ) . '</button>';
$output .= '</p>';
/**
* Filter license settings HTML output.
*
* @since 1.7.9
*
* @param string $output HTML markup to be rendered in place of license settings.
*/
return apply_filters( 'wpforms_settings_license_output', $output );
}
/**
* Settings text input field callback.
*
* @since 1.3.9
*
* @param array $args Settings arguments.
*
* @return string
*/
function wpforms_settings_text_callback( $args ) {
if ( ! in_array( $args['type'], [ 'text', 'password' ], true ) ) {
$args['type'] = 'text';
}
$default = isset( $args['default'] ) ? esc_html( $args['default'] ) : '';
$value = wpforms_setting( $args['id'], $default );
$id = wpforms_sanitize_key( $args['id'] );
$output = '<input type="' . esc_attr( $args['type'] ) . '" id="wpforms-setting-' . $id . '" name="' . $id . '" value="' . esc_attr( $value ) . '">';
if ( ! empty( $args['desc'] ) ) {
$output .= '<p class="desc">' . wp_kses_post( $args['desc'] ) . '</p>';
}
return $output;
}
/**
* Settings password input field callback.
*
* @since 1.8.4
*
* @param array $args Setting field arguments.
*
* @return string
*/
function wpforms_settings_password_callback( $args ) {
return wpforms_settings_text_callback( $args );
}
/**
* Settings number input field callback.
*
* @since 1.5.3
*
* @param array $args Setting field arguments.
*
* @return string
*/
function wpforms_settings_number_callback( $args ) {
$default = isset( $args['default'] ) ? esc_html( $args['default'] ) : '';
$id = 'wpforms-setting-' . wpforms_sanitize_key( $args['id'] );
$attr = [
'value' => wpforms_setting( $args['id'], $default ),
'name' => wpforms_sanitize_key( $args['id'] ),
];
$data = ! empty( $args['data'] ) ? $args['data'] : [];
if ( ! empty( $args['attr'] ) ) {
$attr = array_merge( $attr, $args['attr'] );
}
$output = sprintf(
'<input type="number" %s>',
wpforms_html_attributes( $id, [], $data, $attr )
);
if ( ! empty( $args['desc'] ) ) {
$output .= '<p class="desc">' . wp_kses_post( $args['desc'] ) . '</p>';
}
return $output;
}
/**
* Settings select field callback.
*
* @since 1.3.9
*
* @param array $args
*
* @return string
*/
function wpforms_settings_select_callback( $args ) {
$default = isset( $args['default'] ) ? esc_html( $args['default'] ) : '';
$value = wpforms_setting( $args['id'], $default );
$id = wpforms_sanitize_key( $args['id'] );
$select_name = $id;
$class = ! empty( $args['choicesjs'] ) ? 'choicesjs-select' : '';
$choices = ! empty( $args['choicesjs'] ) ? true : false;
$data = isset( $args['data'] ) ? (array) $args['data'] : [];
$attr = isset( $args['attr'] ) ? (array) $args['attr'] : [];
if ( $choices && ! empty( $args['search'] ) ) {
$data['search'] = 'true';
}
if ( ! empty( $args['placeholder'] ) ) {
$data['placeholder'] = $args['placeholder'];
}
$size_attr = '';
if ( $choices && ! empty( $args['multiple'] ) ) {
$attr[] = 'multiple';
$select_name = $id . '[]';
$size_attr = ' size="1"';
}
foreach ( $data as $name => $val ) {
$data[ $name ] = 'data-' . sanitize_html_class( $name ) . '="' . esc_attr( $val ) . '"';
}
$data = implode( ' ', $data );
$attr = implode( ' ', array_map( 'sanitize_html_class', $attr ) );
$output = $choices ? '<span class="choicesjs-select-wrap">' : '';
$output .= '<select id="wpforms-setting-' . $id . '" name="' . $select_name . '" class="' . $class . '"' . $data . $attr . $size_attr . '>';
foreach ( $args['options'] as $option => $name ) {
if ( empty( $args['selected'] ) ) {
$selected = selected( $value, $option, false );
} else {
$selected = is_array( $args['selected'] ) && in_array( $option, $args['selected'], true ) ? 'selected' : '';
}
$output .= '<option value="' . esc_attr( $option ) . '" ' . $selected . '>' . esc_html( $name ) . '</option>';
}
$output .= '</select>';
$output .= $choices ? '</span>' : '';
if ( ! empty( $args['desc'] ) ) {
$output .= '<p class="desc">' . wp_kses_post( $args['desc'] ) . '</p>';
}
return $output;
}
/**
* Settings checkbox field callback.
*
* @since 1.3.9
*
* @param array $args
*
* @return string
*/
function wpforms_settings_checkbox_callback( $args ) {
$value = wpforms_setting( $args['id'] );
$id = wpforms_sanitize_key( $args['id'] );
$checked = ! empty( $value ) ? checked( 1, $value, false ) : '';
$disabled = ! empty( $args['disabled'] ) ? ' disabled' : '';
$output = '<input type="checkbox" id="wpforms-setting-' . $id . '" name="' . $id . '" ' . $checked . $disabled . '>';
if ( ! empty( $args['desc'] ) ) {
$output .= '<p class="desc">' . wp_kses_post( $args['desc'] ) . '</p>';
}
if ( ! empty( $args['disabled_desc'] ) ) {
$output .= '<p class="disabled-desc">' . wp_kses_post( $args['disabled_desc'] ) . '</p>';
}
return $output;
}
/**
* Settings radio field callback.
*
* @since 1.3.9
*
* @param array $args
*
* @return string
*/
function wpforms_settings_radio_callback( $args ) {
$default = isset( $args['default'] ) ? esc_html( $args['default'] ) : '';
$value = wpforms_setting( $args['id'], $default );
$id = wpforms_sanitize_key( $args['id'] );
$output = '';
$x = 1;
foreach ( $args['options'] as $option => $name ) {
$checked = checked( $value, $option, false );
$output .= '<span class="wpforms-settings-field-radio-wrapper">';
$output .= '<input type="radio" id="wpforms-setting-' . $id . '[' . $x . ']" name="' . $id . '" value="' . esc_attr( $option ) . '" ' . $checked . '>';
$output .= '<label for="wpforms-setting-' . $id . '[' . $x . ']" class="option-' . sanitize_html_class( $option ) . '">';
$output .= esc_html( $name );
$output .= '</label>';
$output .= '</span>';
$x ++;
}
if ( ! empty( $args['desc'] ) ) {
$output .= '<p class="desc">' . wp_kses_post( $args['desc'] ) . '</p>';
}
return $output;
}
/**
* Email template endpoint field callback.
*
* @since 1.8.5
*
* @param array $args Field arguments.
*
* @return string
*/
function wpforms_settings_email_template_callback( $args ) {
$id = wpforms_sanitize_key( $args['id'] );
$is_pro = wpforms()->is_pro();
$output = '';
$x = 1;
$education_args = [
'name' => esc_html__( 'Email Templates', 'wpforms-lite' ),
'plural' => '1',
'action' => 'upgrade',
];
foreach ( $args['options'] as $option => $attrs ) {
$checked = checked( $args['value'], $option, false );
$has_education = ! $is_pro && isset( $attrs['is_pro'] ) && $attrs['is_pro'];
$class = [ 'wpforms-settings-field-radio-wrapper', 'wpforms-card-image' ];
$datas = [];
// Add class and data attributes for education modal, if needed.
if ( $has_education ) {
$class[] = 'education-modal'; // This class is used for JS.
$datas = $education_args; // This data is used for JS.
}
$output .= '<span ' . wpforms_html_attributes( '', $class, $datas ) . '>';
$output .= '<input type="radio" id="wpforms-setting-' . $id . '[' . $x . ']" name="' . $id . '" value="' . esc_attr( $option ) . '" ' . $checked . '>';
$output .= '<label for="wpforms-setting-' . $id . '[' . $x . ']" class="option-' . sanitize_html_class( $option ) . '">';
$output .= esc_html( $attrs['name'] );
// Add class and data attributes for education modal, if needed.
if ( $has_education ) {
$output .= EducationHelpers::get_badge( 'Pro' );
}
$output .= '<span class="wpforms-card-image-overlay">';
$output .= '<span class="wpforms-btn-choose wpforms-btn wpforms-btn-md wpforms-btn-orange">';
$output .= esc_html__( 'Choose', 'wpforms-lite' ) . '</span>';
// Only add the preview action button if provided.
if ( ! empty( $attrs['preview'] ) ) {
$output .= '<a href="' . esc_url( $attrs['preview'] ) . '" class="wpforms-btn-preview wpforms-btn wpforms-btn-md wpforms-btn-light-grey" target="_blank">';
$output .= esc_html__( 'Preview', 'wpforms-lite' );
$output .= '</a>';
}
$output .= '</span>';
$output .= '</label>';
$output .= '</span>';
++$x;
}
if ( ! empty( $args['desc'] ) ) {
$output .= '<p class="desc">' . wp_kses_post( $args['desc'] ) . '</p>';
}
return $output;
}
/**
* Settings toggle field callback.
*
* @since 1.7.4
*
* @param array $args Arguments.
*
* @return string
*/
function wpforms_settings_toggle_callback( $args ) {
$value = wpforms_setting( $args['id'] );
$id = wpforms_sanitize_key( $args['id'] );
$class = ! empty( $args['control-class'] ) ? $args['control-class'] : '';
$class .= ! empty( $args['is-important'] ) ? ' wpforms-important' : '';
$input_attr = ! empty( $args['input-attr'] ) ? $args['input-attr'] : '';
$default_args = [
'control-class' => $class,
];
$args = wp_parse_args( $args, $default_args );
$output = wpforms_panel_field_toggle_control(
$args,
'wpforms-setting-' . $id,
$id,
! empty( $args['label'] ) ? $args['label'] : '',
$value,
$input_attr
);
$desc_on = ! empty( $args['desc'] ) ? $args['desc'] : '';
$desc_on = ! empty( $args['desc-on'] ) ? $args['desc-on'] : $desc_on;
$desc_off = ! empty( $args['desc-off'] ) ? $args['desc-off'] : '';
$output .= sprintf(
'<p class="desc desc-on wpforms-toggle-desc%1$s">%2$s</p>',
empty( $value ) && ! empty( $desc_off ) ? ' wpforms-hidden' : '',
wp_kses_post( $desc_on )
);
if ( ! empty( $desc_off ) ) {
$output .= sprintf(
'<p class="desc desc-off wpforms-toggle-desc%1$s">%2$s</p>',
empty( $value ) ? '' : ' wpforms-hidden',
wp_kses_post( $desc_off )
);
}
return $output;
}
/**
* Settings image upload field callback.
*
* @since 1.3.9
*
* @param array $args Arguments.
*
* @return string
*/
function wpforms_settings_image_callback( $args ) {
$default = isset( $args['default'] ) ? esc_html( $args['default'] ) : '';
$value = wpforms_setting( $args['id'], $default );
$id = wpforms_sanitize_key( $args['id'] );
$output = '';
if ( ! empty( $value ) ) {
$output .= '<img src="' . esc_url_raw( $value ) . '">';
}
$output .= '<input type="text" id="wpforms-setting-' . $id . '" name="' . $id . '" value="' . esc_url_raw( $value ) . '">';
// Show the remove button if specified.
if ( isset( $args['show_remove'] ) && $args['show_remove'] ) {
$output .= '<button class="wpforms-btn wpforms-btn-md wpforms-setting-remove-image">' . esc_html__( 'Remove Image', 'wpforms-lite' ) . '</button>';
}
$output .= '<button class="wpforms-btn wpforms-btn-md wpforms-btn-light-grey wpforms-setting-upload-image">' . esc_html__( 'Upload Image', 'wpforms-lite' ) . '</button>';
if ( ! empty( $args['desc'] ) ) {
$output .= '<p class="desc">' . wp_kses_post( $args['desc'] ) . '</p>';
}
return $output;
}
/**
* Settings color picker field callback.
*
* @since 1.3.9
*
* @param array $args
*
* @return string
*/
function wpforms_settings_color_callback( $args ) {
$default = isset( $args['default'] ) ? esc_html( $args['default'] ) : '';
$value = wpforms_setting( $args['id'], $default );
$id = wpforms_sanitize_key( $args['id'] );
$data = isset( $args['data'] ) ? (array) $args['data'] : [];
foreach ( $data as $name => $val ) {
$data[ $name ] = 'data-' . sanitize_html_class( $name ) . '="' . esc_attr( $val ) . '"';
}
$data = implode( ' ', $data );
$output = '<input type="text" id="wpforms-setting-' . $id . '" class="wpforms-color-picker" name="' . $id . '" value="' . esc_attr( $value ) . '" ' . $data . '>';
if ( ! empty( $args['desc'] ) ) {
$output .= '<p class="desc">' . wp_kses_post( $args['desc'] ) . '</p>';
}
return $output;
}
/**
* Color scheme endpoint fieldset callback.
* This function will output a fieldset with color picker inputs.
*
* @since 1.8.5
*
* @param array $args Field arguments.
*
* @return string
*/
function wpforms_settings_color_scheme_callback( $args ) {
$id = wpforms_sanitize_key( $args['id'] );
$value = wpforms_setting( $args['id'], [] );
$output = '';
foreach ( $args['colors'] as $color => $attrs ) {
$data = isset( $attrs['data'] ) ? (array) $attrs['data'] : [];
$default_value = isset( $data['fallback-color'] ) ? wpforms_sanitize_hex_color( $data['fallback-color'] ) : '';
$field_id = "{$id}-{$color}";
$field_value = isset( $value[ $color ] ) ? wpforms_sanitize_hex_color( $value[ $color ] ) : $default_value;
$input_attributes = wpforms_html_attributes(
"wpforms-setting-{$field_id}",
[ 'wpforms-color-picker' ],
$data,
[
'type' => 'text',
'name' => "{$id}[{$color}]",
'value' => esc_attr( $field_value ),
]
);
$output .= "<input {$input_attributes}>";
$output .= '<label for="wpforms-setting-' . $field_id . '">';
$output .= esc_html( $attrs['name'] );
$output .= '</label>';
}
if ( ! empty( $args['desc'] ) ) {
$output .= '<p class="desc">' . wp_kses_post( $args['desc'] ) . '</p>';
}
return $output;
}
/**
* Settings providers field callback - this is for the Integrations tab.
*
* @since 1.3.9
*
* @param array $args
*
* @return string
*/
function wpforms_settings_providers_callback( $args ) {
$active = wpforms_get_providers_available();
$providers = wpforms_get_providers_options();
$output = '<div id="wpforms-settings-providers">';
ob_start();
do_action( 'wpforms_settings_providers', $active, $providers );
$output .= ob_get_clean();
$output .= '</div>';
return $output;
}
/**
* Webhooks' endpoint field callback.
*
* @since 1.8.4
*
* @param array $args Field arguments.
*
* @return string
*/
function wpforms_settings_webhook_endpoint_callback( $args ) {
$output = '';
if ( ! empty( $args['url'] ) ) {
$output = '<div class="wpforms-stripe-webhook-endpoint-url">';
$output .= '<input type="text" disabled id="wpforms-stripe-webhook-endpoint-url" value="' . esc_url_raw( $args['url'] ) . '" />';
$output .= '<a class="button button-secondary wpforms-copy-to-clipboard" data-clipboard-target="#wpforms-stripe-webhook-endpoint-url" href="" ><span class="dashicons dashicons-admin-page"></span></a>';
$output .= '</div>';
if ( ! empty( $args['desc'] ) ) {
$output .= '<p class="desc">' . wp_kses_post( $args['desc'] ) . '</p>';
}
}
return $output;
}
/**
* Settings field columns callback.
*
* @since 1.5.8
*
* @param array $args Arguments passed by the setting.
*
* @return string
*/
function wpforms_settings_columns_callback( $args ) {
if ( empty( $args['columns'] ) || ! is_array( $args['columns'] ) ) {
return '';
}
$output = '<div class="wpforms-setting-columns">';
foreach ( $args['columns'] as $column ) {
// Define default callback for this field type.
$callback = ! empty( $column['type'] ) ? 'wpforms_settings_' . $column['type'] . '_callback' : '';
// Allow custom callback to be provided via arg.
if ( ! empty( $column['callback'] ) ) {
$callback = $column['callback'];
}
$output .= '<div class="wpforms-setting-column">';
if ( ! empty( $column['name'] ) ) {
$output .= '<label><b>' . wp_kses_post( $column['name'] ) . '</b></label>';
}
if ( function_exists( $callback ) ) {
$output .= call_user_func( $callback, $column );
}
$output .= '</div>';
}
$output .= '</div>';
return $output;
}

View File

@@ -0,0 +1,496 @@
<?php
// phpcs:disable WPForms.Comments.PHPDocHooks.RequiredHookDocumentation, WPForms.PHP.ValidateHooks.InvalidHookName
// phpcs:ignore Generic.Commenting.DocComment.MissingShort
/** @noinspection AutoloadingIssuesInspection */
/**
* DB class.
*
* This handy class originated from Pippin's Easy Digital Downloads.
* https://github.com/easydigitaldownloads/easy-digital-downloads/blob/master/includes/class-edd-db.php
*
* Sub-classes should define $table_name, $version, and $primary_key in __construct() method.
*
* @since 1.1.6
*/
abstract class WPForms_DB {
/**
* Maximum length of index key.
*
* Indexes have a maximum size of 767 bytes. Historically, we haven't needed to be concerned about that.
* As of WP 4.2, however, WP moved to utf8mb4, which uses 4 bytes per character. This means that an index which
* used to have room for floor(767/3) = 255 characters, now only has room for floor(767/4) = 191 characters.
*
* @since 1.8.2
*/
const MAX_INDEX_LENGTH = 191;
/**
* Database table name.
*
* @since 1.1.6
*
* @var string
*/
public $table_name;
/**
* Database version.
*
* @since 1.1.6
*
* @var string
*/
public $version;
/**
* Primary key (unique field) for the database table.
*
* @since 1.1.6
*
* @var string
*/
public $primary_key;
/**
* Database type identifier.
*
* @since 1.5.1
*
* @var string
*/
public $type;
/**
* Retrieve the list of columns for the database table.
* Subclasses should define an array of columns here.
*
* @since 1.1.6
*
* @return array List of columns.
*/
public function get_columns() {
return [];
}
/**
* Retrieve column defaults.
* Subclasses can define default for any/all columns defined in the get_columns() method.
*
* @since 1.1.6
*
* @return array All defined column defaults.
*/
public function get_column_defaults() {
return [];
}
/**
* Retrieve a row from the database based on a given row ID.
*
* @since 1.1.6
*
* @param int $row_id Row ID.
*
* @return null|object
*/
public function get( $row_id ) {
global $wpdb;
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.NoCaching
return $wpdb->get_row(
$wpdb->prepare(
"SELECT * FROM $this->table_name WHERE $this->primary_key = %d LIMIT 1;", // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
(int) $row_id
)
);
}
/**
* Retrieve a row based on column and row ID.
*
* @since 1.1.6
*
* @param string $column Column name.
* @param int|string $value Column value.
*
* @return object|null Database query result, object or null on failure.
*/
public function get_by( $column, $value ) {
global $wpdb;
if (
empty( $value ) ||
! array_key_exists( $column, $this->get_columns() )
) {
return null;
}
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.NoCaching
return $wpdb->get_row(
$wpdb->prepare(
"SELECT * FROM $this->table_name WHERE $column = %s LIMIT 1;", // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
$value
)
);
}
/**
* Retrieve a value based on column name and row ID.
*
* @since 1.1.6
*
* @param string $column Column name.
* @param int|string $row_id Row ID.
*
* @return string|null Database query result (as string), or null on failure.
* @noinspection PhpUnused
*/
public function get_column( $column, $row_id ) {
global $wpdb;
if ( empty( $row_id ) || ! array_key_exists( $column, $this->get_columns() ) ) {
return null;
}
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.NoCaching
return $wpdb->get_var(
$wpdb->prepare(
"SELECT $column FROM $this->table_name WHERE $this->primary_key = %d LIMIT 1;", // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
(int) $row_id
)
);
}
/**
* Retrieve one column value based on another given column and matching value.
*
* @since 1.1.6
*
* @param string $column Column name.
* @param string $column_where Column to match against in the WHERE clause.
* @param string $column_value Value to match to the column in the WHERE clause.
*
* @return string|null Database query result (as string), or null on failure.
* @noinspection PhpUnused
*/
public function get_column_by( $column, $column_where, $column_value ) {
global $wpdb;
if (
empty( $column ) ||
empty( $column_where ) ||
empty( $column_value ) ||
! array_key_exists( $column_where, $this->get_columns() ) ||
! array_key_exists( $column, $this->get_columns() )
) {
return null;
}
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.NoCaching
return $wpdb->get_var(
$wpdb->prepare(
"SELECT $column FROM $this->table_name WHERE $column_where = %s LIMIT 1;", // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
$column_value
)
);
}
/**
* Insert a new record into the database.
*
* @since 1.1.6
*
* @param array $data Column data.
* @param string $type Optional. Data type context.
*
* @return int ID for the newly inserted record. 0 otherwise.
*/
public function add( $data, $type = '' ) {
global $wpdb;
// Set default values.
$data = wp_parse_args( $data, $this->get_column_defaults() );
do_action( 'wpforms_pre_insert_' . $type, $data );
// Initialise column format array.
$column_formats = $this->get_columns();
// Force fields to lower case.
$data = array_change_key_case( $data );
// White list columns.
$data = array_intersect_key( $data, $column_formats );
// Reorder $column_formats to match the order of columns given in $data.
$data_keys = array_keys( $data );
$column_formats = array_merge( array_flip( $data_keys ), $column_formats );
$wpdb->insert( $this->table_name, $data, $column_formats );
do_action( 'wpforms_post_insert_' . $type, $wpdb->insert_id, $data );
return $wpdb->insert_id;
}
/**
* Insert a new record into the database. This runs the add() method.
*
* @see add()
*
* @since 1.1.6
*
* @param array $data Column data.
*
* @return int ID for the newly inserted record.
*/
public function insert( $data ) {
return $this->add( $data );
}
/**
* Update an existing record in the database.
*
* @since 1.1.6
*
* @param int|string $row_id Row ID for the record being updated.
* @param array $data Optional. Array of columns and associated data to update. Default empty array.
* @param string $where Optional. Column to match against in the WHERE clause. If empty, $primary_key
* will be used. Default empty.
* @param string $type Optional. Data type context, e.g. 'affiliate', 'creative', etc. Default empty.
*
* @return bool False if the record could not be updated, true otherwise.
*/
public function update( $row_id, $data = [], $where = '', $type = '' ) {
global $wpdb;
// Row ID must be a positive integer.
$row_id = absint( $row_id );
if ( empty( $row_id ) ) {
return false;
}
if ( empty( $where ) ) {
$where = $this->primary_key;
}
do_action( 'wpforms_pre_update_' . $type, $data );
// Initialise column format array.
$column_formats = $this->get_columns();
// Force fields to lower case.
$data = array_change_key_case( $data );
// White list columns.
$data = array_intersect_key( $data, $column_formats );
// Reorder $column_formats to match the order of columns given in $data.
$data_keys = array_keys( $data );
$column_formats = array_merge( array_flip( $data_keys ), $column_formats );
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.NoCaching
if ( $wpdb->update( $this->table_name, $data, [ $where => $row_id ], $column_formats ) === false ) {
return false;
}
do_action( 'wpforms_post_update_' . $type, $data );
return true;
}
/**
* Delete a record from the database.
*
* @since 1.1.6
*
* @param int|string $row_id Row ID.
*
* @return bool False if the record could not be deleted, true otherwise.
*/
public function delete( $row_id = 0 ) {
global $wpdb;
// Row ID must be positive integer.
$row_id = absint( $row_id );
if ( empty( $row_id ) ) {
return false;
}
do_action( 'wpforms_pre_delete', $row_id );
do_action( 'wpforms_pre_delete_' . $this->type, $row_id );
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.NoCaching
$result = $wpdb->query(
$wpdb->prepare(
"DELETE FROM $this->table_name WHERE $this->primary_key = %d", // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
$row_id
)
);
if ( $result === false ) {
return false;
}
do_action( 'wpforms_post_delete', $row_id );
do_action( 'wpforms_post_delete_' . $this->type, $row_id );
return true;
}
/**
* Delete a record from the database by column.
*
* @since 1.1.6
*
* @param string $column Column name.
* @param int|string $column_value Column value.
*
* @return bool False if the record could not be deleted, true otherwise.
*/
public function delete_by( $column, $column_value ) {
global $wpdb;
if (
empty( $column ) ||
empty( $column_value ) ||
! array_key_exists( $column, $this->get_columns() )
) {
return false;
}
do_action( 'wpforms_pre_delete', $column_value );
do_action( 'wpforms_pre_delete_' . $this->type, $column_value );
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.NoCaching
$result = $wpdb->query(
$wpdb->prepare(
"DELETE FROM $this->table_name WHERE $column = %s", // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
$column_value
)
);
if ( $result === false ) {
return false;
}
do_action( 'wpforms_post_delete', $column_value );
do_action( 'wpforms_post_delete_' . $this->type, $column_value );
return true;
}
/**
* Delete record(s) from the database using WHERE IN syntax.
*
* @since 1.6.4
*
* @param string $column Column name.
* @param mixed $column_values Column values.
*
* @return int|bool Number of deleted records, false otherwise.
*/
public function delete_where_in( $column, $column_values ) {
global $wpdb;
if ( empty( $column ) || empty( $column_values ) ) {
return false;
}
if ( ! array_key_exists( $column, $this->get_columns() ) ) {
return false;
}
$values = (array) $column_values;
foreach ( $values as $key => $value ) {
// Check if a string contains an integer and sanitize accordingly.
if ( (string) (int) $value === $value ) {
$values[ $key ] = (int) $value;
$placeholders[ $key ] = '%d';
} else {
$values[ $key ] = sanitize_text_field( $value );
$placeholders[ $key ] = '%s';
}
}
$placeholders = isset( $placeholders ) ? implode( ',', $placeholders ) : '';
$sql = "DELETE FROM $this->table_name WHERE $column IN ( $placeholders )";
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.PreparedSQL.NotPrepared
return $wpdb->query( $wpdb->prepare( $sql, $values ) );
}
/**
* Check if the given table exists.
*
* @since 1.1.6
* @since 1.5.9 Default value is now the current child class table name.
*
* @param string $table The table name. Defaults to the child class table name.
*
* @return bool If the table name exists.
*/
public function table_exists( $table = '' ) {
global $wpdb;
if ( ! empty( $table ) ) {
$table = sanitize_text_field( $table );
} else {
$table = $this->table_name;
}
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.NoCaching
return $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $table ) ) === $table;
}
/**
* Build WHERE for a query.
*
* @since 1.7.2.2
*
* @param array $args Optional args.
* @param array $keys Allowed arg items.
* @param string|string[] $formats Formats of arg items.
*
* @return string
*/
protected function build_where( $args, $keys = [], $formats = [] ) {
$formats = array_pad( $formats, count( $keys ), '%d' );
$where = '';
foreach ( $keys as $index => $key ) {
// Value `$args[ $key ]` can be a natural number and a numeric string.
// We should skip empty string values, but continue working with '0'.
if ( empty( $args[ $key ] ) && $args[ $key ] !== '0' ) {
continue;
}
$ids = wpforms_wpdb_prepare_in( $args[ $key ], $formats[ $index ] );
$where .= empty( $where ) ? 'WHERE' : 'AND';
$where .= " `{$key}` IN ( {$ids} ) ";
}
return $where;
}
}

View File

@@ -0,0 +1,120 @@
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Load the field types.
*
* @since 1.0.0
*/
class WPForms_Fields {
/**
* Primary class constructor.
*
* @since 1.0.0
*/
public function __construct() {
$this->init();
}
/**
* Initialize hooks.
*
* @since 1.2.8
* @since 1.8.2 Moved base class loading to \WPForms\WPForms::includes.
*/
public function init() {
$this->hooks();
}
/**
* Hooks.
*
* @since 1.7.7
*/
private function hooks() {
// Load default fields on WP init.
add_action( 'init', [ $this, 'load' ] );
}
/**
* Load default field types.
*
* @since 1.0.0
*/
public function load() {
$fields = [
'text',
'textarea',
'select',
'radio',
'checkbox',
'divider',
'entry-preview',
'email',
'url',
'hidden',
'html',
'content',
'name',
'password',
'address',
'phone',
'date-time',
'number',
'page-break',
'rating',
'file-upload',
'payment-credit-card',
'number-slider',
'richtext',
'internal-information',
'layout',
];
// Include GDPR Checkbox field if GDPR enhancements are enabled.
if ( wpforms_setting( 'gdpr', false ) ) {
$fields[] = 'gdpr-checkbox';
}
/**
* Filters array of fields to be loaded.
*
* @since 1.0.0
*
* @param array $fields Field types.
*/
$fields = (array) apply_filters( // phpcs:ignore WPForms.PHP.ValidateHooks.InvalidHookName
'wpforms_load_fields',
$fields
);
foreach ( $fields as $field ) {
$file = WPFORMS_PLUGIN_DIR . 'includes/fields/class-' . $field . '.php';
if ( file_exists( $file ) ) {
require_once $file;
continue;
}
$pro_file = WPFORMS_PLUGIN_DIR . 'pro/includes/fields/class-' . $field . '.php';
if ( wpforms()->is_pro() && file_exists( $pro_file ) ) {
require_once $pro_file;
}
}
// We have to put it here due to tests for restricted emails.
new WPForms_Field_Email();
}
}
new WPForms_Fields();

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,211 @@
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Handle plugin installation upon activation.
*
* @since 1.0.0
*/
class WPForms_Install {
/**
* Primary class constructor.
*
* @since 1.0.0
*/
public function __construct() {
// When activated, trigger install method.
register_activation_hook( WPFORMS_PLUGIN_FILE, [ $this, 'install' ] );
register_deactivation_hook( WPFORMS_PLUGIN_FILE, [ $this, 'deactivate' ] );
// Watch for new multisite blogs.
add_action( 'wp_initialize_site', [ $this, 'new_multisite_blog' ], 10, 2 );
// Watch for delayed admin install.
add_action( 'admin_init', [ $this, 'admin' ] );
}
/**
* Perform certain actions on plugin activation.
*
* @since 1.0.0
*
* @param bool $network_wide Whether to enable the plugin for all sites in the network
* or just the current site. Multisite only. Default is false.
*/
public function install( $network_wide = false ) {
// Check if we are on multisite and network activating.
if ( is_multisite() && $network_wide ) {
// Multisite - go through each subsite and run the installer.
$sites = get_sites(
[
'fields' => 'ids',
'number' => 0,
]
);
foreach ( $sites as $blog_id ) {
switch_to_blog( $blog_id );
$this->run();
restore_current_blog();
}
} else {
// Normal single site.
$this->run();
}
set_transient( 'wpforms_just_activated', wpforms()->is_pro() ? 'pro' : 'lite', 60 );
// Abort so we only set the transient for single site installs.
if ( isset( $_GET['activate-multi'] ) || is_network_admin() ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
return;
}
// Add transient to trigger redirect to the Welcome screen.
set_transient( 'wpforms_activation_redirect', true, 30 );
}
/**
* Run manual installation.
*
* @since 1.5.4.2
*
* @param bool $silent Silent install, disables welcome page.
*/
public function manual( $silent = false ) {
$this->install( is_plugin_active_for_network( plugin_basename( WPFORMS_PLUGIN_FILE ) ) );
if ( $silent ) {
delete_transient( 'wpforms_activation_redirect' );
}
}
/**
* Perform certain actions on plugin deactivation.
*
* @since 1.5.9
*/
public function deactivate() {
// Unschedule all ActionScheduler actions by group.
wpforms()->get( 'tasks' )->cancel_all();
// Remove plugin cron jobs.
wp_clear_scheduled_hook( 'wpforms_email_summaries_cron' );
}
/**
* Watch for delayed install procedure from WPForms admin.
*
* @since 1.5.4.2
*/
public function admin() {
if ( ! is_admin() ) {
return;
}
$install = get_option( 'wpforms_install', false );
if ( empty( $install ) ) {
return;
}
$this->manual( true );
delete_option( 'wpforms_install' );
}
/**
* Run the actual installer.
*
* @since 1.5.4.2
*/
protected function run() {
// Create custom database tables.
$this->maybe_create_tables();
// Hook for Pro users.
do_action( 'wpforms_install' );
/*
* Set current version, to be referenced in future updates.
*/
// Used by Pro migrations.
update_option( 'wpforms_version', WPFORMS_VERSION );
// Used by Lite migrations.
update_option( 'wpforms_version_lite', WPFORMS_VERSION );
// Store the date when the initial activation was performed.
$type = class_exists( 'WPForms_Lite', false ) ? 'lite' : 'pro';
$activated = get_option( 'wpforms_activated', [] );
if ( empty( $activated[ $type ] ) ) {
$activated[ $type ] = time();
update_option( 'wpforms_activated', $activated );
}
}
/**
* When a new site is created in multisite, see if we are network activated,
* and if so run the installer.
*
* @since 1.3.0
* @since 1.8.4 Added $new_site and $args parameters and removed $blog_id, $user_id, $domain, $path, $site_id,
* $meta parameters.
*
* @param WP_Site $new_site New site object.
* @param array $args Arguments for the initialization.
*
* @noinspection PhpUnusedParameterInspection
*/
public function new_multisite_blog( $new_site, $args ) {
if ( is_plugin_active_for_network( plugin_basename( WPFORMS_PLUGIN_FILE ) ) ) {
switch_to_blog( $new_site->blog_id );
$this->run();
restore_current_blog();
}
}
/**
* Create database tables if they do not exist.
* It covers new installations.
*
* @since 1.8.2
*/
private function maybe_create_tables() {
array_map(
static function( $handler ) {
if ( ! method_exists( $handler, 'table_exists' ) ) {
return;
}
if ( $handler->table_exists() ) {
return;
}
$handler->create_table();
},
[
wpforms()->get( 'tasks_meta' ),
wpforms()->get( 'payment' ),
wpforms()->get( 'payment_meta' ),
]
);
}
}
new WPForms_Install();

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,70 @@
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Load the providers.
*
* @since 1.3.6
*/
class WPForms_Providers {
/**
* Primary class constructor.
*
* @since 1.3.6
*/
public function __construct() {
$this->init();
}
/**
* Load and init the base provider class.
*
* @since 1.3.6
*/
public function init() {
// Parent class template.
require_once WPFORMS_PLUGIN_DIR . 'includes/providers/class-base.php';
// Load default templates on WP init.
add_action( 'wpforms_loaded', [ $this, 'load' ] );
}
/**
* Load default marketing providers.
*
* @since 1.3.6
*/
public function load() {
$providers = [
'constant-contact',
];
$providers = (array) apply_filters( 'wpforms_load_providers', $providers );
foreach ( $providers as $provider ) {
$provider = sanitize_file_name( $provider );
$path = WPFORMS_PLUGIN_DIR . 'includes/providers/class-' . $provider . '.php';
if ( file_exists( $path ) ) {
require_once $path;
}
/**
* Allow third-party plugins to load their own providers.
*
* @since 1.7.0
*/
do_action( "wpforms_load_{$provider}_provider" );
}
}
}
new WPForms_Providers();

View File

@@ -0,0 +1,66 @@
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Pre-configured packaged templates.
*
* @since 1.0.0
*/
class WPForms_Templates {
/**
* Primary class constructor.
*
* @since 1.0.0
*/
public function __construct() {
$this->init();
}
/**
* Load and init the base form template class.
*
* @since 1.2.8
*/
public function init() {
// Parent class template.
require_once WPFORMS_PLUGIN_DIR . 'includes/templates/class-base.php';
// Load default templates on WP init.
add_action( 'init', [ $this, 'load' ] );
}
/**
* Load default form templates.
*
* @since 1.0.0
*/
public function load() {
$templates = apply_filters(
'wpforms_load_templates',
[
'blank',
'simple-contact-form',
]
);
foreach ( $templates as $template ) {
$template = sanitize_file_name( $template );
if ( file_exists( WPFORMS_PLUGIN_DIR . 'includes/templates/class-' . $template . '.php' ) ) {
require_once WPFORMS_PLUGIN_DIR . 'includes/templates/class-' . $template . '.php';
} elseif ( file_exists( WPFORMS_PLUGIN_DIR . 'pro/includes/templates/class-' . $template . '.php' ) && wpforms()->is_pro() ) {
require_once WPFORMS_PLUGIN_DIR . 'pro/includes/templates/class-' . $template . '.php';
}
}
}
}
new WPForms_Templates;

View File

@@ -0,0 +1,172 @@
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* WPForms widget.
*
* @since 1.0.2
*/
class WPForms_Widget extends WP_Widget {
/**
* Hold widget settings defaults, populated in constructor.
*
* @since 1.0.2
*
* @var array
*/
protected $defaults;
/**
* Constructor
*
* @since 1.0.2
*/
public function __construct() {
// Widget defaults.
$this->defaults = [
'title' => '',
'form_id' => '',
'show_title' => false,
'show_desc' => false,
];
// Widget Slug.
$widget_slug = 'wpforms-widget';
// Widget basics.
$widget_ops = [
'classname' => $widget_slug,
'description' => esc_html_x( 'Display a form.', 'Widget', 'wpforms-lite' ),
'show_instance_in_rest' => true,
];
// Widget controls.
$control_ops = [
'id_base' => $widget_slug,
];
// Load widget.
parent::__construct( $widget_slug, esc_html_x( 'WPForms', 'Widget', 'wpforms-lite' ), $widget_ops, $control_ops );
}
/**
* Output the HTML for this widget.
*
* @since 1.0.2
*
* @param array $args An array of standard parameters for widgets in this theme.
* @param array $instance An array of settings for this widget instance.
*/
public function widget( $args, $instance ) {
// Merge with defaults.
$instance = wp_parse_args( (array) $instance, $this->defaults );
echo $args['before_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
// Title.
if ( ! empty( $instance['title'] ) ) {
echo $args['before_title'] . apply_filters( 'widget_title', $instance['title'], $instance, $this->id_base ) . $args['after_title']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
// Form.
if ( ! empty( $instance['form_id'] ) ) {
wpforms()->frontend->output( absint( $instance['form_id'] ), $instance['show_title'], $instance['show_desc'] );
}
echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
/**
* Deal with the settings when they are saved by the admin. Here is
* where any validation should be dealt with.
*
* @since 1.0.2
*
* @param array $new_instance An array of new settings as submitted by the admin.
* @param array $old_instance An array of the previous settings.
*
* @return array The validated and (if necessary) amended settings
*/
public function update( $new_instance, $old_instance ) {
$new_instance['title'] = wp_strip_all_tags( $new_instance['title'] );
$new_instance['form_id'] = ! empty( $new_instance['form_id'] ) ? (int) $new_instance['form_id'] : 0;
$new_instance['show_title'] = isset( $new_instance['show_title'] ) && $new_instance['show_title'] ? '1' : false;
$new_instance['show_desc'] = isset( $new_instance['show_desc'] ) && $new_instance['show_desc'] ? '1' : false;
return $new_instance;
}
/**
* Display the form for this widget on the Widgets page of the WP Admin area.
*
* @since 1.0.2
*
* @param array $instance An array of the current settings for this widget.
*/
public function form( $instance ) {
// Merge with defaults.
$instance = wp_parse_args( (array) $instance, $this->defaults );
?>
<p>
<label for="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>">
<?php echo esc_html( _x( 'Title:', 'Widget', 'wpforms-lite' ) ); ?>
</label>
<input type="text"
id="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>"
name="<?php echo esc_attr( $this->get_field_name( 'title' ) ); ?>"
value="<?php echo esc_attr( $instance['title'] ); ?>" class="widefat"/>
</p>
<p>
<label for="<?php echo esc_attr( $this->get_field_id( 'form_id' ) ); ?>">
<?php echo esc_html( _x( 'Form:', 'Widget', 'wpforms-lite' ) ); ?>
</label>
<select class="widefat"
id="<?php echo esc_attr( $this->get_field_id( 'form_id' ) ); ?>"
name="<?php echo esc_attr( $this->get_field_name( 'form_id' ) ); ?>">
<?php
$forms = wpforms()->form->get();
if ( ! empty( $forms ) ) {
echo '<option value="" selected disabled>' . esc_html_x( 'Select your form', 'Widget', 'wpforms-lite' ) . '</option>';
foreach ( $forms as $form ) {
echo '<option value="' . esc_attr( $form->ID ) . '" ' . selected( $instance['form_id'], $form->ID, false ) . '>' . esc_html( $form->post_title ) . '</option>';
}
} else {
echo '<option value="">' . esc_html_x( 'No forms', 'Widget', 'wpforms-lite' ) . '</option>';
}
?>
</select>
</p>
<p>
<input type="checkbox" id="<?php echo esc_attr( $this->get_field_id( 'show_title' ) ); ?>"
name="<?php echo esc_attr( $this->get_field_name( 'show_title' ) ); ?>" <?php checked( '1', $instance['show_title'] ); ?>>
<label for="<?php echo esc_attr( $this->get_field_id( 'show_title' ) ); ?>">
<?php echo esc_html( _x( 'Display form name', 'Widget', 'wpforms-lite' ) ); ?>
</label>
<br>
<input type="checkbox" id="<?php echo esc_attr( $this->get_field_id( 'show_desc' ) ); ?>"
name="<?php echo esc_attr( $this->get_field_name( 'show_desc' ) ); ?>" <?php checked( '1', $instance['show_desc'] ); ?>>
<label for="<?php echo esc_attr( $this->get_field_id( 'show_desc' ) ); ?>">
<?php echo esc_html( _x( 'Display form description', 'Widget', 'wpforms-lite' ) ); ?>
</label>
</p>
<?php
}
}
/**
* Register WPForms plugin widgets.
*/
function wpforms_register_widgets() {
register_widget( 'WPForms_Widget' );
}
add_action( 'widgets_init', 'wpforms_register_widgets' );

View File

@@ -0,0 +1,74 @@
<?php
/**
* WordPress core function polyfill for WordPress 5.2 - 5.4.
*
* @since 1.7.6
*/
if ( ! function_exists( 'wp_get_environment_type' ) ) {
/**
* Retrieves the current environment type.
*
* The type can be set via the `WP_ENVIRONMENT_TYPE` global system variable,
* or a constant of the same name.
*
* Possible values are 'local', 'development', 'staging', and 'production'.
* If not set, the type defaults to 'production'.
*
* @return string The current environment type.
*/
function wp_get_environment_type() { // phpcs:ignore Generic.Metrics.CyclomaticComplexity.TooHigh, WPForms.Comments.SinceTag.MissingSince
static $current_env = '';
if ( ! defined( 'WP_RUN_CORE_TESTS' ) && $current_env ) {
return $current_env;
}
$wp_environments = [
'local',
'development',
'staging',
'production',
];
// Add a note about the deprecated WP_ENVIRONMENT_TYPES constant.
if ( defined( 'WP_ENVIRONMENT_TYPES' ) && function_exists( '_deprecated_argument' ) ) {
// phpcs:disable WPForms.PHP.ValidateDomain.InvalidDomain
if ( function_exists( '__' ) ) {
/* translators: %s - WP_ENVIRONMENT_TYPES. */
$message = sprintf( __( 'The %s constant is no longer supported.' ), 'WP_ENVIRONMENT_TYPES' );
} else {
$message = sprintf( 'The %s constant is no longer supported.', 'WP_ENVIRONMENT_TYPES' );
}
// phpcs:enable WPForms.PHP.ValidateDomain.InvalidDomain
_deprecated_argument(
'define()',
'5.5.1',
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
$message
);
}
// Check if the environment variable has been set, if `getenv` is available on the system.
if ( function_exists( 'getenv' ) ) {
$has_env = getenv( 'WP_ENVIRONMENT_TYPE' );
if ( $has_env !== false ) {
$current_env = $has_env;
}
}
// Fetch the environment from a constant, this overrides the global system variable.
if ( defined( 'WP_ENVIRONMENT_TYPE' ) ) {
$current_env = WP_ENVIRONMENT_TYPE;
}
// Make sure the environment is an allowed one, and not accidentally set to an invalid value.
if ( ! in_array( $current_env, $wp_environments, true ) ) {
$current_env = 'production';
}
return $current_env;
}
}

View File

@@ -0,0 +1,371 @@
<?php
// phpcs:ignoreFile Generic.Files.OneObjectStructurePerFile.MultipleFound
namespace WPForms {
/**
* The removed class helps prevent fatal errors for clients
* that use some of the classes we are about to remove.
* Use the class extending instead of class_alias function.
*
* @since 1.8.0
*/
class Removed {
/**
* List of removed classes in the next format:
* Fully-Qualified Class Name => version.
*
* @since 1.8.0
*/
const CLASSES = [
'WPForms\Pro\Admin\Entries\DefaultScreen' => '1.8.2'
];
/**
* Inform clients that the class is removed.
*
* @since 1.8.0
*/
public function __construct() {
self::trigger_error();
}
/**
* Inform clients that the class is removed.
*
* @since 1.8.0
*
* @param string $name Property name.
*/
public function __get( $name ) {
self::trigger_error( $name );
}
/**
* Inform clients that the class is removed.
*
* @since 1.8.0
*
* @param string $name Property name.
* @param mixed $value Property value.
*/
public function __set( $name, $value ) {
self::trigger_error( $name );
}
/**
* Inform clients that the class is removed.
*
* @since 1.8.0
*
* @param string $name Property name.
*/
public function __isset( $name ) {
self::trigger_error( $name );
}
/**
* Inform clients that the class is removed.
*
* @since 1.8.0
*
* @param string $name Method name.
* @param array $arguments List of arguments.
*/
public function __call( $name, $arguments ) {
self::trigger_error( $name );
}
/**
* Inform clients that the class is removed.
*
* @since 1.8.0
*
* @param string $name Method name.
* @param array $arguments List of arguments.
*/
public static function __callStatic( $name, $arguments ) {
self::trigger_error( $name );
}
/**
* Inform clients that the class is removed.
*
* @since 1.8.0
*
* @param string $element_name Property or method name.
*/
private static function trigger_error( $element_name = '' ) {
$current_class = static::class;
$removed_element = $current_class;
if ( $element_name ) {
$removed_element .= '::' . $element_name;
}
$version = ! empty( self::CLASSES[ $current_class ] ) ? self::CLASSES[ $current_class ] : WPFORMS_VERSION;
trigger_error(
sprintf(
'%1$s has been removed in %2$s of the WPForms plugin',
esc_html( $removed_element ),
esc_html( $version )
),
E_USER_WARNING
);
}
}
}
namespace WPForms\Forms {
use WPForms\Removed;
class Loader extends Removed {}
}
namespace {
/**
* To be compatible with both WP 4.9 (that can run on PHP 5.2+) and WP 5.3+ (PHP 5.6+)
* we need to rewrite some core WP classes and tweak our own skins to not use PHP 5.6 splat operator (...$args)
* that were introduced in WP 5.3 in \WP_Upgrader_Skin::feedback().
* This alias is a safeguard to those developers who decided to use our internal class WPForms_Install_Silent_Skin,
* which we deleted.
*
* @since 1.5.6.1
*/
class_alias( 'WPForms\Helpers\PluginSilentUpgraderSkin', 'WPForms_Install_Silent_Skin' );
/**
* Legacy `WPForms_Addons` class was refactored and moved to the new `WPForms\Pro\Admin\Pages\Addons` class.
* This alias is a safeguard to those developers who use our internal class WPForms_Addons,
* which we deleted.
*
* @since 1.6.7
*/
class_alias( wpforms()->is_pro() ? 'WPForms\Pro\Admin\Pages\Addons' : 'WPForms\Lite\Admin\Pages\Addons', 'WPForms_Addons' );
/**
* This alias is a safeguard to those developers who decided to use our internal class WPForms_Smart_Tags,
* which we deleted.
*
* @since 1.6.7
*/
class_alias( wpforms()->is_pro() ? 'WPForms\Pro\SmartTags\SmartTags' : 'WPForms\SmartTags\SmartTags', 'WPForms_Smart_Tags' );
/**
* This alias is a safeguard to those developers who decided to use our internal class \WPForms\Providers\Loader,
* which we deleted.
*
* @since 1.7.3
*/
class_alias( '\WPForms\Providers\Providers', '\WPForms\Providers\Loader' );
/**
* Legacy `\WPForms\Admin\Notifications` class was refactored and moved to the new `\WPForms\Admin\Notifications\Notifications` class.
* This alias is a safeguard to those developers who use our internal class \WPForms\Admin\Notifications,
* which we deleted.
*
* @since 1.7.5
*/
class_alias( '\WPForms\Admin\Notifications\Notifications', '\WPForms\Admin\Notifications' );
/**
* Legacy `\WPForms_Field_Payment_Checkbox` class was refactored and moved to the new `\WPForms\Forms\Fields\PaymentCheckbox\Field` class.
* This alias is a safeguard to those developers who use our internal class \WPForms_Field_Payment_Checkbox,
* which we deleted.
*
* @since 1.8.2
*/
class_alias( '\WPForms\Forms\Fields\PaymentCheckbox\Field', '\WPForms_Field_Payment_Checkbox' );
/**
* Legacy `\WPForms_Field_Payment_Multiple` class was refactored and moved to the new `\WPForms\Forms\Fields\PaymentMultiple\Field` class.
* This alias is a safeguard to those developers who use our internal class \WPForms_Field_Payment_Multiple,
* which we deleted.
*
* @since 1.8.2
*/
class_alias( '\WPForms\Forms\Fields\PaymentMultiple\Field', '\WPForms_Field_Payment_Multiple' );
/**
* Legacy `\WPForms_Field_Payment_Single` class was refactored and moved to the new `\WPForms\Forms\Fields\PaymentSingle\Field` class.
* This alias is a safeguard to those developers who use our internal class \WPForms_Field_Payment_Single,
* which we deleted.
*
* @since 1.8.2
*/
class_alias( '\WPForms\Forms\Fields\PaymentSingle\Field', '\WPForms_Field_Payment_Single' );
/**
* Legacy `\WPForms_Field_Payment_Total` class was refactored and moved to the new `\WPForms\Forms\Fields\PaymentTotal\Field` class.
* This alias is a safeguard to those developers who use our internal class \WPForms_Field_Payment_Total,
* which we deleted.
*
* @since 1.8.2
*/
class_alias( '\WPForms\Forms\Fields\PaymentTotal\Field', '\WPForms_Field_Payment_Total' );
/**
* Legacy `\WPForms_Field_Payment_Select` class was refactored and moved to the new `\WPForms\Forms\Fields\PaymentSelect\Field` class.
* This alias is a safeguard to those developers who use our internal class \WPForms_Field_Payment_Select,
* which we deleted.
*
* @since 1.8.2
*/
class_alias( '\WPForms\Forms\Fields\PaymentSelect\Field', '\WPForms_Field_Payment_Select' );
/**
* Legacy `\WPForms\Migrations` class was refactored and moved to the new `\WPForms\Migrations\Migrations` class.
* This alias is a safeguard to those developers who use our internal class \WPForms\Migrations, which we deleted.
*
* @since 1.7.5
*/
class_alias( '\WPForms\Migrations\Migrations', '\WPForms\Migrations' );
if ( wpforms()->is_pro() ) {
/**
* Legacy `\WPForms\Pro\Migrations` class was refactored and moved to the new `\WPForms\Pro\Migrations\Migrations` class.
* This alias is a safeguard to those developers who use our internal class \WPForms\Migrations, which we deleted.
*
* @since 1.7.5
*/
class_alias( '\WPForms\Pro\Migrations\Migrations', '\WPForms\Pro\Migrations' );
/**
* Legacy `\WPForms\Pro\Integrations\TranslationsPress\Translations` class was refactored and moved to the new
* `\WPForms\Pro\Integrations\Translations\Translations` class.
* This alias is a safeguard to those developers who use our internal class \WPForms\Pro\Integrations\TranslationsPress, which we deleted.
*
* @since 1.8.2.2
*/
class_alias( '\WPForms\Pro\Integrations\Translations\Translations', '\WPForms\Pro\Integrations\TranslationsPress\Translations' );
}
/**
* Legacy `\WPForms_Frontend` class was refactored and moved to the new `\WPForms\Frontend\Frontend` class.
* This alias is a safeguard to those developers who use our internal class \WPForms_Frontend, which we deleted.
*
* @since 1.8.1
*/
class_alias( '\WPForms\Frontend\Frontend', '\WPForms_Frontend' );
/**
* This adds backwards compatibility after scoping the stripe lib and using our own prefix `\WPForms\Vendor\Stripe`.
* This alias is a safeguard for the users who update core plugin to 1.8.5 but have older version of stripe pro addon.
* Fire this right before autoloading of legacy classes so that there is no conflict with other stripe libs when aliasing.
*
* @since 1.8.5
*/
spl_autoload_register(
static function ( $class_name ) {
static $stripe_check_done = false;
static $aliases = [
'\WPForms\Vendor\Stripe\Charge' => 'Stripe\Charge',
'\WPForms\Vendor\Stripe\Customer' => 'Stripe\Customer',
'\WPForms\Vendor\Stripe\Subscription' => 'Stripe\Subscription',
'\WPForms\Vendor\Stripe\Invoice' => 'Stripe\Invoice',
'\WPForms\Vendor\Stripe\Exception\CardException' => 'Stripe\Exception\CardException',
'\WPForms\Vendor\Stripe\Source' => 'Stripe\Source',
];
if ( $stripe_check_done ) {
return;
}
// If class not for aliasing, bail.
if ( ! in_array( $class_name, $aliases, true ) ) {
return;
}
$stripe_check_done = true;
// If no Stripe Pro addon bail.
if ( ! defined( 'WPFORMS_STRIPE_VERSION' ) ) {
return;
}
// Version 3.2.0 has prefixed lib.
// Versions 2.11.0 and below already have the lib bundled, so they don't require alias.
if (
version_compare( WPFORMS_STRIPE_VERSION, '3.2.0', '>=' ) ||
version_compare( WPFORMS_STRIPE_VERSION, '2.11.0', '<=' )
) {
return;
}
// We only need to alias if we are using the legacy API version.
if ( ! \WPFormsStripe\Helpers::is_legacy_api_version() ) {
return;
}
// If a lib is already loaded by a third party plugin,
// checking the CardException class here as a niche to make sure it is the correct library.
if ( class_exists( '\Stripe\Exception\CardException', false ) ) {
return;
}
foreach ( $aliases as $prefixed => $alias ) {
class_alias( $prefixed, '\\' . $alias );
}
}
);
/**
* Get notification state, whether it's opened or closed.
*
* @since 1.4.1
* @deprecated 1.4.8
*
* @param int $notification_id Notification ID.
*
* @param int $form_id Form ID.
*
* @return string
*/
function wpforms_builder_notification_get_state( $form_id, $notification_id ) {
_deprecated_function( __FUNCTION__, '1.4.8 of the WPForms addon', 'wpforms_builder_settings_block_get_state()' );
return wpforms_builder_settings_block_get_state( $form_id, $notification_id, 'notification' );
}
/**
* Convert bytes to megabytes (or in some cases KB).
*
* @since 1.0.0
* @deprecated 1.6.2
*
* @param int $bytes Bytes to convert to a readable format.
*
* @return string
*/
function wpforms_size_to_megabytes( $bytes ) {
_deprecated_function( __FUNCTION__, '1.6.2 of the WPForms plugin', 'size_format()' );
return size_format( $bytes );
}
}
namespace WPForms\Pro\Admin\Entries {
/**
* Default Entries screen showed a chart and the form entries stats.
* Replaced with "WPForms\Pro\Admin\Entries\Overview".
*
* @since 1.5.5
* @deprecated 1.8.2
*/
class DefaultScreen extends \WPForms\Removed {}
}

View File

@@ -0,0 +1,854 @@
<?php
use WPForms\Helpers\Templates;
use WPForms\Tasks\Actions\EntryEmailsTask;
/**
* Emails.
*
* This class handles all (notification) emails sent by WPForms.
*
* Heavily influenced by the great AffiliateWP plugin by Pippin Williamson.
* https://github.com/AffiliateWP/AffiliateWP/blob/master/includes/emails/class-affwp-emails.php
*
* Note that this mailer class is no longer in active use and has been replaced with the "WPForms\Emails\Notifications" class.
* Please refer to the new mailer wrapper extension to extend or add further customizations.
*
* @deprecated 1.8.5
*
* @since 1.1.3
*/
class WPForms_WP_Emails {
/**
* Store the from address.
*
* @since 1.1.3
*
* @var string
*/
private $from_address;
/**
* Store the from name.
*
* @since 1.1.3
*
* @var string
*/
private $from_name;
/**
* Store the reply-to address.
*
* @since 1.1.3
*
* @var bool|string
*/
private $reply_to = false;
/**
* Store the reply-to name.
*
* @since 1.7.9
*
* @var bool|string
*/
private $reply_to_name = false;
/**
* Store the carbon copy addresses.
*
* @since 1.3.1
*
* @var string
*/
private $cc = false;
/**
* Store the email content type.
*
* @since 1.1.3
*
* @var string
*/
private $content_type;
/**
* Store the email headers.
*
* @since 1.1.3
*
* @var string
*/
private $headers;
/**
* Whether to send email in HTML.
*
* @since 1.1.3
*
* @var bool
*/
private $html = true;
/**
* The email template to use.
*
* @since 1.1.3
*
* @var string
*/
private $template;
/**
* Form data and settings.
*
* @since 1.1.3
*
* @var array
*/
public $form_data = [];
/**
* Fields, formatted, and sanitized.
*
* @since 1.1.3
*
* @var array
*/
public $fields = [];
/**
* Entry ID.
*
* @since 1.2.3
*
* @var int
*/
public $entry_id = '';
/**
* Notification ID that is currently being processed.
*
* @since 1.5.7
*
* @var int
*/
public $notification_id = '';
/**
* Get things going.
*
* @since 1.1.3
*/
public function __construct() {
if ( 'none' === $this->get_template() ) {
$this->html = false;
}
add_action( 'wpforms_email_send_before', [ $this, 'send_before' ] );
add_action( 'wpforms_email_send_after', [ $this, 'send_after' ] );
}
/**
* Set a property.
*
* @since 1.1.3
*
* @param string $key Object property key.
* @param mixed $value Object property value.
*/
public function __set( $key, $value ) {
$this->$key = $value;
}
/**
* Get the email from name.
*
* @since 1.1.3
*
* @return string The email from name
*/
public function get_from_name() {
if ( ! empty( $this->from_name ) ) {
$this->from_name = $this->process_tag( $this->from_name );
} else {
$this->from_name = get_bloginfo( 'name' );
}
return apply_filters( 'wpforms_email_from_name', wpforms_decode_string( $this->from_name ), $this );
}
/**
* Get the email from address.
*
* @since 1.1.3
*
* @return string The email from address.
*/
public function get_from_address() {
if ( ! empty( $this->from_address ) ) {
$this->from_address = $this->process_tag( $this->from_address );
} else {
$this->from_address = get_option( 'admin_email' );
}
return apply_filters( 'wpforms_email_from_address', wpforms_decode_string( $this->from_address ), $this );
}
/**
* Get the email reply-to.
*
* @since 1.1.3
*
* @return string The email reply-to address.
*/
public function get_reply_to() {
if ( ! empty( $this->reply_to ) ) {
$email = $this->reply_to;
// Optional custom format with a Reply-to Name specified: John Doe <john@doe.com>
// - starts with anything,
// - followed by space,
// - ends with <anything> (expected to be an email, validated later).
$regex = '/^(.+) (<.+>)$/';
$matches = [];
if ( preg_match( $regex, $this->reply_to, $matches ) ) {
$this->reply_to_name = wpforms_decode_string( $this->process_tag( $matches[1] ) );
$email = trim( $matches[2], '<> ' );
}
$this->reply_to = $this->process_tag( $email );
if ( ! is_email( $this->reply_to ) ) {
$this->reply_to = false;
$this->reply_to_name = false;
}
}
return apply_filters( 'wpforms_email_reply_to', wpforms_decode_string( $this->reply_to ), $this );
}
/**
* Get the email carbon copy addresses.
*
* @since 1.3.1
*
* @return string The email reply-to address.
*/
public function get_cc() {
if ( ! empty( $this->cc ) ) {
$this->cc = $this->process_tag( $this->cc );
$addresses = array_map( 'trim', explode( ',', $this->cc ) );
foreach ( $addresses as $key => $address ) {
if ( ! is_email( $address ) ) {
unset( $addresses[ $key ] );
}
}
$this->cc = implode( ',', $addresses );
}
return apply_filters( 'wpforms_email_cc', wpforms_decode_string( $this->cc ), $this );
}
/**
* Get the email content type.
*
* @since 1.1.3
*
* @return string The email content type.
*/
public function get_content_type() {
if ( ! $this->content_type && $this->html ) {
$this->content_type = apply_filters( 'wpforms_email_default_content_type', 'text/html', $this );
} elseif ( ! $this->html ) {
$this->content_type = 'text/plain';
}
return apply_filters( 'wpforms_email_content_type', $this->content_type, $this );
}
/**
* Get the email headers.
*
* @since 1.1.3
*
* @return string The email headers.
*/
public function get_headers() {
if ( ! $this->headers ) {
$this->headers = "From: {$this->get_from_name()} <{$this->get_from_address()}>\r\n";
if ( $this->get_reply_to() ) {
$this->headers .= $this->reply_to_name ?
"Reply-To: {$this->reply_to_name} <{$this->get_reply_to()}>\r\n" :
"Reply-To: {$this->get_reply_to()}\r\n";
}
if ( $this->get_cc() ) {
$this->headers .= "Cc: {$this->get_cc()}\r\n";
}
$this->headers .= "Content-Type: {$this->get_content_type()}; charset=utf-8\r\n";
}
return apply_filters( 'wpforms_email_headers', $this->headers, $this );
}
/**
* Build the email.
*
* @since 1.1.3
*
* @param string $message The email message.
*
* @return string
*/
public function build_email( $message ) {
// Plain text email shortcut.
if ( false === $this->html ) {
$message = $this->process_tag( $message );
$message = str_replace( '{all_fields}', $this->wpforms_html_field_value( false ), $message );
return apply_filters( 'wpforms_email_message', wpforms_decode_string( $message ), $this );
}
/*
* Generate an HTML email.
*/
ob_start();
$this->get_template_part( 'header', $this->get_template(), true );
// Hooks into the email header.
do_action( 'wpforms_email_header', $this );
$this->get_template_part( 'body', $this->get_template(), true );
// Hooks into the email body.
do_action( 'wpforms_email_body', $this );
$this->get_template_part( 'footer', $this->get_template(), true );
// Hooks into the email footer.
do_action( 'wpforms_email_footer', $this );
$message = $this->process_tag( $message );
$message = nl2br( $message );
$body = ob_get_clean();
$message = str_replace( '{email}', $message, $body );
$message = str_replace( '{all_fields}', $this->wpforms_html_field_value( true ), $message );
$message = make_clickable( $message );
return apply_filters( 'wpforms_email_message', $message, $this );
}
/**
* Send the email.
*
* @since 1.1.3
*
* @param string $to The To address.
* @param string $subject The subject line of the email.
* @param string $message The body of the email.
* @param array $attachments Attachments to the email.
*
* @return bool
*/
public function send( $to, $subject, $message, $attachments = [] ) {
if ( ! did_action( 'init' ) && ! did_action( 'admin_init' ) ) {
_doing_it_wrong( __FUNCTION__, esc_html__( 'You cannot send emails with WPForms_WP_Emails() until init/admin_init has been reached.', 'wpforms-lite' ), null );
return false;
}
// Don't send anything if emails have been disabled.
if ( $this->is_email_disabled() ) {
return false;
}
// Don't send if email address is invalid.
if ( ! is_email( $to ) ) {
return false;
}
// Hooks before email is sent.
do_action( 'wpforms_email_send_before', $this );
// Deprecated filter for $attachments.
$attachments = apply_filters_deprecated(
'wpforms_email_attachments',
[ $attachments, $this ],
'1.5.7 of the WPForms plugin',
'wpforms_emails_send_email_data'
);
/*
* Allow to filter data on per-email basis,
* useful for localizations based on recipient email address, form settings,
* or for specific notifications - whatever available in WPForms_WP_Emails class.
*/
$data = apply_filters(
'wpforms_emails_send_email_data',
[
'to' => $to,
'subject' => $subject,
'message' => $message,
'headers' => $this->get_headers(),
'attachments' => $attachments,
],
$this
);
$send_same_process = apply_filters(
'wpforms_tasks_entry_emails_trigger_send_same_process',
false,
$this->fields,
! empty( wpforms()->entry ) ? wpforms()->entry->get( $this->entry_id ) : [],
$this->form_data,
$this->entry_id,
'entry'
);
if (
$send_same_process ||
! empty( $this->form_data['settings']['disable_entries'] )
) {
// Let's do this NOW.
$result = wp_mail(
$data['to'],
$this->get_prepared_subject( $data['subject'] ),
$this->build_email( $data['message'] ),
$data['headers'],
$data['attachments']
);
} else {
// Schedule the email.
$result = (bool) ( new EntryEmailsTask() )
->params(
$data['to'],
$this->get_prepared_subject( $data['subject'] ),
$this->build_email( $data['message'] ),
$data['headers'],
$data['attachments']
)
->register();
}
/**
* Hooks after the email is sent.
*
* @since 1.1.3
*
* @param WPForms_WP_Emails $this Current instance of this object.
*/
do_action( 'wpforms_email_send_after', $this ); // phpcs:ignore WPForms.PHP.ValidateHooks.InvalidHookName
return $result;
}
/**
* Add filters/actions before the email is sent.
*
* @since 1.1.3
*/
public function send_before() {
add_filter( 'wp_mail_from', [ $this, 'get_from_address' ] );
add_filter( 'wp_mail_from_name', [ $this, 'get_from_name' ] );
add_filter( 'wp_mail_content_type', [ $this, 'get_content_type' ] );
}
/**
* Remove filters/actions after the email is sent.
*
* @since 1.1.3
*/
public function send_after() {
remove_filter( 'wp_mail_from', [ $this, 'get_from_address' ] );
remove_filter( 'wp_mail_from_name', [ $this, 'get_from_name' ] );
remove_filter( 'wp_mail_content_type', [ $this, 'get_content_type' ] );
}
/**
* Convert text formatted HTML. This is primarily for turning line breaks
* into <p> and <br/> tags.
*
* @since 1.1.3
*
* @param string $message Text to convert.
*
* @return string
*/
public function text_to_html( $message ) {
if ( 'text/html' === $this->content_type || true === $this->html ) {
$message = wpautop( $message );
}
return $message;
}
/**
* Process a smart tag.
* Decodes entities and sanitized (keeping line breaks) by default.
*
* @uses wpforms_decode_string()
*
* @since 1.1.3
* @since 1.6.0 Deprecated 2 params: $sanitize, $linebreaks.
*
* @param string $string String that may contain tags.
*
* @return string
*/
public function process_tag( $string = '' ) {
return wpforms_process_smart_tags( $string, $this->form_data, $this->fields, $this->entry_id );
}
/**
* Process the all fields smart tag if present.
*
* @since 1.1.3
*
* @param bool $is_html_email Toggle to use HTML or plaintext.
*
* @return string
*/
public function wpforms_html_field_value( $is_html_email = true ) { // phpcs:ignore
if ( empty( $this->fields ) ) {
return '';
}
if ( empty( $this->form_data['fields'] ) ) {
$is_html_email = false;
}
$message = '';
if ( $is_html_email ) {
/*
* HTML emails.
*/
ob_start();
// Hooks into the email field.
do_action( 'wpforms_email_field', $this );
$this->get_template_part( 'field', $this->get_template(), true );
$field_template = ob_get_clean();
// Check to see if user has added support for field type.
$other_fields = apply_filters( 'wpforms_email_display_other_fields', [], $this );
$x = 1;
foreach ( $this->form_data['fields'] as $field_id => $field ) {
$field_name = '';
$field_val = '';
// If the field exists in the form_data but not in the final
// field data, then it's a non-input based field, "other fields".
if ( empty( $this->fields[ $field_id ] ) ) {
if ( empty( $other_fields ) || ! in_array( $field['type'], $other_fields, true ) ) {
continue;
}
if ( $field['type'] === 'divider' ) {
$field_name = ! empty( $field['label'] ) ? str_repeat( '&mdash;', 3 ) . ' ' . $field['label'] . ' ' . str_repeat( '&mdash;', 3 ) : null;
$field_val = ! empty( $field['description'] ) ? $field['description'] : '';
} elseif ( $field['type'] === 'pagebreak' ) {
if ( ! empty( $field['position'] ) && $field['position'] === 'bottom' ) {
continue;
}
$title = ! empty( $field['title'] ) ? $field['title'] : esc_html__( 'Page Break', 'wpforms-lite' );
$field_name = str_repeat( '&mdash;', 6 ) . ' ' . $title . ' ' . str_repeat( '&mdash;', 6 );
} elseif ( $field['type'] === 'html' ) {
if ( $this->is_field_conditionally_hidden( $field['id'] ) ) {
continue;
}
$field_name = ! empty( $field['name'] ) ? $field['name'] : esc_html__( 'HTML / Code Block', 'wpforms-lite' );
$field_val = $field['code'];
} elseif ( $field['type'] === 'content' ) {
if ( $this->is_field_conditionally_hidden( $field['id'] ) ) {
continue;
}
$field_name = esc_html__( 'Content', 'wpforms-lite' );
$field_val = $field['content'];
}
} else {
if (
! apply_filters( 'wpforms_email_display_empty_fields', false ) &&
( ! isset( $this->fields[ $field_id ]['value'] ) || (string) $this->fields[ $field_id ]['value'] === '' )
) {
continue;
}
$field_name = isset( $this->fields[ $field_id ]['name'] ) ? $this->fields[ $field_id ]['name'] : '';
$field_val = empty( $this->fields[ $field_id ]['value'] ) && ! is_numeric( $this->fields[ $field_id ]['value'] ) ? '<em>' . esc_html__( '(empty)', 'wpforms-lite' ) . '</em>' : $this->fields[ $field_id ]['value'];
}
if ( empty( $field_name ) && null !== $field_name ) {
$field_name = sprintf( /* translators: %d - field ID. */
esc_html__( 'Field ID #%d', 'wpforms-lite' ),
absint( $field['id'] )
);
}
$field_item = $field_template;
if ( 1 === $x ) {
$field_item = str_replace( 'border-top:1px solid #dddddd;', '', $field_item );
}
$field_item = str_replace( '{field_name}', $field_name, $field_item );
$field_item = str_replace(
'{field_value}',
apply_filters(
'wpforms_html_field_value',
$field_val,
isset( $this->fields[ $field_id ] ) ? $this->fields[ $field_id ] : $field,
$this->form_data,
'email-html'
),
$field_item
);
$message .= wpautop( $field_item );
$x ++;
}
} else {
/*
* Plain Text emails.
*/
foreach ( $this->fields as $field ) {
if (
! apply_filters( 'wpforms_email_display_empty_fields', false ) &&
( ! isset( $field['value'] ) || (string) $field['value'] === '' )
) {
continue;
}
$field_val = empty( $field['value'] ) && ! is_numeric( $field['value'] ) ? esc_html__( '(empty)', 'wpforms-lite' ) : $field['value'];
$field_name = $field['name'];
if ( empty( $field_name ) ) {
$field_name = sprintf( /* translators: %d - field ID. */
esc_html__( 'Field ID #%d', 'wpforms-lite' ),
absint( $field['id'] )
);
}
$message .= '--- ' . $field_name . " ---\r\n\r\n";
$field_value = $field_val . "\r\n\r\n";
$message .= apply_filters( 'wpforms_plaintext_field_value', $field_value, $field, $this->form_data );
}
}
if ( empty( $message ) ) {
$empty_message = esc_html__( 'An empty form was submitted.', 'wpforms-lite' );
$message = $is_html_email ? wpautop( $empty_message ) : $empty_message;
}
return $message;
}
/**
* Email kill switch if needed.
*
* @since 1.1.3
*
* @return bool
*/
public function is_email_disabled() {
return (bool) apply_filters( 'wpforms_disable_all_emails', false, $this );
}
/**
* Get the enabled email template.
*
* @since 1.1.3
*
* @return string When filtering return 'none' to switch to text/plain email.
*/
public function get_template() {
if ( ! $this->template ) {
$this->template = wpforms_setting( 'email-template', 'default' );
}
return apply_filters( 'wpforms_email_template', $this->template );
}
/**
* Retrieve a template part. Taken from bbPress.
*
* @since 1.1.3
*
* @param string $slug Template file slug.
* @param string $name Optional. Default null.
* @param bool $load Maybe load.
*
* @return string
*/
public function get_template_part( $slug, $name = null, $load = true ) {
// Setup possible parts.
$templates = [];
if ( isset( $name ) ) {
$templates[] = $slug . '-' . $name . '.php';
}
$templates[] = $slug . '.php';
// Return the part that is found.
return $this->locate_template( $templates, $load, false );
}
/**
* Retrieve the name of the highest priority template file that exists.
*
* Search in the STYLESHEETPATH before TEMPLATEPATH so that themes which
* inherit from a parent theme can just overload one file. If the template is
* not found in either of those, it looks in the theme-compat folder last.
*
* Taken from bbPress.
*
* @since 1.1.3
*
* @param string|array $template_names Template file(s) to search for, in order.
* @param bool $load If true the template file will be loaded if it is found.
* @param bool $require_once Whether to require_once or require. Default true.
* Has no effect if $load is false.
*
* @return string The template filename if one is located.
*/
public function locate_template( $template_names, $load = false, $require_once = true ) {
// No file found yet.
$located = false;
// Try to find a template file.
foreach ( (array) $template_names as $template_name ) {
// Continue if template is empty.
if ( empty( $template_name ) ) {
continue;
}
// Trim off any slashes from the template name.
$template_name = ltrim( $template_name, '/' );
// Try locating this template file by looping through the template paths.
foreach ( $this->get_theme_template_paths() as $template_path ) {
$validated_path = Templates::validate_safe_path(
$template_path . $template_name,
[ 'theme', 'plugins' ]
);
if ( $validated_path ) {
$located = $validated_path;
break;
}
}
}
if ( ( true === $load ) && ! empty( $located ) ) {
load_template( $located, $require_once );
}
return $located;
}
/**
* Return a list of paths to check for template locations
*
* @since 1.1.3
*
* @return array
*/
public function get_theme_template_paths() {
$template_dir = 'wpforms-email';
$file_paths = [
1 => trailingslashit( get_stylesheet_directory() ) . $template_dir,
10 => trailingslashit( get_template_directory() ) . $template_dir,
100 => WPFORMS_PLUGIN_DIR . 'includes/emails/templates',
];
$file_paths = apply_filters( 'wpforms_email_template_paths', $file_paths );
// Sort the file paths based on priority.
ksort( $file_paths, SORT_NUMERIC );
return array_map( 'trailingslashit', $file_paths );
}
/**
* Perform email subject preparation: process tags, remove new lines, etc.
*
* @since 1.6.1
*
* @param string $subject Email subject to post-process.
*
* @return string
*/
private function get_prepared_subject( $subject ) {
$subject = $this->process_tag( $subject );
$subject = trim( str_replace( [ "\r\n", "\r", "\n" ], ' ', $subject ) );
return wpforms_decode_string( $subject );
}
/**
* If CL is enabled and the field is conditionally hidden, hide it from message.
*
* @since 1.7.9
*
* @param int $field_id Field ID.
*
* @return bool
*/
private function is_field_conditionally_hidden( $field_id ) {
return ! empty( $this->form_data['fields'][ $field_id ]['conditionals'] ) && ! wpforms_conditional_logic_fields()->field_is_visible( $this->form_data, $field_id );
}
}

View File

@@ -0,0 +1,16 @@
<?php
/**
* Email Body
*
* Heavily influenced by the great AffiliateWP plugin by Pippin Williamson.
* https://github.com/AffiliateWP/AffiliateWP/tree/master/templates/emails
*
* @since 1.1.3
*/
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
?>
{email}

View File

@@ -0,0 +1,328 @@
<?php
/**
* Default email template.
*
* Don't forget to run final template through
* http://templates.mailchimp.com/resources/inline-css/
*
* @since 1.1.3
*/
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
$header_image = wpforms_setting( 'email-header-image', false );
?>
<!doctype html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office">
<head>
<!--[if gte mso 15]>
<xml>
<o:OfficeDocumentSettings>
<o:AllowPNG/>
<o:PixelsPerInch>96</o:PixelsPerInch>
</o:OfficeDocumentSettings>
</xml>
<![endif]-->
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title></title>
<style type="text/css">
p{
margin:10px 0;
padding:0;
}
table{
border-collapse:collapse;
}
h1,h2,h3,h4,h5,h6{
display:block;
margin:0;
padding:0;
}
img,a img{
border:0;
height:auto;
outline:none;
text-decoration:none;
}
body,#bodyTable,#bodyCell{
height:100%;
margin:0;
padding:0;
width:100%;
}
#outlook a{
padding:0;
}
img{
-ms-interpolation-mode:bicubic;
}
table{
mso-table-lspace:0pt;
mso-table-rspace:0pt;
}
.ReadMsgBody{
width:100%;
}
.ExternalClass{
width:100%;
}
p,a,li,td,blockquote{
mso-line-height-rule:exactly;
}
a[href^=tel],a[href^=sms]{
color:inherit;
cursor:default;
text-decoration:none;
}
p,a,li,td,body,table,blockquote{
-ms-text-size-adjust:100%;
-webkit-text-size-adjust:100%;
}
.ExternalClass,.ExternalClass p,.ExternalClass td,.ExternalClass div,.ExternalClass span,.ExternalClass font{
line-height:100%;
}
a[x-apple-data-detectors]{
color:inherit !important;
text-decoration:none !important;
font-size:inherit !important;
font-family:inherit !important;
font-weight:inherit !important;
line-height:inherit !important;
}
#bodyCell{
padding:50px 50px;
}
.templateContainer{
max-width:600px !important;
border:0;
}
a.mcnButton{
display:block;
}
.mcnTextContent{
word-break:break-word;
}
.mcnTextContent img{
height:auto !important;
}
.mcnDividerBlock{
table-layout:fixed !important;
}
/***** Make theme edits below if needed *****/
/* Page - Background Style */
body,#bodyTable{
background-color:#e9eaec;
}
/* Page - Heading 1 */
h1{
color:#202020;
font-family: 'Helvetica Neue', Helvetica, Arial, 'Lucida Grande', sans-serif;
font-size:26px;
font-style:normal;
font-weight:bold;
line-height:125%;
letter-spacing:normal;
text-align:left;
}
/* Page - Heading 2 */
h2{
color:#202020;
font-family: 'Helvetica Neue', Helvetica, Arial, 'Lucida Grande', sans-serif;
font-size:22px;
font-style:normal;
font-weight:bold;
line-height:125%;
letter-spacing:normal;
text-align:left;
}
/* Page - Heading 3 */
h3{
color:#202020;
font-family: 'Helvetica Neue', Helvetica, Arial, 'Lucida Grande', sans-serif;
font-size:20px;
font-style:normal;
font-weight:bold;
line-height:125%;
letter-spacing:normal;
text-align:left;
}
/* Page - Heading 4 */
h4{
color:#202020;
font-family: 'Helvetica Neue', Helvetica, Arial, 'Lucida Grande', sans-serif;
font-size:18px;
font-style:normal;
font-weight:bold;
line-height:125%;
letter-spacing:normal;
text-align:left;
}
/* Header - Header Style */
#templateHeader{
border-top:0;
border-bottom:0;
padding-top:0px;
padding-bottom:20px;
}
/* Body - Body Style */
#templateBody{
background-color:#FFFFFF;
border-top:0;
border: 1px solid #c1c1c1;
padding-top:0;
padding-bottom:0px;
}
/* Body -Body Text */
#templateBody .mcnTextContent,
#templateBody .mcnTextContent p{
color:#555555;
font-family: 'Helvetica Neue', Helvetica, Arial, 'Lucida Grande', sans-serif;
font-size:14px;
line-height:150%;
text-align:left;
}
/* Body - Body Link */
#templateBody .mcnTextContent a,
#templateBody .mcnTextContent p a{
color:#ff7f50;
font-weight:normal;
text-decoration:underline;
}
/* Footer - Footer Style */
#templateFooter{
background-color:#e9eaec;
border-top:0;
border-bottom:0;
padding-top:12px;
padding-bottom:12px;
}
/* Footer - Footer Text */
#templateFooter .mcnTextContent,
#templateFooter .mcnTextContent p{
color:#cccccc;
font-family: 'Helvetica Neue', Helvetica, Arial, 'Lucida Grande', sans-serif;
font-size:12px;
line-height:150%;
text-align:center;
}
/* Footer - Footer Link */
#templateFooter .mcnTextContent a,
#templateFooter .mcnTextContent p a{
color:#cccccc;
font-weight:normal;
text-decoration:underline;
}
@media only screen and (min-width:768px){
.templateContainer{
width:600px !important;
}
}
@media only screen and (max-width: 480px){
body,table,td,p,a,li,blockquote{
-webkit-text-size-adjust:none !important;
}
}
@media only screen and (max-width: 480px){
body{
width:100% !important;
min-width:100% !important;
}
}
@media only screen and (max-width: 480px){
#bodyCell{
padding:20px !important;
}
}
@media only screen and (max-width: 480px){
.mcnTextContentContainer{
max-width:100% !important;
width:100% !important;
}
}
</style>
</head>
<body>
<center>
<table align="center" border="0" cellpadding="0" cellspacing="0" height="100%" width="100%" id="bodyTable">
<tr>
<td align="center" valign="top" id="bodyCell">
<!-- BEGIN TEMPLATE // -->
<!--[if gte mso 9]>
<table align="center" border="0" cellspacing="0" cellpadding="0" width="600" style="width:600px;">
<tr>
<td align="center" valign="top" width="600" style="width:600px;">
<![endif]-->
<table border="0" cellpadding="0" cellspacing="0" width="100%" class="templateContainer">
<?php
if ( !empty( $header_image ) ) {
echo '<tr><td valign="top" align="center" id="templateHeader" style="padding-bottom:20px;text-align:center;">';
echo '<img src="' . esc_url( $header_image ) . '" alt="' . get_bloginfo( 'name' ) . '" />';
echo '</td></tr>';
}
?>
<tr>
<td valign="top" id="templateBody">
<table border="0" cellpadding="0" cellspacing="0" width="100%" class="mcnTextBlock" style="min-width:100%;">
<tbody class="mcnTextBlockOuter">
<tr>
<td valign="top" class="mcnTextBlockInner">
<table align="left" border="0" cellpadding="0" cellspacing="0" width="100%" style="min-width:100%;" class="mcnTextContentContainer">
<tbody>
<tr>
<td valign="top" class="mcnTextContent" style="padding-top:30px; padding-right: 30px; padding-bottom: 30px; padding-left: 30px;">
<!-- Content -->
<h1>Content.</h1>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
<tr>
<td valign="top" id="templateFooter">
<table border="0" cellpadding="0" cellspacing="0" width="100%" class="mcnTextBlock" style="min-width:100%;">
<tbody class="mcnTextBlockOuter">
<tr>
<td valign="top" class="mcnTextBlockInner">
<table align="left" border="0" cellpadding="0" cellspacing="0" width="100%" style="min-width:100%;" class="mcnTextContentContainer">
<tbody>
<tr>
<td valign="top" class="mcnTextContent" style="padding-top:9px; padding-right: 18px; padding-bottom: 9px; padding-left: 18px;">
<!-- Footer content -->
Footer
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</table>
<!--[if gte mso 9]>
</td>
</tr>
</table>
<![endif]-->
<!-- // END TEMPLATE -->
</td>
</tr>
</table>
</center>
</body>
</html>

View File

@@ -0,0 +1,18 @@
<?php
/**
* Email form field entry.
*
* This is used with the {all_fields} smart tag.
*
* @since 1.1.3
*/
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
?>
<table align="left" border="0" cellpadding="0" cellspacing="0" width="100%" style="border-top:1px solid #dddddd; display:block;min-width: 100%;border-collapse: collapse;width:100%;"><tbody>
<tr><td style="color:#333333;padding-top: 20px;padding-bottom: 3px;"><strong>{field_name}</strong></td></tr>
<tr><td style="color:#555555;padding-top: 3px;padding-bottom: 20px;">{field_value}</td></tr>
</tbody></table>

View File

@@ -0,0 +1,65 @@
<?php
/**
* Email Footer.
*
* @since 1.1.3
*/
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
$background_color = wpforms_setting( 'email-background-color', '#e9eaec' );
?>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
<tr>
<td valign="top" id="templateFooter" style="mso-line-height-rule: exactly;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;background-color: <?php echo $background_color; ?>;border-top: 0;border-bottom: 0;padding-top: 12px;padding-bottom: 12px;">
<table border="0" cellpadding="0" cellspacing="0" width="100%" class="mcnTextBlock" style="min-width: 100%;border-collapse: collapse;mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;">
<tbody class="mcnTextBlockOuter">
<tr>
<td valign="top" class="mcnTextBlockInner" style="mso-line-height-rule: exactly;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;">
<table align="left" border="0" cellpadding="0" cellspacing="0" width="100%" style="min-width: 100%;border-collapse: collapse;mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;" class="mcnTextContentContainer">
<tbody>
<tr>
<td valign="top" class="mcnTextContent" style="padding-top: 9px;padding-right: 18px;padding-bottom: 9px;padding-left: 18px;mso-line-height-rule: exactly;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;word-break: break-word;color: #aaa;font-family: Helvetica;font-size: 12px;line-height: 150%;text-align: center;">
<!-- Footer content -->
<?php
/* translators: %s - link to the site. */
$footer = sprintf( esc_html__( 'Sent from %s', 'wpforms-lite' ), '<a href="' . esc_url( home_url() ) . '" style="color:#bbbbbb;">' . wp_specialchars_decode( get_bloginfo( 'name' ) ) . '</a>' );
echo apply_filters( 'wpforms_email_footer_text', $footer );
?>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</table>
<!--[if gte mso 9]>
</td>
</tr>
</table>
<![endif]-->
<!-- // END TEMPLATE -->
</td>
</tr>
</table>
</center>
</body>
</html>

View File

@@ -0,0 +1,306 @@
<?php
/**
* Email Header
*
* @since 1.1.3
*/
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
$header_image = wpforms_setting( 'email-header-image', false );
$background_color = wpforms_setting( 'email-background-color', '#e9eaec' );
$text_direction = is_rtl() ? 'rtl' : 'ltr';
?>
<!doctype html>
<html dir="<?php echo $text_direction; ?>" xmlns="http://www.w3.org/1999/xhtml" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office">
<head>
<!--[if gte mso 15]>
<xml>
<o:OfficeDocumentSettings>
<o:AllowPNG/>
<o:PixelsPerInch>96</o:PixelsPerInch>
</o:OfficeDocumentSettings>
</xml>
<![endif]-->
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title><?php echo get_bloginfo( 'name' ); ?></title>
<style type="text/css">
p{
margin:10px 0;
padding:0;
}
table{
border-collapse:collapse;
}
h1,h2,h3,h4,h5,h6{
display:block;
margin:0;
padding:0;
}
img,a img{
border:0;
height:auto;
outline:none;
text-decoration:none;
}
body,#bodyTable,#bodyCell{
height:100%;
margin:0;
padding:0;
width:100%;
}
#outlook a{
padding:0;
}
img{
-ms-interpolation-mode:bicubic;
}
table{
mso-table-lspace:0pt;
mso-table-rspace:0pt;
}
.ReadMsgBody{
width:100%;
}
.ExternalClass{
width:100%;
}
p,a,li,td,blockquote{
mso-line-height-rule:exactly;
}
a[href^=tel],a[href^=sms]{
color:inherit;
cursor:default;
text-decoration:none;
}
p,a,li,td,body,table,blockquote{
-ms-text-size-adjust:100%;
-webkit-text-size-adjust:100%;
}
.ExternalClass,.ExternalClass p,.ExternalClass td,.ExternalClass div,.ExternalClass span,.ExternalClass font{
line-height:100%;
}
a[x-apple-data-detectors]{
color:inherit !important;
text-decoration:none !important;
font-size:inherit !important;
font-family:inherit !important;
font-weight:inherit !important;
line-height:inherit !important;
}
#bodyCell{
padding:50px 50px;
}
.templateContainer{
max-width:600px !important;
border:0;
}
a.mcnButton{
display:block;
}
.mcnTextContent{
word-break:break-word;
}
.mcnTextContent img{
height:auto !important;
}
.mcnDividerBlock{
table-layout:fixed !important;
}
/***** Make theme edits below if needed *****/
/* Page - Background Style */
body,#bodyTable{
background-color:<?php echo $background_color; ?>;
}
/* Page - Heading 1 */
h1{
color:#202020;
font-family: 'Helvetica Neue', Helvetica, Arial, 'Lucida Grande', sans-serif;
font-size:26px;
font-style:normal;
font-weight:bold;
line-height:125%;
letter-spacing:normal;
}
/* Page - Heading 2 */
h2{
color:#202020;
font-family: 'Helvetica Neue', Helvetica, Arial, 'Lucida Grande', sans-serif;
font-size:22px;
font-style:normal;
font-weight:bold;
line-height:125%;
letter-spacing:normal;
}
/* Page - Heading 3 */
h3{
color:#202020;
font-family: 'Helvetica Neue', Helvetica, Arial, 'Lucida Grande', sans-serif;
font-size:20px;
font-style:normal;
font-weight:bold;
line-height:125%;
letter-spacing:normal;
}
/* Page - Heading 4 */
h4{
color:#202020;
font-family: 'Helvetica Neue', Helvetica, Arial, 'Lucida Grande', sans-serif;
font-size:18px;
font-style:normal;
font-weight:bold;
line-height:125%;
letter-spacing:normal;
}
/* Header - Header Style */
#templateHeader{
border-top:0;
border-bottom:0;
padding-top:0;
padding-bottom:20px;
text-align: center;
}
/* Body - Body Style */
#templateBody{
background-color:#FFFFFF;
border-top:0;
border: 1px solid #c1c1c1;
padding-top:0;
padding-bottom:0px;
}
/* Body -Body Text */
#templateBody .mcnTextContent,
#templateBody .mcnTextContent p{
color:#555555;
font-family: 'Helvetica Neue', Helvetica, Arial, 'Lucida Grande', sans-serif;
font-size:14px;
line-height:150%;
}
/* Body - Body Link */
#templateBody .mcnTextContent a,
#templateBody .mcnTextContent p a{
color:#ff7f50;
font-weight:normal;
text-decoration:underline;
}
/* Footer - Footer Style */
#templateFooter{
background-color:<?php echo $background_color; ?>;
border-top:0;
border-bottom:0;
padding-top:12px;
padding-bottom:12px;
}
/* Footer - Footer Text */
#templateFooter .mcnTextContent,
#templateFooter .mcnTextContent p{
color:#cccccc;
font-family: 'Helvetica Neue', Helvetica, Arial, 'Lucida Grande', sans-serif;
font-size:12px;
line-height:150%;
text-align:center;
}
/* Footer - Footer Link */
#templateFooter .mcnTextContent a,
#templateFooter .mcnTextContent p a{
color:#cccccc;
font-weight:normal;
text-decoration:underline;
}
@media only screen and (min-width:768px){
.templateContainer{
width:600px !important;
}
}
@media only screen and (max-width: 480px){
body,table,td,p,a,li,blockquote{
-webkit-text-size-adjust:none !important;
}
}
@media only screen and (max-width: 480px){
body{
width:100% !important;
min-width:100% !important;
}
}
@media only screen and (max-width: 680px){
#bodyCell{
padding:20px 20px !important;
}
}
@media only screen and (max-width: 480px){
.mcnTextContentContainer{
max-width:100% !important;
width:100% !important;
}
}
/* Rich Text compatibility - image alignment. */
.mcnTextContentContainer table tbody,
.mcnTextContentContainer table tbody tr,
.mcnTextContentContainer table tbody td {
display: block;
}
.mcnTextContentContainer p::after {
content: "";
clear: both;
display: block;
}
.mcnTextContentContainer p .alignleft, .mcnTextContentContainer li .alignleft {
float: left;
margin-right: 16px;
margin-top: 8px;
margin-bottom: 8px;
}
.mcnTextContentContainer p .aligncenter, .mcnTextContentContainer li .aligncenter {
display: block;
margin-left: auto;
margin-right: auto;
}
.mcnTextContentContainer p .alignright, .mcnTextContentContainer li .alignright {
float: right;
margin-left: 16px;
margin-top: 8px;
margin-bottom: 8px;
}
/* styling lists */
.mcnTextContentContainer li {
list-style-position: inside;
mso-text-indent-alt: 0;
}
</style>
</head>
<body style="height: 100%;margin: 0;padding: 0;width: 100%;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;background-color: <?php echo $background_color; ?>;">
<!-- Don't forget to run final template through http://templates.mailchimp.com/resources/inline-css/ -->
<center>
<table align="center" border="0" cellpadding="0" cellspacing="0" height="100%" width="100%" id="bodyTable" style="border-collapse: collapse;mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;height: 100%;margin: 0;padding: 0;width: 100%;background-color: <?php echo $background_color; ?>;">
<tr>
<td align="center" valign="top" id="bodyCell" style="mso-line-height-rule: exactly;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;height: 100%;margin: 0;padding: 50px 50px;width: 100%;">
<!-- BEGIN TEMPLATE // -->
<!--[if gte mso 9]>
<table align="center" border="0" cellspacing="0" cellpadding="0" width="600" style="width:600px;">
<tr>
<td align="center" valign="top" width="600" style="width:600px;">
<![endif]-->
<table border="0" cellpadding="0" cellspacing="0" width="100%" class="templateContainer" style="border-collapse: collapse;mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;border: 0;max-width: 600px !important;">
<?php
if ( ! empty( $header_image ) ) {
echo '<tr><td valign="top" align="center" id="templateHeader" style="padding-bottom:20px;text-align:center;">';
echo '<img src="' . esc_url( $header_image ) . '" alt="' . esc_attr( get_bloginfo( 'name' ) ) . '" />';
echo '</td></tr>';
}
?>
<tr>
<td valign="top" id="templateBody" style="mso-line-height-rule: exactly;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;background-color: #FFFFFF;border-top: 0;border: 1px solid #c1c1c1;padding-top: 0;padding-bottom: 0px;">
<table border="0" cellpadding="0" cellspacing="0" width="100%" class="mcnTextBlock" style="min-width: 100%;border-collapse: collapse;mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;">
<tbody class="mcnTextBlockOuter">
<tr>
<td valign="top" class="mcnTextBlockInner" style="mso-line-height-rule: exactly;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;">
<table align="left" border="0" cellpadding="0" cellspacing="0" width="100%" style="min-width: 100%;border-collapse: collapse;mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;" class="mcnTextContentContainer">
<tbody>
<tr>
<td valign="top" style="padding-top: 30px;padding-right: 30px;padding-bottom: 30px;padding-left: 30px;" class="mcnTextContent">

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,780 @@
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Checkbox field.
*
* @since 1.0.0
*/
class WPForms_Field_Checkbox extends WPForms_Field {
/**
* Primary class constructor.
*
* @since 1.0.0
*/
public function init() {
// Define field type information.
$this->name = esc_html__( 'Checkboxes', 'wpforms-lite' );
$this->keywords = esc_html__( 'choice', 'wpforms-lite' );
$this->type = 'checkbox';
$this->icon = 'fa-check-square-o';
$this->order = 110;
$this->defaults = [
1 => [
'label' => esc_html__( 'First Choice', 'wpforms-lite' ),
'value' => '',
'image' => '',
'icon' => '',
'icon_style' => '',
'default' => '',
],
2 => [
'label' => esc_html__( 'Second Choice', 'wpforms-lite' ),
'value' => '',
'image' => '',
'icon' => '',
'icon_style' => '',
'default' => '',
],
3 => [
'label' => esc_html__( 'Third Choice', 'wpforms-lite' ),
'value' => '',
'image' => '',
'icon' => '',
'icon_style' => '',
'default' => '',
],
];
$this->hooks();
}
/**
* Hooks.
*
* @since 1.8.1
*/
private function hooks() {
// Customize HTML field values.
add_filter( 'wpforms_html_field_value', [ $this, 'field_html_value' ], 10, 4 );
// Define additional field properties.
add_filter( 'wpforms_field_properties_checkbox', [ $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.4.5
*
* @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 ) {
// Define data.
$form_id = absint( $form_data['id'] );
$field_id = absint( $field['id'] );
$choices = $field['choices'];
$dynamic = wpforms_get_field_dynamic_choices( $field, $form_id, $form_data );
if ( $dynamic !== false ) {
$choices = $dynamic;
$field['show_values'] = true;
}
// Remove primary input.
unset( $properties['inputs']['primary'] );
// Set input container (ul) properties.
$properties['input_container'] = [
'class' => [ ! empty( $field['random'] ) ? 'wpforms-randomize' : '' ],
'data' => [],
'attr' => [],
'id' => "wpforms-{$form_id}-field_{$field_id}",
];
$field['choice_limit'] = empty( $field['choice_limit'] ) ? 0 : (int) $field['choice_limit'];
if ( $field['choice_limit'] > 0 ) {
$properties['input_container']['data']['choice-limit'] = $field['choice_limit'];
}
// Set input properties.
foreach ( $choices as $key => $choice ) {
// Used for dynamic choices.
$depth = isset( $choice['depth'] ) ? absint( $choice['depth'] ) : 1;
$label = isset( $choice['label'] ) ? $choice['label'] : '';
// Choice labels should not be left blank, but if they are we
// provide a basic value.
$value = isset( $field['show_values'] ) ? $choice['value'] : $label;
if ( '' === $value ) {
if ( 1 === count( $choices ) ) {
$value = esc_html__( 'Checked', 'wpforms-lite' );
} else {
/* translators: %s - choice number. */
$value = sprintf( esc_html__( 'Choice %s', 'wpforms-lite' ), $key );
}
}
$properties['inputs'][ $key ] = [
'container' => [
'attr' => [],
'class' => [ "choice-{$key}", "depth-{$depth}" ],
'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' => $value,
],
'class' => [],
'data' => [],
'id' => "wpforms-{$form_id}-field_{$field_id}_{$key}",
'icon' => isset( $choice['icon'] ) ? $choice['icon'] : '',
'icon_style' => isset( $choice['icon_style'] ) ? $choice['icon_style'] : '',
'image' => isset( $choice['image'] ) ? $choice['image'] : '',
'required' => ! empty( $field['required'] ) ? 'required' : '',
'default' => isset( $choice['default'] ),
];
// Rule for validator only if needed.
if ( $field['choice_limit'] > 0 ) {
$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 is enabled.
if ( ! $dynamic && ! 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 ( ! $dynamic && ! empty( $field['choices_icons'] ) ) {
$properties = wpforms()->get( 'icon_choices' )->field_properties( $properties, $field );
}
// Custom properties for disclaimer format display.
if ( ! empty( $field['disclaimer_format'] ) ) {
$properties['description']['class'][] = 'wpforms-disclaimer-description';
$properties['description']['value'] = nl2br( $properties['description']['value'] );
}
// 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;
}
/**
* 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',
]
);
// Label.
$this->field_option( 'label', $field );
// Choices.
$this->field_option( 'choices', $field );
// Choices Images.
$this->field_option( 'choices_images', $field );
// Choices 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',
]
);
// Randomize order of choices.
$this->field_element(
'row',
$field,
[
'slug' => 'random',
'content' => $this->field_element(
'toggle',
$field,
[
'slug' => 'random',
'value' => isset( $field['random'] ) ? '1' : '0',
'desc' => esc_html__( 'Randomize Choices', 'wpforms-lite' ),
'tooltip' => esc_html__( 'Check this option to randomize the order of the choices.', 'wpforms-lite' ),
],
false
),
]
);
// Show Values toggle option. This option will only show if already used
// or if manually enabled by a filter.
if ( ! empty( $field['show_values'] ) || wpforms_show_fields_options_setting() ) {
$this->field_element(
'row',
$field,
[
'slug' => 'show_values',
'content' => $this->field_element(
'toggle',
$field,
[
'slug' => 'show_values',
'value' => isset( $field['show_values'] ) ? $field['show_values'] : '0',
'desc' => esc_html__( 'Show Values', 'wpforms-lite' ),
'tooltip' => esc_html__( 'Check this option to manually set form field values.', 'wpforms-lite' ),
],
false
),
]
);
}
// Display format.
$this->field_option( 'input_columns', $field );
// Choice Limit.
$field['choice_limit'] = empty( $field['choice_limit'] ) ? 0 : (int) $field['choice_limit'];
$this->field_element(
'row',
$field,
[
'slug' => 'choice_limit',
'content' =>
$this->field_element(
'label',
$field,
[
'slug' => 'choice_limit',
'value' => esc_html__( 'Choice Limit', 'wpforms-lite' ),
'tooltip' => esc_html__( 'Limit the number of checkboxes a user can select. Leave empty for unlimited.', 'wpforms-lite' ),
],
false
) . $this->field_element(
'text',
$field,
[
'slug' => 'choice_limit',
'value' => $field['choice_limit'] > 0 ? $field['choice_limit'] : '',
'type' => 'number',
],
false
),
]
);
// Dynamic choice auto-populating toggle.
$this->field_option( 'dynamic_choices', $field );
// Dynamic choice source.
$this->field_option( 'dynamic_choices_source', $field );
// Custom CSS classes.
$this->field_option( 'css', $field );
// Hide label.
$this->field_option( 'label_hide', $field );
// Enable Disclaimer formatting.
$this->field_element(
'row',
$field,
[
'slug' => 'disclaimer_format',
'content' => $this->field_element(
'toggle',
$field,
[
'slug' => 'disclaimer_format',
'value' => isset( $field['disclaimer_format'] ) ? '1' : '0',
'desc' => esc_html__( 'Enable Disclaimer / Terms of Service Display', 'wpforms-lite' ),
'tooltip' => esc_html__( 'Check this option to adjust the field styling to support Disclaimers and Terms of Service type agreements.', 'wpforms-lite' ),
],
false
),
]
);
// Options close markup.
$this->field_option(
'advanced-options',
$field,
[
'markup' => 'close',
]
);
}
/**
* Field preview inside the builder.
*
* @since 1.0.0
*
* @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,
[
'class' => ! empty( $field['disclaimer_format'] ) ? 'disclaimer nl2br' : false,
]
);
}
/**
* Field display on the form front-end and admin entry edit page.
*
* @since 1.0.0
*
* @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 ) {
$using_image_choices = empty( $field['dynamic_choices'] ) && ! empty( $field['choices_images'] );
$using_icon_choices = empty( $field['dynamic_choices'] ) && empty( $field['choices_images'] ) && ! empty( $field['choices_icons'] );
// Define data.
$container = $field['properties']['input_container'];
$choices = $field['properties']['inputs'];
// Do not display the field with empty choices on the frontend.
if ( ! $choices && ! is_admin() ) {
return;
}
// Display a warning message on Entry Edit page.
if ( ! $choices && is_admin() ) {
$this->display_empty_dynamic_choices_message( $field );
return;
}
$amp_state_id = '';
if ( wpforms_is_amp() && ( $using_image_choices || $using_icon_choices ) ) {
$amp_state_id = str_replace( '-', '_', sanitize_key( $container['id'] ) ) . '_state';
$state = [];
foreach ( $choices as $key => $choice ) {
$state[ $choice['id'] ] = ! empty( $choice['default'] );
}
printf(
'<amp-state id="%s"><script type="application/json">%s</script></amp-state>',
esc_attr( $amp_state_id ),
wp_json_encode( $state )
);
}
printf(
'<ul %s>',
wpforms_html_attributes( $container['id'], $container['class'], $container['data'], $container['attr'] )
);
foreach ( $choices as $key => $choice ) {
if ( wpforms_is_amp() && ( $using_image_choices || $using_icon_choices ) ) {
$choice['container']['attr']['[class]'] = sprintf(
'%s + ( %s[%s] ? " wpforms-selected" : "")',
wp_json_encode( implode( ' ', $choice['container']['class'] ) ),
$amp_state_id,
wp_json_encode( $choice['id'] )
);
}
// If the field is required, has the label hidden, and has
// disclaimer mode enabled, so the required status in choice
// label.
$required = '';
if ( ! empty( $field['disclaimer_format'] ) && ! empty( $choice['required'] ) && ! empty( $field['label_hide'] ) ) {
$required = wpforms_get_field_required_label();
}
printf(
'<li %s>',
wpforms_html_attributes( $choice['container']['id'], $choice['container']['class'], $choice['container']['data'], $choice['container']['attr'] )
);
// The required constraint in HTML5 form validation does not work with checkbox groups, so omit in AMP.
$required_attr = wpforms_is_amp() && count( $choices ) > 1 ? '' : $choice['required'];
if ( $using_image_choices ) {
// Make sure the image choices are keyboard-accessible.
$choice['label']['attr']['tabindex'] = 0;
if ( wpforms_is_amp() ) {
$choice['label']['attr']['on'] = sprintf(
'tap:AMP.setState({ %s: { %s: ! %s[%s] } })',
wp_json_encode( $amp_state_id ),
wp_json_encode( $choice['id'] ),
$amp_state_id,
wp_json_encode( $choice['id'] )
);
$choice['label']['attr']['role'] = 'button';
}
// Image choices.
printf(
'<label %s>',
wpforms_html_attributes( $choice['label']['id'], $choice['label']['class'], $choice['label']['data'], $choice['label']['attr'] )
);
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>';
}
$choice['attr']['tabindex'] = '-1';
if ( wpforms_is_amp() ) {
$choice['attr']['[checked]'] = sprintf(
'%s[%s]',
$amp_state_id,
wp_json_encode( $choice['id'] )
);
}
printf(
'<input type="checkbox" %s %s %s>',
wpforms_html_attributes( $choice['id'], $choice['class'], $choice['data'], $choice['attr'] ),
esc_attr( $required_attr ),
checked( '1', $choice['default'], false )
);
echo '<span class="wpforms-image-choices-label">' . wp_kses_post( $choice['label']['text'] ) . '</span>';
echo '</label>';
} elseif ( $using_icon_choices ) {
if ( wpforms_is_amp() ) {
$choice['label']['attr']['on'] = sprintf(
'tap:AMP.setState({ %s: { %s: ! %s[%s] } })',
wp_json_encode( $amp_state_id ),
wp_json_encode( $choice['id'] ),
$amp_state_id,
wp_json_encode( $choice['id'] )
);
$choice['label']['attr']['role'] = 'button';
}
// Icon Choices.
wpforms()->get( 'icon_choices' )->field_display( $field, $choice, 'checkbox' );
} else {
// Normal display.
printf(
'<input type="checkbox" %s %s %s>',
wpforms_html_attributes( $choice['id'], $choice['class'], $choice['data'], $choice['attr'] ),
esc_attr( $required_attr ),
checked( '1', $choice['default'], false )
);
printf(
'<label %s>%s%s</label>',
wpforms_html_attributes( $choice['label']['id'], $choice['label']['class'], $choice['label']['data'], $choice['label']['attr'] ),
wp_kses_post( $choice['label']['text'] ),
wp_kses(
$required,
[
'span' => [
'class' => true,
],
]
)
);
}
echo '</li>';
}
echo '</ul>';
}
/**
* Validate field on form submit.
*
* @since 1.5.2
*
* @param int $field_id Field ID.
* @param array $field_submit Submitted field value (selected option).
* @param array $form_data Form data.
*/
public function validate( $field_id, $field_submit, $form_data ) {
$field = $form_data['fields'][ $field_id ];
// Skip validation if field is dynamic and choices are empty.
if ( $this->is_dynamic_choices_empty( $field, $form_data ) ) {
return;
}
$field_submit = (array) $field_submit;
$choice_limit = empty( $form_data['fields'][ $field_id ]['choice_limit'] ) ? 0 : (int) $form_data['fields'][ $field_id ]['choice_limit'];
$count_choices = count( $field_submit );
if ( $choice_limit > 0 && $count_choices > $choice_limit ) {
// Generating the error.
$error = wpforms_setting( 'validation-check-limit', esc_html__( 'You have exceeded the number of allowed selections: {#}.', 'wpforms-lite' ) );
$error = str_replace( '{#}', $choice_limit, $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 ) ||
(
count( $field_submit ) === 1 &&
( ! isset( $field_submit[0] ) || (string) $field_submit[0] === '' )
)
)
) {
$error = wpforms_get_required_label();
}
if ( ! empty( $error ) ) {
wpforms()->process->errors[ $form_data['id'] ][ $field_id ] = $error;
}
}
/**
* Format and sanitize field.
*
* @since 1.0.2
*
* @param int $field_id Field ID.
* @param array $field_submit Submitted form data.
* @param array $form_data Form data and settings.
*/
public function format( $field_id, $field_submit, $form_data ) {
$field_submit = (array) $field_submit;
$field = $form_data['fields'][ $field_id ];
$dynamic = ! empty( $field['dynamic_choices'] ) ? $field['dynamic_choices'] : false;
$name = sanitize_text_field( $field['label'] );
$value_raw = wpforms_sanitize_array_combine( $field_submit );
$data = [
'name' => $name,
'value' => '',
'value_raw' => $value_raw,
'id' => absint( $field_id ),
'type' => $this->type,
];
if ( 'post_type' === $dynamic && ! empty( $field['dynamic_post_type'] ) ) {
// Dynamic population is enabled using post type.
$value_raw = implode( ',', array_map( 'absint', $field_submit ) );
$data['value_raw'] = $value_raw;
$data['dynamic'] = 'post_type';
$data['dynamic_items'] = $value_raw;
$data['dynamic_post_type'] = $field['dynamic_post_type'];
$posts = [];
foreach ( $field_submit as $id ) {
$post = get_post( $id );
if ( ! is_wp_error( $post ) && ! empty( $post ) && $data['dynamic_post_type'] === $post->post_type ) {
$posts[] = esc_html( wpforms_get_post_title( $post ) );
}
}
$data['value'] = ! empty( $posts ) ? wpforms_sanitize_array_combine( $posts ) : '';
}
elseif ( 'taxonomy' === $dynamic && ! empty( $field['dynamic_taxonomy'] ) ) {
// Dynamic population is enabled using taxonomy.
$value_raw = implode( ',', array_map( 'absint', $field_submit ) );
$data['value_raw'] = $value_raw;
$data['dynamic'] = 'taxonomy';
$data['dynamic_items'] = $value_raw;
$data['dynamic_taxonomy'] = $field['dynamic_taxonomy'];
$terms = [];
foreach ( $field_submit as $id ) {
$term = get_term( $id, $field['dynamic_taxonomy'] );
if ( ! is_wp_error( $term ) && ! empty( $term ) ) {
$terms[] = esc_html( wpforms_get_term_name( $term ) );
}
}
$data['value'] = ! empty( $terms ) ? wpforms_sanitize_array_combine( $terms ) : '';
} else {
// Normal processing, dynamic population is off.
$choice_keys = [];
// If show_values is true, that means values posted are the raw values
// and not the labels. So we need to set label values. Also store
// the choice keys.
if ( ! empty( $field['show_values'] ) && (int) $field['show_values'] === 1 ) {
foreach ( $field_submit as $item ) {
foreach ( $field['choices'] as $key => $choice ) {
if ( $item === $choice['value'] || ( empty( $choice['value'] ) && (int) str_replace( 'Choice ', '', $item ) === $key ) ) {
$value[] = $choice['label'];
$choice_keys[] = $key;
break;
}
}
}
$data['value'] = ! empty( $value ) ? wpforms_sanitize_array_combine( $value ) : '';
} else {
$data['value'] = $value_raw;
// Determine choices keys, this is needed for image choices.
foreach ( $field_submit as $item ) {
foreach ( $field['choices'] as $key => $choice ) {
/* translators: %s - choice number. */
if ( $item === $choice['label'] || $item === sprintf( esc_html__( 'Choice %s', 'wpforms-lite' ), $key ) ) {
$choice_keys[] = $key;
break;
}
}
}
}
// Images choices are enabled, lookup and store image URLs.
if ( ! empty( $choice_keys ) && ! empty( $field['choices_images'] ) ) {
$data['images'] = [];
foreach ( $choice_keys as $key ) {
$data['images'][] = ! empty( $field['choices'][ $key ]['image'] ) ? esc_url_raw( $field['choices'][ $key ]['image'] ) : '';
}
}
}
// Push field details to be saved.
wpforms()->process->fields[ $field_id ] = $data;
}
}
new WPForms_Field_Checkbox();

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,315 @@
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* GDPR Checkbox field.
*
* @since 1.4.6
*/
class WPForms_Field_GDPR_Checkbox extends WPForms_Field {
/**
* Primary class constructor.
*
* @since 1.4.6
*/
public function init() {
// Define field type information.
$this->name = esc_html__( 'GDPR Agreement', 'wpforms-lite' );
$this->type = 'gdpr-checkbox';
$this->icon = 'fa-check-square-o';
$this->order = 500;
$this->defaults = [
1 => [
'label' => esc_html__( 'I consent to having this website store my submitted information so they can respond to my inquiry.', 'wpforms-lite' ),
'value' => '',
'image' => '',
'default' => '',
],
];
// Set field to default to required.
add_filter( 'wpforms_field_new_required', [ $this, 'field_default_required' ], 10, 2 );
// Define additional field properties.
add_filter( 'wpforms_field_properties_gdpr-checkbox', [ $this, 'field_properties' ], 5, 3 );
}
/**
* Field should default to being required.
*
* @since 1.4.6
*
* @param bool $required Required status, true is required.
* @param array $field Field settings.
*
* @return bool
*/
public function field_default_required( $required, $field ) {
if ( $this->type === $field['type'] ) {
return true;
}
return $required;
}
/**
* Define additional field properties.
*
* @since 1.4.6
*
* @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 ) {
// Define data.
$form_id = absint( $form_data['id'] );
$field_id = absint( $field['id'] );
$choices = ! empty( $field['choices'] ) ? $field['choices'] : [];
// Remove primary input.
unset( $properties['inputs']['primary'] );
// 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' => $choice['label'],
],
'attr' => [
'name' => "wpforms[fields][{$field_id}][]",
'value' => $choice['label'],
],
'class' => [],
'data' => [],
'id' => "wpforms-{$form_id}-field_{$field_id}_{$key}",
'image' => '',
'required' => ! empty( $field['required'] ) ? 'required' : '',
'default' => '',
];
}
// Required class for pagebreak validation.
if ( ! empty( $field['required'] ) ) {
$properties['input_container']['class'][] = 'wpforms-field-required';
}
return $properties;
}
/**
* @inheritdoc
*/
public function is_dynamic_population_allowed( $properties, $field ) {
return false;
}
/**
* Field options panel inside the builder.
*
* @since 1.4.6
*
* @param array $field Field settings.
*/
public function field_options( $field ) {
// 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',
]
);
// Label.
$this->field_option( 'label', $field );
// Choices.
$this->field_option(
'choices',
$field,
[
'label' => esc_html__( 'Agreement', 'wpforms-lite' ),
]
);
// 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',
]
);
// 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.4.6
*
* @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.4.6
*
* @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 ) {
// 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'] )
);
foreach ( $choices as $key => $choice ) {
$required = '';
if ( ! empty( $choice['required'] ) && ! empty( $field['label_hide'] ) ) {
$required = wpforms_get_field_required_label();
}
printf(
'<li %s>',
wpforms_html_attributes( $choice['container']['id'], $choice['container']['class'], $choice['container']['data'], $choice['container']['attr'] )
);
// Normal display.
printf(
'<input type="checkbox" %s %s %s>',
wpforms_html_attributes( $choice['id'], $choice['class'], $choice['data'], $choice['attr'] ),
esc_attr( $choice['required'] ),
checked( '1', $choice['default'], false )
);
printf(
'<label %s>%s%s</label>',
wpforms_html_attributes( $choice['label']['id'], $choice['label']['class'], $choice['label']['data'], $choice['label']['attr'] ),
wp_kses_post( $choice['label']['text'] ),
$required
); // WPCS: XSS ok.
echo '</li>';
}
echo '</ul>';
}
/**
* Format and sanitize field.
*
* @since 1.4.6
*
* @param int $field_id Field ID.
* @param array $field_submit Submitted form data.
* @param array $form_data Form data and settings.
*/
public function format( $field_id, $field_submit, $form_data ) {
wpforms()->process->fields[ $field_id ] = [
'name' => ! empty( $form_data['fields'][ $field_id ]['label'] ) ? sanitize_text_field( $form_data['fields'][ $field_id ]['label'] ) : '',
'value' => $form_data['fields'][ $field_id ]['choices'][1]['label'],
'id' => absint( $field_id ),
'type' => $this->type,
];
}
}
new WPForms_Field_GDPR_Checkbox();

View File

@@ -0,0 +1,857 @@
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Internal information field class.
*
* @since 1.7.6
*/
class WPForms_Field_Internal_Information extends WPForms_Field {
/**
* The key used to save form checkboxes in post meta table.
*
* @since 1.7.6
*
* @var string
*/
const CHECKBOX_META_KEY = 'wpforms_iif_checkboxes';
/**
* Class initialization method.
*
* @since 1.7.6
*/
public function init() {
$this->name = $this->is_editable() ? esc_html__( 'Internal Information', 'wpforms-lite' ) : esc_html__( 'This field is not editable', 'wpforms-lite' );
$this->type = 'internal-information';
$this->icon = 'fa fa-sticky-note-o';
$this->order = 550;
$this->hooks();
}
/**
* Register hooks.
*
* @since 1.7.6
*
* @noinspection PhpUnnecessaryCurlyVarSyntaxInspection
*/
private function hooks() {
add_filter( 'wpforms_entries_table_fields_disallow', [ $this, 'hide_column_in_entries_table' ], 10, 2 );
add_filter( 'wpforms_field_preview_class', [ $this, 'add_css_class_for_field_wrapper' ], 10, 2 );
add_filter( 'wpforms_field_new_class', [ $this, 'add_css_class_for_field_wrapper' ], 10, 2 );
add_filter( "wpforms_pro_admin_entries_edit_is_field_displayable_{$this->type}", '__return_false' );
add_filter( 'wpforms_builder_strings', [ $this, 'builder_strings' ], 10, 2 );
add_filter( 'wpforms_frontend_form_data', [ $this, 'remove_internal_fields_on_front_end' ], 10, 1 );
add_filter( 'wpforms_process_before_form_data', [ $this, 'process_before_form_data' ], 10, 2 );
add_filter( 'wpforms_field_preview_display_duplicate_button', [ $this, 'display_duplicate_button' ], 10, 3 );
add_action( 'wpforms_builder_enqueues', [ $this, 'builder_enqueues' ] );
add_action( 'wp_ajax_wpforms_builder_save_internal_information_checkbox', [ $this, 'save_internal_information_checkbox' ] );
}
/**
* Whether current field can be populated dynamically.
*
* @since 1.7.6
*
* @param array $properties Field properties.
* @param array $field Current field specific data.
*
* @return bool
*/
public function is_dynamic_population_allowed( $properties, $field ) {
return false;
}
/**
* Whether current field can be populated using a fallback.
*
* @since 1.7.6
*
* @param array $properties Field properties.
* @param array $field Current field specific data.
*
* @return bool
*/
public function is_fallback_population_allowed( $properties, $field ) {
return false;
}
/**
* Define field options to display in left panel.
*
* @since 1.7.6
*
* @param array $field Field data and settings.
*/
public function field_options( $field ) {
$this->field_option(
'basic-options',
$field,
[
'markup' => 'open',
]
);
$this->heading_option( $field );
$this->field_option( 'description', $field );
$this->expanded_description_option( $field );
$this->cta_label_option( $field );
$this->cta_link_option( $field );
$this->field_option(
'basic-options',
$field,
[
'markup' => 'close',
]
);
}
/**
* Define field preview on the right side on builder.
*
* @since 1.7.6
*
* @param array $field Field data and settings.
*/
public function field_preview( $field ) {
echo '<div class="internal-information-wrap wpforms-clear">';
// phpcs:disable WordPress.Security.EscapeOutput.OutputNotEscaped
echo wpforms_render( 'fields/internal-information/icon-lightbulb' );
echo '<div class="internal-information-content">';
$this->render_preview( 'heading', $field );
$this->render_preview( 'description', $field );
$this->render_preview( 'expanded-description', $field );
if ( $this->is_button_displayable( $field ) ) {
echo '<div class="wpforms-field-internal-information-row wpforms-field-internal-information-row-cta-button">';
echo $this->render_custom_preview( 'cta-button', $field, [] );
echo '</div>';
}
echo '</div>';
// phpcs:enable WordPress.Security.EscapeOutput.OutputNotEscaped
echo '</div>';
}
/**
* Checks if button is displayable.
*
* @since 1.7.6
*
* @param array $field Field data.
*
* @return bool
*/
private function is_button_displayable( $field ) {
return ! empty( $field['expanded-description'] ) ||
( ! empty( $field['cta-label'] ) && ! empty( $field['cta-link'] ) ) ||
$this->is_editable();
}
/**
* Stub to make the field not visible in the front-end.
*
* @since 1.7.6
*
* @param array $field Field data and settings.
* @param array $field_atts Field attributes.
* @param array $form_data Form data.
*/
public function field_display( $field, $field_atts, $form_data ) {
}
/**
* Heading option.
*
* @since 1.7.6
*
* @param array $field Field data and settings.
*/
private function heading_option( $field ) {
$output = $this->field_element(
'label',
$field,
[
'slug' => 'heading',
'value' => esc_html__( 'Heading', 'wpforms-lite' ),
'tooltip' => esc_attr__( 'Enter text for the form field heading.', 'wpforms-lite' ),
],
false
);
$output .= $this->field_element(
'text',
$field,
[
'slug' => 'label',
'value' => ! empty( $field['label'] ) ? esc_attr( $field['label'] ) : '',
],
false
);
$this->field_element(
'row',
$field,
[
'slug' => 'heading',
'content' => $output,
]
);
}
/**
* Expanded description option.
*
* @since 1.7.6
*
* @param array $field Field data and settings.
*/
private function expanded_description_option( $field ) {
$output = $this->field_element(
'label',
$field,
[
'slug' => 'expanded-description',
'value' => esc_html__( 'Expanded Content', 'wpforms-lite' ),
'tooltip' => esc_attr__( 'Enter text for the form field expanded description.', 'wpforms-lite' ),
],
false
);
$output .= $this->field_element(
'textarea',
$field,
[
'slug' => 'expanded-description',
'value' => ! empty( $field['expanded-description'] ) ? esc_html( $field['expanded-description'] ) : '',
],
false
);
$output .= sprintf(
'<p class="note">%s</p>',
esc_html__( 'Adds an expandable content area below the description.', 'wpforms-lite' )
);
$this->field_element(
'row',
$field,
[
'slug' => 'expanded-description',
'content' => $output,
]
);
}
/**
* CTA label option.
*
* @since 1.7.6
*
* @param array $field Field data and settings.
*/
private function cta_label_option( $field ) {
$output = $this->field_element(
'label',
$field,
[
'slug' => 'cta-label',
'value' => esc_html__( 'CTA Label', 'wpforms-lite' ),
'tooltip' => esc_attr__( 'Enter label for the form field call to action button. The label will be ignored if the field has extended description content: in that case button will be used to expand the description content.', 'wpforms-lite' ),
],
false
);
$output .= $this->field_element(
'text',
$field,
[
'slug' => 'cta-label',
'value' => ! empty( $field['cta-label'] ) ? esc_attr( $field['cta-label'] ) : esc_attr__( 'Learn More', 'wpforms-lite' ),
],
false
);
$this->field_element(
'row',
$field,
[
'slug' => 'cta-label',
'content' => $output,
]
);
}
/**
* CTA link option.
*
* @since 1.7.6
*
* @param array $field Field data and settings.
*/
private function cta_link_option( $field ) {
$output = $this->field_element(
'label',
$field,
[
'slug' => 'cta-link',
'value' => esc_html__( 'CTA Link', 'wpforms-lite' ),
'tooltip' => esc_attr__( 'Enter the URL for the form field call to action button. URL will be ignored if the field has extended description content: in that case button will be used to expand the description content.', 'wpforms-lite' ),
],
false
);
$output .= $this->field_element(
'text',
$field,
[
'slug' => 'cta-link',
'value' => ! empty( $field['cta-link'] ) ? esc_url( $field['cta-link'] ) : '',
],
false
);
$output .= sprintf(
'<p class="note">%s</p>',
esc_html__( 'CTA is hidden if Expanded Content is used.', 'wpforms-lite' )
);
$this->field_element(
'row',
$field,
[
'slug' => 'cta-link',
'content' => $output,
]
);
}
/**
* Add CSS class to hide field settings when field is not editable.
*
* @since 1.7.6
*
* @param string $option Field option to render.
* @param array $field Field data and settings.
* @param array $args Field preview arguments.
* @param bool $echo Print or return the value. Print by default.
*
* @return string
*/
public function field_element( $option, $field, $args = [], $echo = true ) {
if ( ! isset( $args['class'] ) ) {
$args['class'] = '';
}
if ( ! $this->is_editable() ) {
$args['class'] .= ' wpforms-hidden ';
}
return parent::field_element( $option, $field, $args, $echo );
}
/**
* Render custom option preview on the right side of builder.
*
* @since 1.7.6
*
* @param string $option Field option to render.
* @param array $field Field data and settings.
* @param array $args Field arguments.
*
* @return string
*/
private function render_custom_preview( $option, $field, $args = [] ) { // phpcs:ignore Generic.Metrics.CyclomaticComplexity.MaxExceeded, Generic.Metrics.CyclomaticComplexity.TooHigh
$class = ! empty( $args['class'] ) ? wpforms_sanitize_classes( $args['class'] ) : '';
$allowed_tags = $this->get_allowed_tags();
switch ( $option ) {
case 'heading':
$label = isset( $field['label'] ) && ! wpforms_is_empty_string( $field['label'] ) ? esc_html( $field['label'] ) : '';
if ( ! $label ) {
$class .= ' hidden ';
}
return sprintf(
'<label class="label-title heading %s"><span class="text">%s</span><span class="required">*</span></label>',
esc_attr( $class ),
esc_html( $label )
);
case 'description': // phpcs:ignore WPForms.Formatting.Switch.AddEmptyLineBefore
$description = isset( $field['description'] ) && ! empty( $field['description'] ) ? wp_kses( $field['description'], $allowed_tags ) : '';
$description = wpautop( $this->replace_checkboxes( $description, $field ) );
$description = $this->add_link_attributes( $description );
return sprintf( '<div class="description %s">%s</div>', $class, $description );
case 'expanded-description': // phpcs:ignore WPForms.Formatting.Switch.AddEmptyLineBefore
$description = isset( $field['expanded-description'] ) && ! wpforms_is_empty_string( $field['expanded-description'] ) ? wp_kses( $field['expanded-description'], $allowed_tags ) : '';
$description = wpautop( $this->replace_checkboxes( $description, $field ) );
$description = $this->add_link_attributes( $description );
return sprintf( '<div class="expanded-description %s">%s</div>', esc_attr( $class ), wp_kses( $description, $allowed_tags ) );
case 'cta-button': // phpcs:ignore WPForms.Formatting.Switch.AddEmptyLineBefore
$label = isset( $field['cta-label'] ) && ! empty( $field['cta-label'] ) && empty( $field['expanded-description'] ) ? esc_attr( $field['cta-label'] ) : esc_attr__( 'Learn More', 'wpforms-lite' );
if ( isset( $field['expanded-description'] ) && ! empty( $field['expanded-description'] ) ) {
return sprintf(
'<div class="cta-button cta-expand-description not-expanded %s"><a href="#" target="_blank" rel="noopener noreferrer"><span class="button-label">%s</span> %s %s</a></div>',
esc_attr( $class ),
esc_html( $label ),
wpforms_render( 'fields/internal-information/icon-not-expanded' ),
wpforms_render( 'fields/internal-information/icon-expanded' )
);
}
if ( isset( $field['cta-link'] ) && ! empty( $field['cta-link'] ) ) {
return sprintf( '<div class="cta-button cta-link-external %s"><a href="%s" target="_blank" rel="noopener noreferrer">%s</a></div>', esc_attr( $class ), esc_url( $this->add_url_utm( $field ) ), esc_html( $label ) );
}
return sprintf( '<div class="cta-button cta-link-external %s"><a href="" target="_blank" rel="noopener noreferrer" class="hidden"><span class="button-label"></span></a></div>', esc_attr( $class ) );
}
return '';
}
/**
* Display field button in left panel only if the field is editable.
*
* @since 1.7.6
*
* @param array $fields All fields to display in the left panel.
*
* @return array
*/
public function field_button( $fields ) {
if ( $this->is_editable() ) {
return parent::field_button( $fields );
}
return $fields;
}
/**
* When the form is going to be displayed on front-end, remove internal information fields.
*
* @since 1.7.6
*
* @param array $form_data Form data.
*
* @return array
*/
public function remove_internal_fields_on_front_end( $form_data ) {
if ( empty( $form_data['fields'] ) ) {
return $form_data;
}
foreach ( $form_data['fields'] as $id => $field ) {
if ( $field['type'] === $this->type ) {
unset( $form_data['fields'][ $id ] );
}
}
return $form_data;
}
/**
* Remove field from form data before processing the form submit.
*
* @since 1.7.6
*
* @param array $form_data Form data.
* @param array $entry Form submission raw data ($_POST).
*
* @return array
*/
public function process_before_form_data( $form_data, $entry ) {
return $this->remove_internal_fields_on_front_end( $form_data );
}
/**
* Do not display the duplicate button.
*
* @since 1.7.6
*
* @param bool $is_visible If true, the duplicate button will be displayed.
* @param array $field Field data and settings.
* @param array $form_data Form data and settings.
*
* @return bool
*/
public function display_duplicate_button( $is_visible, $field, $form_data ) {
if ( $this->is_internal_information_field( $field ) && ! $this->is_editable() ) {
return false;
}
return $is_visible;
}
/**
* Hide column from the entries list table.
*
* @since 1.7.6
*
* @param array $disallowed Table columns.
*
* @return array
*/
public function hide_column_in_entries_table( $disallowed ) {
$disallowed[] = $this->type;
return $disallowed;
}
/**
* Add CSS class for the field parent div informing about mode (editable or not).
*
* @since 1.7.6
*
* @param string $css CSS classes.
* @param array $field Field data and settings.
*
* @return string
*/
public function add_css_class_for_field_wrapper( $css, $field ) {
if ( ! $this->is_internal_information_field( $field ) ) {
return $css;
}
if ( $this->is_editable() ) {
$css .= ' internal-information-editable ';
return $css;
}
$css .= ' ui-sortable-disabled internal-information-not-editable internal-information-not-draggable ';
return str_replace( 'ui-sortable-handle', '', $css );
}
/**
* Save checkbox state to the post meta table.
*
* @since 1.7.6
*/
public function save_internal_information_checkbox() {
// Run several checks: required items, security, permissions.
if (
! isset( $_POST['formId'], $_POST['name'], $_POST['checked'] ) ||
! check_ajax_referer( 'wpforms-builder', 'nonce', false ) ||
! wpforms_current_user_can( 'edit_forms' )
) {
wp_send_json_error();
}
$form_id = (int) $_POST['formId'];
$checked = (int) $_POST['checked'];
$name = sanitize_text_field( wp_unslash( $_POST['name'] ) );
$post_meta = get_post_meta( $form_id, self::CHECKBOX_META_KEY, true );
$post_meta = ! empty( $post_meta ) ? (array) $post_meta : [];
if ( $checked ) {
$post_meta[ $name ] = $checked;
} else {
unset( $post_meta[ $name ] );
}
update_post_meta( $form_id, self::CHECKBOX_META_KEY, $post_meta );
wp_send_json_success();
}
/**
* Localized strings for wpforms-internal-information-field JS script.
*
* @since 1.7.6
*
* @param array $strings Localized strings.
* @param array $form The form element.
*
* @return array
* @noinspection PhpUnusedParameterInspection
*/
public function builder_strings( $strings, $form ) {
$strings['iif_redirect_url_field_error'] = esc_html__( 'You should enter a valid absolute address to the CTA Link field or leave it empty.', 'wpforms-lite' );
$strings['iif_dismiss'] = esc_html__( 'Dismiss', 'wpforms-lite' );
$strings['iif_more'] = esc_html__( 'Learn More', 'wpforms-lite' );
return $strings;
}
/**
* Enqueue wpforms-internal-information-field script.
*
* @since 1.7.6
*
* @param string $view Current view.
*
* @noinspection PhpUnusedParameterInspection, PhpUnnecessaryCurlyVarSyntaxInspection
*/
public function builder_enqueues( $view ) {
$min = wpforms_get_min_suffix();
wp_enqueue_script(
'wpforms-md5-hash',
WPFORMS_PLUGIN_URL . 'assets/lib/md5.min.js',
[ 'wpforms-builder' ],
'2.19.0'
);
wp_enqueue_script(
'wpforms-internal-information-field',
WPFORMS_PLUGIN_URL . "assets/js/components/admin/fields/internal-information-field{$min}.js",
[ 'wpforms-builder', 'wpforms-md5-hash', 'wpforms-builder-drag-fields' ],
WPFORMS_VERSION
);
}
/**
* Checks if user is allowed to edit the field's content.
*
* @since 1.7.6
*
* @return bool
*/
private function is_editable() {
/**
* Allow changing a mode.
*
* @since 1.7.6
*
* @param bool $is_editable True if editable mode is allowed. Default: false.
*/
return (bool) apply_filters( 'wpforms_field_internal_information_is_editable', false );
}
/**
* Check if the field has type internal-information.
*
* @since 1.7.6
*
* @param array $field Field data.
*
* @return bool
*/
private function is_internal_information_field( $field ) {
return isset( $field['type'] ) && $field['type'] === $this->type;
}
/**
* Render result of field_preview_option into custom div.
*
* If field has no value, do not echo anything.
*
* @since 1.7.6
*
* @param string $label Field label.
* @param array $field Field settings and data.
* @param array $args Field arguments.
*/
private function render_preview( $label, $field, $args = [] ) {
$key = $label === 'heading' ? 'label' : $label;
if ( empty( $field[ $key ] ) && ! $this->is_editable() ) {
return;
}
$allowed_tags = $this->get_allowed_tags();
printf(
'<div class="wpforms-field-internal-information-row wpforms-field-internal-information-row-%s">%s</div>',
esc_attr( $label ),
wp_kses( $this->render_custom_preview( $label, $field, $args ), $allowed_tags )
);
}
/**
* Replace `[] some text` with checkboxes.
*
* Additionally, generates input name by hashing the line of text where the checkbox is.
*
* @since 1.7.6
*
* @param string $description Expanded description.
* @param array $field Field data and settings.
*
* @return string
*/
private function replace_checkboxes( $description, array $field ) { // phpcs:ignore Generic.Metrics.CyclomaticComplexity.TooHigh
if ( ! $this->form_id ) {
return $description;
}
$lines = explode( PHP_EOL, $description );
$replaced = [];
$post_meta = get_post_meta( $this->form_id, self::CHECKBOX_META_KEY, true );
$post_meta = ! empty( $post_meta ) ? (array) $post_meta : [];
$field_id = isset( $field['id'] ) ? $field['id'] : 0;
$needle = '[] ';
foreach ( $lines as $line_number => $line ) {
$line = trim( $line );
if ( strpos( $line, $needle ) !== 0 ) {
$replaced[] = $line . PHP_EOL;
continue;
}
$field_name = sprintf( 'iif-%d-%s-%d', $field_id, md5( $line ), $line_number );
$checked = (int) isset( $post_meta[ $field_name ] );
$attributes = [
'name' => esc_attr( $field_name ),
'value' => 1,
];
if ( $this->is_editable() ) {
$attributes['disabled'] = 'disabled';
$attributes['title'] = esc_html__( 'This field is disabled in the editor mode.', 'wpforms-lite' );
}
$html = sprintf(
'<div class="wpforms-field-internal-information-checkbox-input"><input type="checkbox" %s %s /></div><div class="wpforms-field-internal-information-checkbox-label">',
wpforms_html_attributes(
'',
[ 'wpforms-field-internal-information-checkbox' ],
[],
$attributes
),
! $this->is_editable() ? checked( $checked, 1, false ) : ''
);
$line = substr_replace( $line, $html, 0, strlen( $needle ) );
$replaced[] = '<div class="wpforms-field-internal-information-checkbox-wrap">' . $line . '</div></div>';
}
return implode( '', $replaced );
}
/**
* Return allowed tags specific to internal information field content.
*
* @since 1.7.6
*
* @return array
*/
private function get_allowed_tags() {
$allowed_tags = wpforms_builder_preview_get_allowed_tags();
$allowed_tags['input'] = [
'type' => [],
'name' => [],
'value' => [],
'class' => [],
'checked' => [],
'disabled' => [],
'title' => [],
];
return $allowed_tags;
}
/**
* Adds link parameters to all links in the provided content.
*
* @since 1.8.3
*
* @param string $content The content to modify.
*
* @return string The modified content with UTM parameters added to links.
*/
private function add_link_attributes( $content ) {
if ( empty( $content ) || ! class_exists( 'DOMDocument' ) ) {
return $content;
}
$dom = new DOMDocument();
$form_data = wpforms()->get( 'form' )->get( $this->form_id, [ 'content_only' => true ] );
$template_data = ! empty( $form_data['meta'] ) ? wpforms()->get( 'builder_templates' )->get_template( $form_data['meta']['template'] ) : [];
$template_name = ! empty( $template_data ) ? $template_data['name'] : '';
$dom->loadHTML( htmlspecialchars_decode( htmlentities( $content ) ) );
$links = $dom->getElementsByTagName( 'a' );
foreach ( $links as $link ) {
$href = $link->getAttribute( 'href' );
$text = $link->textContent; // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
$modified_href = wpforms_utm_link( $href, 'Form Template Information Note', $template_name, $text );
$link->setAttribute( 'href', $modified_href );
$link->setAttribute( 'target', '_blank' );
$link->setAttribute( 'rel', 'noopener noreferrer' );
}
// Remove the wrapper elements.
$body = $dom->getElementsByTagName( 'body' )->item( 0 );
$inner_html = '';
foreach ( $body->childNodes as $node ) { // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
$inner_html .= $dom->saveHTML( $node );
}
return $inner_html;
}
/**
* Add UTM parameters to the CTA button link.
*
* @since 1.7.6
*
* @param array $field Field data.
*
* @return string
*/
private function add_url_utm( $field ) {
if ( strpos( $field['cta-link'], 'https://wpforms.com' ) === 0 ) {
return wpforms_utm_link( $field['cta-link'], 'Template Documentation' );
}
return $field['cta-link'];
}
}
new WPForms_Field_Internal_Information();

View File

@@ -0,0 +1,622 @@
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Name text field.
*
* @since 1.0.0
*/
class WPForms_Field_Name extends WPForms_Field {
/**
* Primary class constructor.
*
* @since 1.0.0
*/
public function init() {
// Define field type information.
$this->name = esc_html__( 'Name', 'wpforms-lite' );
$this->keywords = esc_html__( 'user, first, last', 'wpforms-lite' );
$this->type = 'name';
$this->icon = 'fa-user';
$this->order = 150;
$this->hooks();
}
/**
* Hooks.
*
* @since 1.8.1
*/
private function hooks() {
// Define additional field properties.
add_filter( 'wpforms_field_properties_name', [ $this, 'field_properties' ], 5, 3 );
// Set field to default to required.
add_filter( 'wpforms_field_new_required', [ $this, 'default_required' ], 10, 2 );
// This field requires fieldset+legend instead of the field label.
add_filter( "wpforms_frontend_modern_is_field_requires_fieldset_{$this->type}", [ $this, 'is_field_requires_fieldset' ], PHP_INT_MAX, 2 );
}
/**
* Define additional field properties.
*
* @since 1.3.7
*
* @param array $properties Field properties.
* @param array $field Field data and settings.
* @param array $form_data Form data and settings.
*
* @return array
*/
public function field_properties( $properties, $field, $form_data ) {
$format = ! empty( $field['format'] ) ? esc_attr( $field['format'] ) : 'first-last';
// Simple format.
if ( 'simple' === $format ) {
$properties['inputs']['primary']['attr']['placeholder'] = ! empty( $field['simple_placeholder'] ) ? $field['simple_placeholder'] : '';
$properties['inputs']['primary']['attr']['value'] = ! empty( $field['simple_default'] ) ? wpforms_process_smart_tags( $field['simple_default'], $form_data ) : '';
return $properties;
}
// Expanded formats.
// Remove primary for expanded formats since we have first, middle, last.
unset( $properties['inputs']['primary'] );
$form_id = absint( $form_data['id'] );
$field_id = absint( $field['id'] );
$props = [
'inputs' => [
'first' => [
'attr' => [
'name' => "wpforms[fields][{$field_id}][first]",
'value' => ! empty( $field['first_default'] ) ? wpforms_process_smart_tags( $field['first_default'], $form_data ) : '',
'placeholder' => ! empty( $field['first_placeholder'] ) ? $field['first_placeholder'] : '',
],
'block' => [
'wpforms-field-row-block',
'wpforms-first',
],
'class' => [
'wpforms-field-name-first',
],
'data' => [],
'id' => "wpforms-{$form_id}-field_{$field_id}",
'required' => ! empty( $field['required'] ) ? 'required' : '',
'sublabel' => [
'hidden' => ! empty( $field['sublabel_hide'] ),
'value' => esc_html__( 'First', 'wpforms-lite' ),
],
],
'middle' => [
'attr' => [
'name' => "wpforms[fields][{$field_id}][middle]",
'value' => ! empty( $field['middle_default'] ) ? wpforms_process_smart_tags( $field['middle_default'], $form_data ) : '',
'placeholder' => ! empty( $field['middle_placeholder'] ) ? $field['middle_placeholder'] : '',
],
'block' => [
'wpforms-field-row-block',
'wpforms-one-fifth',
],
'class' => [
'wpforms-field-name-middle',
],
'data' => [],
'id' => "wpforms-{$form_id}-field_{$field_id}-middle",
'required' => '',
'sublabel' => [
'hidden' => ! empty( $field['sublabel_hide'] ),
'value' => esc_html__( 'Middle', 'wpforms-lite' ),
],
],
'last' => [
'attr' => [
'name' => "wpforms[fields][{$field_id}][last]",
'value' => ! empty( $field['last_default'] ) ? wpforms_process_smart_tags( $field['last_default'], $form_data ) : '',
'placeholder' => ! empty( $field['last_placeholder'] ) ? $field['last_placeholder'] : '',
],
'block' => [
'wpforms-field-row-block',
],
'class' => [
'wpforms-field-name-last',
],
'data' => [],
'id' => "wpforms-{$form_id}-field_{$field_id}-last",
'required' => ! empty( $field['required'] ) ? 'required' : '',
'sublabel' => [
'hidden' => ! empty( $field['sublabel_hide'] ),
'value' => esc_html__( 'Last', 'wpforms-lite' ),
],
],
],
];
$properties = array_merge_recursive( $properties, $props );
$has_common_error = ! empty( $properties['error']['value'] ) && is_string( $properties['error']['value'] );
// Input First: add error class if needed.
if ( ! empty( $properties['error']['value']['first'] ) || $has_common_error ) {
$properties['inputs']['first']['class'][] = 'wpforms-error';
}
// Input First: add required class if needed.
if ( ! empty( $field['required'] ) ) {
$properties['inputs']['first']['class'][] = 'wpforms-field-required';
}
// Input First: add column class.
$properties['inputs']['first']['block'][] = 'first-last' === $format ? 'wpforms-one-half' : 'wpforms-two-fifths';
// Input Middle: add error class if needed.
if ( $has_common_error ) {
$properties['inputs']['middle']['class'][] = 'wpforms-error';
}
// Input Last: add error class if needed.
if ( ! empty( $properties['error']['value']['last'] ) || $has_common_error ) {
$properties['inputs']['last']['class'][] = 'wpforms-error';
}
// Input Last: add required class if needed.
if ( ! empty( $field['required'] ) ) {
$properties['inputs']['last']['class'][] = 'wpforms-field-required';
}
// Input Last: add column class.
$properties['inputs']['last']['block'][] = 'first-last' === $format ? 'wpforms-one-half' : 'wpforms-two-fifths';
return $properties;
}
/**
* Name fields should default to being required.
*
* @since 1.0.8
*
* @param bool $required
* @param array $field
*
* @return bool
*/
public function default_required( $required, $field ) {
if ( 'name' === $field['type'] ) {
return true;
}
return $required;
}
/**
* Field options panel inside the builder.
*
* @since 1.0.0
*
* @param array $field
*/
public function field_options( $field ) {
// Define data.
$format = ! empty( $field['format'] ) ? esc_attr( $field['format'] ) : 'first-last';
/*
* Basic field options.
*/
// Options open markup.
$args = [
'markup' => 'open',
];
$this->field_option( 'basic-options', $field, $args );
// 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 to use for the name form field', 'wpforms-lite' ),
],
false
);
$fld = $this->field_element(
'select',
$field,
[
'slug' => 'format',
'value' => $format,
'options' => [
'simple' => esc_html__( 'Simple', 'wpforms-lite' ),
'first-last' => esc_html__( 'First Last', 'wpforms-lite' ),
'first-middle-last' => esc_html__( 'First Middle Last', '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 );
echo '<div class="format-selected-' . $format . ' format-selected">';
// Simple.
$simple_placeholder = ! empty( $field['simple_placeholder'] ) ? esc_attr( $field['simple_placeholder'] ) : '';
$simple_default = ! empty( $field['simple_default'] ) ? esc_attr( $field['simple_default'] ) : '';
printf( '<div class="wpforms-clear wpforms-field-option-row wpforms-field-option-row-simple" id="wpforms-field-option-row-%d-simple" data-subfield="simple" data-field-id="%d">', $field['id'], $field['id'] );
$this->field_element( 'label', $field, [ 'slug' => 'simple_placeholder', 'value' => esc_html__( 'Name', 'wpforms-lite' ), 'tooltip' => esc_html__( 'Name field advanced options.', 'wpforms-lite' ) ] );
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-%d-simple_placeholder" name="fields[%d][simple_placeholder]" value="%s">', (int) $field['id'], (int) $field['id'], esc_attr( $simple_placeholder ) );
printf( '<label for="wpforms-field-option-%d-simple_placeholder" class="sub-label">%s</label>', (int) $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-%d-simple_default" name="fields[%d][simple_default]" value="%s">', (int) $field['id'], (int) $field['id'], esc_attr( $simple_default ) );
printf( '<label for="wpforms-field-option-%d-simple_default" class="sub-label">%s</label>', (int) $field['id'], esc_html__( 'Default Value', 'wpforms-lite' ) );
echo '</div>';
echo '</div>';
echo '</div>';
// First.
$first_placeholder = ! empty( $field['first_placeholder'] ) ? esc_attr( $field['first_placeholder'] ) : '';
$first_default = ! empty( $field['first_default'] ) ? esc_attr( $field['first_default'] ) : '';
printf( '<div class="wpforms-clear wpforms-field-option-row wpforms-field-option-row-first" id="wpforms-field-option-row-%d-first" data-subfield="first-name" data-field-id="%d">', $field['id'], $field['id'] );
$this->field_element( 'label', $field, [ 'slug' => 'first_placeholder', 'value' => esc_html__( 'First Name', 'wpforms-lite' ), 'tooltip' => esc_html__( 'First name field advanced options.', 'wpforms-lite' ) ] );
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-first_placeholder" name="fields[%1$d][first_placeholder]" value="%2$s">', (int) $field['id'], esc_attr( $first_placeholder ) );
printf( '<label for="wpforms-field-option-%d-first_placeholder" class="sub-label">%s</label>', (int) $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-first_default" name="fields[%1$d][first_default]" value="%2$s">', (int) $field['id'], esc_attr( $first_default ) );
printf( '<label for="wpforms-field-option-%d-first_default" class="sub-label">%s</label>', (int) $field['id'], esc_html__( 'Default Value', 'wpforms-lite' ) );
echo '</div>';
echo '</div>';
echo '</div>';
// Middle.
$middle_placeholder = ! empty( $field['middle_placeholder'] ) ? esc_attr( $field['middle_placeholder'] ) : '';
$middle_default = ! empty( $field['middle_default'] ) ? esc_attr( $field['middle_default'] ) : '';
printf( '<div class="wpforms-clear wpforms-field-option-row wpforms-field-option-row-middle" id="wpforms-field-option-row-%d-middle" data-subfield="middle-name" data-field-id="%d">', $field['id'], $field['id'] );
$this->field_element( 'label', $field, [ 'slug' => 'middle_placeholder', 'value' => esc_html__( 'Middle Name', 'wpforms-lite' ), 'tooltip' => esc_html__( 'Middle name field advanced options.', 'wpforms-lite' ) ] );
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-middle_placeholder" name="fields[%1$d][middle_placeholder]" value="%2$s">', (int) $field['id'], esc_attr( $middle_placeholder ) );
printf( '<label for="wpforms-field-option-%d-middle_placeholder" class="sub-label">%s</label>', (int) $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-middle_default" name="fields[%1$d][middle_default]" value="%2$s">', (int) $field['id'], esc_attr( $middle_default ) );
printf( '<label for="wpforms-field-option-%d-middle_default" class="sub-label">%s</label>', (int) $field['id'], esc_html__( 'Default Value', 'wpforms-lite' ) );
echo '</div>';
echo '</div>';
echo '</div>';
// Last.
$last_placeholder = ! empty( $field['last_placeholder'] ) ? esc_attr( $field['last_placeholder'] ) : '';
$last_default = ! empty( $field['last_default'] ) ? esc_attr( $field['last_default'] ) : '';
printf( '<div class="wpforms-clear wpforms-field-option-row wpforms-field-option-row-last" id="wpforms-field-option-row-%d-last" data-subfield="last-name" data-field-id="%d">', $field['id'], $field['id'] );
$this->field_element( 'label', $field, [ 'slug' => 'last_placeholder', 'value' => esc_html__( 'Last Name', 'wpforms-lite' ), 'tooltip' => esc_html__( 'Last name field advanced options.', 'wpforms-lite' ) ] );
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-last_placeholder" name="fields[%1$d][last_placeholder]" value="%2$s">', (int) $field['id'], esc_attr( $last_placeholder ) );
printf( '<label for="wpforms-field-option-%d-last_placeholder" class="sub-label">%s</label>', (int) $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-last_default" name="fields[%1$d][last_default]" value="%2$s">', (int) $field['id'], esc_attr( $last_default ) );
printf( '<label for="wpforms-field-option-%d-last_default" class="sub-label">%s</label>', (int) $field['id'], esc_html__( 'Default Value', 'wpforms-lite' ) );
echo '</div>';
echo '</div>';
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'] ) && ! in_array( $field['format'], [ 'first-last', 'first-middle-last' ], true ) ? 'wpforms-hidden' : '';
$this->field_option( 'sublabel_hide', $field, [ 'class' => $sublabel_class ] );
// 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 information.
*/
public function field_preview( $field ) {
// Define data.
$simple_placeholder = ! empty( $field['simple_placeholder'] ) ? $field['simple_placeholder'] : '';
$first_placeholder = ! empty( $field['first_placeholder'] ) ? $field['first_placeholder'] : '';
$middle_placeholder = ! empty( $field['middle_placeholder'] ) ? $field['middle_placeholder'] : '';
$last_placeholder = ! empty( $field['last_placeholder'] ) ? $field['last_placeholder'] : '';
$simple_default = ! empty( $field['simple_default'] ) ? $field['simple_default'] : '';
$first_default = ! empty( $field['first_default'] ) ? $field['first_default'] : '';
$middle_default = ! empty( $field['middle_default'] ) ? $field['middle_default'] : '';
$last_default = ! empty( $field['last_default'] ) ? $field['last_default'] : '';
$format = ! empty( $field['format'] ) ? $field['format'] : 'first-last';
// Label.
$this->field_preview_option( 'label', $field );
?>
<div class="format-selected-<?php echo sanitize_html_class( $format ); ?> format-selected wpforms-clear">
<div class="wpforms-simple">
<input type="text" placeholder="<?php echo esc_attr( $simple_placeholder ); ?>" value="<?php echo esc_attr( $simple_default ); ?>" class="primary-input" readonly>
</div>
<div class="wpforms-first-name">
<input type="text" placeholder="<?php echo esc_attr( $first_placeholder ); ?>" value="<?php echo esc_attr( $first_default ); ?>" class="primary-input" readonly>
<label class="wpforms-sub-label"><?php esc_html_e( 'First', 'wpforms-lite' ); ?></label>
</div>
<div class="wpforms-middle-name">
<input type="text" placeholder="<?php echo esc_attr( $middle_placeholder ); ?>" value="<?php echo esc_attr( $middle_default ); ?>" class="primary-input" readonly>
<label class="wpforms-sub-label"><?php esc_html_e( 'Middle', 'wpforms-lite' ); ?></label>
</div>
<div class="wpforms-last-name">
<input type="text" placeholder="<?php echo esc_attr( $last_placeholder ); ?>" value="<?php echo esc_attr( $last_default ); ?>" class="primary-input" readonly>
<label class="wpforms-sub-label"><?php esc_html_e( 'Last', 'wpforms-lite' ); ?></label>
</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 information.
* @param array $deprecated Deprecated parameter, not used anymore.
* @param array $form_data Form data and settings.
*/
public function field_display( $field, $deprecated, $form_data ) {
// Define data.
$format = ! empty( $field['format'] ) ? esc_attr( $field['format'] ) : 'first-last';
$primary = ! empty( $field['properties']['inputs']['primary'] ) ? $field['properties']['inputs']['primary'] : '';
$first = ! empty( $field['properties']['inputs']['first'] ) ? $field['properties']['inputs']['first'] : '';
$middle = ! empty( $field['properties']['inputs']['middle'] ) ? $field['properties']['inputs']['middle'] : '';
$last = ! empty( $field['properties']['inputs']['last'] ) ? $field['properties']['inputs']['last'] : '';
// Simple format.
if ( 'simple' === $format ) {
// Primary field (Simple).
printf(
'<input type="text" %s %s>',
wpforms_html_attributes( $primary['id'], $primary['class'], $primary['data'], $primary['attr'] ),
$primary['required']
);
// Expanded formats.
} else {
// Row wrapper.
echo '<div class="wpforms-field-row wpforms-field-' . sanitize_html_class( $field['size'] ) . '">';
// First name.
echo '<div ' . wpforms_html_attributes( false, $first['block'] ) . '>';
$this->field_display_sublabel( 'first', 'before', $field );
printf(
'<input type="text" %s %s>',
wpforms_html_attributes( $first['id'], $first['class'], $first['data'], $first['attr'] ),
$first['required']
);
$this->field_display_sublabel( 'first', 'after', $field );
$this->field_display_error( 'first', $field );
echo '</div>';
// Middle name.
if ( 'first-middle-last' === $format ) {
echo '<div ' . wpforms_html_attributes( false, $middle['block'] ) . '>';
$this->field_display_sublabel( 'middle', 'before', $field );
printf(
'<input type="text" %s %s>',
wpforms_html_attributes( $middle['id'], $middle['class'], $middle['data'], $middle['attr'] ),
$middle['required']
);
$this->field_display_sublabel( 'middle', 'after', $field );
$this->field_display_error( 'middle', $field );
echo '</div>';
}
// Last name.
echo '<div ' . wpforms_html_attributes( false, $last['block'] ) . '>';
$this->field_display_sublabel( 'last', 'before', $field );
printf(
'<input type="text" %s %s>',
wpforms_html_attributes( $last['id'], $last['class'], $last['data'], $last['attr'] ),
$last['required']
);
$this->field_display_sublabel( 'last', 'after', $field );
$this->field_display_error( 'last', $field );
echo '</div>';
echo '</div>';
}
}
/**
* Validate field on form submit.
*
* @since 1.0.0
*
* @param int $field_id Field id.
* @param array|string $field_submit Field submit.
* @param array $form_data Form data.
*/
public function validate( $field_id, $field_submit, $form_data ) {
if ( empty( $form_data['fields'][ $field_id ]['required'] ) ) {
return;
}
// Extended validation needed for the different name fields.
$form_id = $form_data['id'];
$format = $form_data['fields'][ $field_id ]['format'];
$required = wpforms_get_required_label();
$process = wpforms()->get( 'process' );
if ( $format === 'simple' && wpforms_is_empty_string( $field_submit ) ) {
$process->errors[ $form_id ][ $field_id ] = $required;
return;
}
if ( ! ( $format === 'first-last' || $format === 'first-middle-last' ) ) {
return;
}
$this->validate_complicated_formats( $process, $form_id, $field_id, $field_submit, $required );
}
/**
* Format and sanitize field.
*
* @since 1.0.0
*
* @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 ) {
// Define data.
$name = isset( $form_data['fields'][ $field_id ]['label'] ) && ! wpforms_is_empty_string( $form_data['fields'][ $field_id ]['label'] ) ? $form_data['fields'][ $field_id ]['label'] : '';
$first = isset( $field_submit['first'] ) && ! wpforms_is_empty_string( $field_submit['first'] ) ? $field_submit['first'] : '';
$middle = isset( $field_submit['middle'] ) && ! wpforms_is_empty_string( $field_submit['middle'] ) ? $field_submit['middle'] : '';
$last = isset( $field_submit['last'] ) && ! wpforms_is_empty_string( $field_submit['last'] ) ? $field_submit['last'] : '';
if ( is_array( $field_submit ) ) {
$value = implode( ' ', array_filter( [ $first, $middle, $last ] ) );
} else {
$value = $field_submit;
}
// Set final field details.
wpforms()->process->fields[ $field_id ] = [
'name' => sanitize_text_field( $name ),
'value' => sanitize_text_field( $value ),
'id' => absint( $field_id ),
'type' => $this->type,
'first' => sanitize_text_field( $first ),
'middle' => sanitize_text_field( $middle ),
'last' => sanitize_text_field( $last ),
];
}
/**
* Determine if the field requires fieldset+legend instead of the regular field label.
*
* @since 1.8.1
*
* @param bool $requires_fieldset True if requires fieldset.
* @param array $field Field data.
*
* @return bool
*
* @noinspection PhpUnusedParameterInspection
*/
public function is_field_requires_fieldset( $requires_fieldset, $field ) {
return isset( $field['format'] ) && $field['format'] !== 'simple';
}
/**
* Validate complicated formats.
*
* @since 1.8.2.3
*
* @param WPForms_Process $process Process class instance.
* @param int|string $form_id Form id.
* @param int|string $field_id Field id.
* @param array $field_submit Field submit.
* @param string $required Required message text.
*/
private function validate_complicated_formats( $process, $form_id, $field_id, $field_submit, $required ) {
// Prevent PHP Warning: Illegal string offset first or 'last'.
if ( isset( $process->errors[ $form_id ][ $field_id ] ) ) {
$process->errors[ $form_id ][ $field_id ] = (array) $process->errors[ $form_id ][ $field_id ];
}
if ( isset( $field_submit['first'] ) && wpforms_is_empty_string( $field_submit['first'] ) ) {
$process->errors[ $form_id ][ $field_id ]['first'] = $required;
}
if ( isset( $field_submit['last'] ) && wpforms_is_empty_string( $field_submit['last'] ) ) {
$process->errors[ $form_id ][ $field_id ]['last'] = $required;
}
}
}
new WPForms_Field_Name();

View File

@@ -0,0 +1,510 @@
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Number Slider field.
*
* @since 1.5.7
*/
class WPForms_Field_Number_Slider extends WPForms_Field {
/**
* Default minimum value of the field.
*
* @since 1.5.7
*/
const SLIDER_MIN = 0;
/**
* Default maximum value of the field.
*
* @since 1.5.7
*/
const SLIDER_MAX = 10;
/**
* Default step value of the field.
*
* @since 1.5.7
*/
const SLIDER_STEP = 1;
/**
* Primary class constructor.
*
* @since 1.5.7
*/
public function init() {
// Define field type information.
$this->name = esc_html__( 'Number Slider', 'wpforms-lite' );
$this->type = 'number-slider';
$this->icon = 'fa-sliders';
$this->order = 180;
// Customize value format for HTML emails.
add_filter( 'wpforms_html_field_value', [ $this, 'html_email_value' ], 10, 4 );
// Builder strings.
add_filter( 'wpforms_builder_strings', [ $this, 'add_builder_strings' ] );
}
/**
* Add Builder strings.
*
* @since 1.6.2.3
*
* @param array $strings Form Builder strings.
*
* @return array Form Builder strings.
*/
public function add_builder_strings( $strings ) {
$strings['error_number_slider_increment'] = esc_html__( 'Increment value should be greater than zero. Decimal fractions allowed.', 'wpforms-lite' );
return $strings;
}
/**
* Customize format for HTML email notifications.
*
* @since 1.5.7
*
* @param string $val Field value.
* @param array $field Field settings.
* @param array $form_data Form data and settings.
* @param string $context Value display context.
*
* @return string
*/
public function html_email_value( $val, $field, $form_data = [], $context = '' ) {
if ( empty( $field['value_raw'] ) || $field['type'] !== $this->type ) {
return $val;
}
$value = isset( $field['value_raw']['value'] ) ? (float) $field['value_raw']['value'] : 0;
$min = isset( $field['value_raw']['min'] ) ? (float) $field['value_raw']['min'] : self::SLIDER_MIN;
$max = isset( $field['value_raw']['max'] ) ? (float) $field['value_raw']['max'] : self::SLIDER_MAX;
$html_value = $value;
if ( strpos( $field['value_raw']['value_display'], '{value}' ) !== false ) {
$html_value = str_replace(
'{value}',
/* translators: %1$s - Number slider selected value, %2$s - its minimum value, %3$s - its maximum value. */
sprintf( esc_html__( '%1$s (%2$s min / %3$s max)', 'wpforms-lite' ), $value, $min, $max ),
$field['value_raw']['value_display']
);
}
return $html_value;
}
/**
* Field options panel inside the builder.
*
* @since 1.5.7
*
* @param array $field Field 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 );
// Required toggle disabled.
$this->field_element(
'text',
$field,
[
'slug' => 'required',
'value' => '',
'type' => 'hidden',
]
);
// Value: min/max.
$lbl = $this->field_element(
'label',
$field,
[
'slug' => 'value',
'value' => esc_html__( 'Value', 'wpforms-lite' ),
'tooltip' => esc_html__( 'Define the minimum and the maximum values for the slider.', 'wpforms-lite' ),
],
false
);
$min = $this->field_element(
'text',
$field,
[
'type' => 'number',
'slug' => 'min',
'class' => 'wpforms-number-slider-min',
'value' => ! empty( $field['min'] ) ? (float) $field['min'] : self::SLIDER_MIN,
],
false
);
$max = $this->field_element(
'text',
$field,
[
'type' => 'number',
'slug' => 'max',
'class' => 'wpforms-number-slider-max',
'value' => ! empty( $field['max'] ) ? (float) $field['max'] : self::SLIDER_MAX,
],
false
);
$this->field_element(
'row',
$field,
[
'slug' => 'min_max',
'content' => $lbl . wpforms_render(
'fields/number-slider/builder-option-min-max',
[
'label' => $lbl,
'input_min' => $min,
'input_max' => $max,
'field_id' => $field['id'],
],
true
),
]
);
// Default value.
$lbl = $this->field_element(
'label',
$field,
[
'slug' => 'default_value',
'value' => esc_html__( 'Default Value', 'wpforms-lite' ),
'tooltip' => esc_html__( 'Enter a default value for this field.', 'wpforms-lite' ),
],
false
);
$fld = $this->field_element(
'text',
$field,
[
'type' => 'number',
'slug' => 'default_value',
'class' => 'wpforms-number-slider-default-value',
'value' => ! empty( $field['default_value'] ) ? (float) $field['default_value'] : 0,
'attrs' => [
'min' => isset( $field['min'] ) && is_numeric( $field['min'] ) ? (float) $field['min'] : self::SLIDER_MIN,
'max' => isset( $field['max'] ) && is_numeric( $field['max'] ) ? (float) $field['max'] : self::SLIDER_MAX,
'step' => isset( $field['step'] ) && is_numeric( $field['step'] ) ? (float) $field['step'] : self::SLIDER_STEP,
],
],
false
);
$this->field_element(
'row',
$field,
[
'slug' => 'default_value',
'content' => $lbl . $fld,
]
);
// Steps.
$lbl = $this->field_element(
'label',
$field,
[
'slug' => 'step',
'value' => esc_html__( 'Increment', 'wpforms-lite' ),
'tooltip' => esc_html__( 'Determines the increment between selectable values on the slider.', 'wpforms-lite' ),
],
false
);
$fld = $this->field_element(
'text',
$field,
[
'type' => 'number',
'slug' => 'step',
'class' => 'wpforms-number-slider-step',
'value' => ! empty( $field['step'] ) ? abs( $field['step'] ) : self::SLIDER_STEP,
'attrs' => [
'min' => 0,
'max' => isset( $field['max'] ) && is_numeric( $field['max'] ) ? abs( (float) $field['max'] ) : self::SLIDER_MAX,
],
],
false
);
$this->field_element(
'row',
$field,
[
'slug' => 'step',
'content' => $lbl . $fld,
]
);
// 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 );
// Value display.
$lbl = $this->field_element(
'label',
$field,
[
'slug' => 'value_display',
'value' => esc_html__( 'Value Display', 'wpforms-lite' ),
'tooltip' => esc_html__( 'Displays the currently selected value below the slider.', 'wpforms-lite' ),
],
false
);
$fld = $this->field_element(
'text',
$field,
[
'slug' => 'value_display',
'class' => 'wpforms-number-slider-value-display',
'value' => isset( $field['value_display'] ) ? $field['value_display'] : $this->get_default_display_value(),
],
false
);
$this->field_element(
'row',
$field,
[
'slug' => 'value_display',
'content' => $lbl . $fld,
]
);
// 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 );
}
/**
* Get default display value.
*
* @since 1.7.1
*
* @return string
*/
private function get_default_display_value() {
return sprintf( /* translators: %s - value. */
esc_html__( 'Selected Value: %s', 'wpforms-lite' ),
'{value}'
);
}
/**
* Field preview inside the builder.
*
* @since 1.5.7
*
* @param array $field Field data.
*/
public function field_preview( $field ) {
// Label.
$this->field_preview_option( 'label', $field );
$value_display = isset( $field['value_display'] ) ? esc_attr( $field['value_display'] ) : $this->get_default_display_value();
$default_value = ! empty( $field['default_value'] ) ? (float) $field['default_value'] : 0;
echo wpforms_render( // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
'fields/number-slider/builder-preview',
[
'min' => isset( $field['min'] ) && is_numeric( $field['min'] ) ? (float) $field['min'] : self::SLIDER_MIN,
'max' => isset( $field['max'] ) && is_numeric( $field['max'] ) ? (float) $field['max'] : self::SLIDER_MAX,
'step' => isset( $field['step'] ) && is_numeric( $field['step'] ) ? (float) $field['step'] : self::SLIDER_STEP,
'value_display' => $value_display,
'default_value' => $default_value,
'value_hint' => str_replace( '{value}', '<b>' . $default_value . '</b>', wp_kses( $value_display, wpforms_builder_preview_get_allowed_tags() ) ),
'field_id' => $field['id'],
],
true
);
// Description.
$this->field_preview_option( 'description', $field );
}
/**
* Field display on the form front-end.
*
* @since 1.5.7
*
* @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 ) {
// Define data.
$primary = $field['properties']['inputs']['primary'];
$value_display = isset( $field['value_display'] ) ? esc_attr( $field['value_display'] ) : esc_html__( 'Selected Value: {value}', 'wpforms-lite' );
$default_value = ! empty( $field['default_value'] ) ? (float) $field['default_value'] : 0;
$hint_value = ! empty( $primary['attr']['value'] ) ? (float) $primary['attr']['value'] : $default_value;
$hint = str_replace( '{value}', '<b>' . $hint_value . '</b>', $value_display );
// phpcs:ignore
echo wpforms_render(
'fields/number-slider/frontend',
[
'atts' => $primary['attr'],
'class' => $primary['class'],
'datas' => $primary['data'],
'default_value' => $default_value,
'id' => $primary['id'],
'max' => isset( $field['max'] ) && is_numeric( $field['max'] ) ? (float) $field['max'] : self::SLIDER_MAX,
'min' => isset( $field['min'] ) && is_numeric( $field['min'] ) ? (float) $field['min'] : self::SLIDER_MIN,
'required' => $primary['required'],
'step' => isset( $field['step'] ) && is_numeric( $field['step'] ) ? (float) $field['step'] : self::SLIDER_STEP,
'value_display' => $value_display,
'value_hint' => $hint,
],
true
);
}
/**
* Validate field on form submit.
*
* @since 1.5.7
*
* @param int $field_id Field ID.
* @param int|float|string $field_submit Submitted field value.
* @param array $form_data Form data and settings.
*/
public function validate( $field_id, $field_submit, $form_data ) {
$form_id = $form_data['id'];
$field_submit = (float) $this->sanitize_value( $field_submit );
// Basic required check - if field is marked as required, check for entry data.
if (
! empty( $form_data['fields'][ $field_id ]['required'] ) &&
empty( $field_submit ) &&
(string) $field_submit !== '0'
) {
wpforms()->process->errors[ $form_id ][ $field_id ] = wpforms_get_required_label();
}
// Check if value is numeric.
if ( ! empty( $field_submit ) && ! is_numeric( $field_submit ) ) {
wpforms()->process->errors[ $form_id ][ $field_id ] = apply_filters( 'wpforms_valid_number_label', esc_html__( 'Please provide a valid value.', 'wpforms-lite' ) );
}
}
/**
* Format and sanitize field.
*
* @since 1.5.7
*
* @param int $field_id Field ID.
* @param int|string|float $field_submit Submitted field value.
* @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'] : '';
$value = (float) $this->sanitize_value( $field_submit );
$value_raw = [
'value' => $value,
'min' => (float) $form_data['fields'][ $field_id ]['min'],
'max' => (float) $form_data['fields'][ $field_id ]['max'],
'value_display' => wp_kses_post( $form_data['fields'][ $field_id ]['value_display'] ),
];
// Set final field details.
wpforms()->process->fields[ $field_id ] = [
'name' => sanitize_text_field( $name ),
'value' => $value,
'value_raw' => $value_raw,
'id' => absint( $field_id ),
'type' => $this->type,
];
}
/**
* Sanitize the value.
*
* @since 1.5.7
*
* @param string $value The number field submitted value.
*
* @return float|int|string
*/
private function sanitize_value( $value ) {
// Some browsers allow other non-digit/decimal characters to be submitted
// with the num input, which then trips the is_numeric validation below.
// To get around this we remove all chars that are not expected.
$signed_value = preg_replace( '/[^-0-9.]/', '', $value );
$abs_value = abs( $signed_value );
$value = strpos( $signed_value, '-' ) === 0 ? '-' . $abs_value : $abs_value;
return $value;
}
}
new WPForms_Field_Number_Slider();

View File

@@ -0,0 +1,220 @@
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Number text field.
*
* @since 1.0.0
*/
class WPForms_Field_Number extends WPForms_Field {
/**
* Primary class constructor.
*
* @since 1.0.0
*/
public function init() {
// Define field type information.
$this->name = esc_html__( 'Numbers', 'wpforms-lite' );
$this->type = 'number';
$this->icon = 'fa-hashtag';
$this->order = 130;
}
/**
* Field options panel inside the builder.
*
* @since 1.0.0
*
* @param array $field Field data.
*/
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 );
// 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.0.0
*
* @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 );
// Primary input.
echo '<input type="text" 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.0.0
*
* @param array $field Field data.
* @param array $deprecated Deprecated, not used.
* @param array $form_data Form data.
*/
public function field_display( $field, $deprecated, $form_data ) {
// Define data.
$primary = $field['properties']['inputs']['primary'];
// Primary field.
printf(
'<input type="number" pattern="\d*" %s %s>',
wpforms_html_attributes( $primary['id'], $primary['class'], $primary['data'], $primary['attr'] ),
esc_attr( $primary['required'] )
);
}
/**
* Validate field on form submit.
*
* @since 1.0.0
*
* @param int $field_id Field id.
* @param string $field_submit Submitted value.
* @param array $form_data Form data.
*/
public function validate( $field_id, $field_submit, $form_data ) {
$form_id = $form_data['id'];
$value = $this->sanitize_value( $field_submit );
// If field is marked as required, check for entry data.
if (
! empty( $form_data['fields'][ $field_id ]['required'] ) &&
empty( $value ) &&
! is_numeric( $value )
) {
wpforms()->process->errors[ $form_id ][ $field_id ] = wpforms_get_required_label();
}
// Check if value is numeric.
if ( ! empty( $value ) && ! is_numeric( $value ) ) {
wpforms()->process->errors[ $form_id ][ $field_id ] = apply_filters( 'wpforms_valid_number_label', esc_html__( 'Please enter a valid number.', 'wpforms-lite' ) );
}
}
/**
* Format and sanitize field.
*
* @since 1.3.5
*
* @param int $field_id Field id.
* @param string $field_submit Submitted value.
* @param array $form_data Form data.
*/
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'] : '';
// Set final field details.
wpforms()->process->fields[ $field_id ] = [
'name' => sanitize_text_field( $name ),
'value' => $this->sanitize_value( $field_submit ),
'id' => absint( $field_id ),
'type' => $this->type,
];
}
/**
* Sanitize the value.
*
* @since 1.5.7
*
* @param string $value The number field submitted value.
*
* @return float|int|string
*/
private function sanitize_value( $value ) {
if ( empty( $value ) && ! is_numeric( $value ) ) {
return '';
}
// Some browsers allow other non-digit/decimal characters to be submitted
// with the num input, which then trips the is_numeric validation below.
// To get around this we remove all chars that are not expected.
$signed_value = preg_replace( '/[^-0-9.]/', '', $value );
$abs_value = str_replace( '-', '', $signed_value );
return $signed_value < 0 ? '-' . $abs_value : $abs_value;
}
}
new WPForms_Field_Number();

View File

@@ -0,0 +1,617 @@
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Multiple Choice field.
*
* @since 1.0.0
*/
class WPForms_Field_Radio extends WPForms_Field {
/**
* Primary class constructor.
*
* @since 1.0.0
*/
public function init() {
// Define field type information.
$this->name = esc_html__( 'Multiple Choice', 'wpforms-lite' );
$this->keywords = esc_html__( 'radio', 'wpforms-lite' );
$this->type = 'radio';
$this->icon = 'fa-dot-circle-o';
$this->order = 110;
$this->defaults = [
1 => [
'label' => esc_html__( 'First Choice', 'wpforms-lite' ),
'value' => '',
'image' => '',
'icon' => '',
'icon_style' => '',
'default' => '',
],
2 => [
'label' => esc_html__( 'Second Choice', 'wpforms-lite' ),
'value' => '',
'image' => '',
'icon' => '',
'icon_style' => '',
'default' => '',
],
3 => [
'label' => esc_html__( 'Third Choice', 'wpforms-lite' ),
'value' => '',
'image' => '',
'icon' => '',
'icon_style' => '',
'default' => '',
],
];
$this->hooks();
}
/**
* Hooks.
*
* @since 1.8.1
*/
private function hooks() {
// Customize HTML field values.
add_filter( 'wpforms_html_field_value', [ $this, 'field_html_value' ], 10, 4 );
// Define additional field properties.
add_filter( 'wpforms_field_properties_radio', [ $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.4.5
*
* @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'];
$dynamic = wpforms_get_field_dynamic_choices( $field, $form_id, $form_data );
if ( $dynamic !== false ) {
$choices = $dynamic;
$field['show_values'] = true;
}
// Set input container (ul) properties.
$properties['input_container'] = [
'class' => [ ! empty( $field['random'] ) ? 'wpforms-randomize' : '' ],
'data' => [],
'attr' => [],
'id' => "wpforms-{$form_id}-field_{$field_id}",
];
// Set input properties.
foreach ( $choices as $key => $choice ) {
// Used for dynamic choices.
$depth = isset( $choice['depth'] ) ? absint( $choice['depth'] ) : 1;
$value = isset( $field['show_values'] ) ? $choice['value'] : $choice['label'];
/* translators: %s - choice number. */
$value = ( $value === '' ) ? sprintf( esc_html__( 'Choice %s', 'wpforms-lite' ), $key ) : $value;
$properties['inputs'][ $key ] = [
'container' => [
'attr' => [],
'class' => [ "choice-{$key}", "depth-{$depth}" ],
'data' => [],
'id' => '',
],
'label' => [
'attr' => [
'for' => "wpforms-{$form_id}-field_{$field_id}_{$key}",
],
'class' => [ 'wpforms-field-label-inline' ],
'data' => [],
'id' => '',
'text' => $choice['label'],
],
'attr' => [
'name' => "wpforms[fields][{$field_id}]",
'value' => $value,
],
'class' => [],
'data' => [],
'id' => "wpforms-{$form_id}-field_{$field_id}_{$key}",
'icon' => isset( $choice['icon'] ) ? $choice['icon'] : '',
'icon_style' => isset( $choice['icon_style'] ) ? $choice['icon_style'] : '',
'image' => isset( $choice['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 is enabled.
if ( ! $dynamic && ! 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 ( ! $dynamic && ! empty( $field['choices_icons'] ) ) {
$properties = wpforms()->get( '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;
}
/**
* 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',
]
);
// Label.
$this->field_option( 'label', $field );
// Choices.
$this->field_option( 'choices', $field );
// Choices Images.
$this->field_option( 'choices_images', $field );
// Choices 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',
]
);
// Randomize order of choices.
$this->field_element(
'row',
$field,
[
'slug' => 'random',
'content' => $this->field_element(
'toggle',
$field,
[
'slug' => 'random',
'value' => isset( $field['random'] ) ? '1' : '0',
'desc' => esc_html__( 'Randomize Choices', 'wpforms-lite' ),
'tooltip' => esc_html__( 'Check this option to randomize the order of the choices.', 'wpforms-lite' ),
],
false
),
]
);
// Show Values toggle option. This option will only show if already used
// or if manually enabled by a filter.
if ( ! empty( $field['show_values'] ) || wpforms_show_fields_options_setting() ) {
$this->field_element(
'row',
$field,
[
'slug' => 'show_values',
'content' => $this->field_element(
'toggle',
$field,
[
'slug' => 'show_values',
'value' => isset( $field['show_values'] ) ? $field['show_values'] : '0',
'desc' => esc_html__( 'Show Values', 'wpforms-lite' ),
'tooltip' => esc_html__( 'Check this option to manually set form field values.', 'wpforms-lite' ),
],
false
),
]
);
}
// Display format.
$this->field_option( 'input_columns', $field );
// Dynamic choice auto-populating toggle.
$this->field_option( 'dynamic_choices', $field );
// Dynamic choice source.
$this->field_option( 'dynamic_choices_source', $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.0.0
*
* @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 and admin entry edit page.
*
* @since 1.0.0
*
* @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 ) {
$using_image_choices = empty( $field['dynamic_choices'] ) && empty( $field['choices_icons'] ) && ! empty( $field['choices_images'] );
$using_icon_choices = empty( $field['dynamic_choices'] ) && empty( $field['choices_images'] ) && ! empty( $field['choices_icons'] );
// Define data.
$container = $field['properties']['input_container'];
$choices = $field['properties']['inputs'];
// Do not display the field with empty choices on the frontend.
if ( ! $choices && ! is_admin() ) {
return;
}
// Display a warning message on Entry Edit page.
if ( ! $choices && is_admin() ) {
$this->display_empty_dynamic_choices_message( $field );
return;
}
$amp_state_id = '';
if ( wpforms_is_amp() && ( $using_image_choices || $using_icon_choices ) ) {
$amp_state_id = str_replace( '-', '_', sanitize_key( $container['id'] ) ) . '_state';
$state = [
'selected' => null,
];
foreach ( $choices as $key => $choice ) {
if ( $choice['default'] ) {
$state['selected'] = $choice['attr']['value'];
break;
}
}
printf(
'<amp-state id="%s"><script type="application/json">%s</script></amp-state>',
esc_attr( $amp_state_id ),
wp_json_encode( $state )
);
}
printf(
'<ul %s>',
wpforms_html_attributes( $container['id'], $container['class'], $container['data'], $container['attr'] )
);
foreach ( $choices as $key => $choice ) {
if ( wpforms_is_amp() && ( $using_image_choices || $using_icon_choices ) ) {
$choice['container']['attr']['[class]'] = sprintf(
'%s + ( %s == %s ? " wpforms-selected" : "")',
wp_json_encode( implode( ' ', $choice['container']['class'] ) ),
$amp_state_id,
wp_json_encode( $choice['attr']['value'] )
);
}
printf(
'<li %s>',
wpforms_html_attributes( $choice['container']['id'], $choice['container']['class'], $choice['container']['data'], $choice['container']['attr'] )
);
if ( $using_image_choices ) {
// Make sure the image choices are keyboard-accessible.
$choice['label']['attr']['tabindex'] = 0;
if ( wpforms_is_amp() ) {
$choice['label']['attr']['on'] = sprintf(
'tap:AMP.setState(%s)',
wp_json_encode( [ $amp_state_id => $choice['attr']['value'] ] )
);
$choice['label']['attr']['role'] = 'button';
}
// Image choices.
printf(
'<label %s>',
wpforms_html_attributes( $choice['label']['id'], $choice['label']['class'], $choice['label']['data'], $choice['label']['attr'] )
);
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>';
}
$choice['attr']['tabindex'] = '-1';
if ( wpforms_is_amp() ) {
$choice['attr']['[checked]'] = sprintf(
'%s == %s',
$amp_state_id,
wp_json_encode( $choice['attr']['value'] )
);
}
printf(
'<input type="radio" %s %s %s>',
wpforms_html_attributes( $choice['id'], $choice['class'], $choice['data'], $choice['attr'] ),
esc_attr( $choice['required'] ),
checked( '1', $choice['default'], false )
);
echo '<span class="wpforms-image-choices-label">' . wp_kses_post( $choice['label']['text'] ) . '</span>';
echo '</label>';
} elseif ( $using_icon_choices ) {
if ( wpforms_is_amp() ) {
$choice['label']['attr']['on'] = sprintf(
'tap:AMP.setState(%s)',
wp_json_encode( [ $amp_state_id => $choice['attr']['value'] ] )
);
$choice['label']['attr']['role'] = 'button';
}
// Icon Choices.
wpforms()->get( 'icon_choices' )->field_display( $field, $choice, 'radio' );
} else {
// Normal display.
printf(
'<input type="radio" %s %s %s>',
wpforms_html_attributes( $choice['id'], $choice['class'], $choice['data'], $choice['attr'] ),
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'] ),
wp_kses_post( $choice['label']['text'] )
);
}
echo '</li>';
}
echo '</ul>';
}
/**
* Validate field.
*
* @since 1.8.2
*
* @param int $field_id Field ID.
* @param string|array $field_submit Submitted field value (selected option).
* @param array $form_data Form data and settings.
*/
public function validate( $field_id, $field_submit, $form_data ) {
$field = $form_data['fields'][ $field_id ];
// Skip validation if field is dynamic and choices are empty.
if ( $this->is_dynamic_choices_empty( $field, $form_data ) ) {
return;
}
parent::validate( $field_id, $field_submit, $form_data );
}
/**
* Format and sanitize field.
*
* @since 1.0.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 ];
$dynamic = ! empty( $field['dynamic_choices'] ) ? $field['dynamic_choices'] : false;
$name = sanitize_text_field( $field['label'] );
$value_raw = sanitize_text_field( $field_submit );
$data = [
'name' => $name,
'value' => '',
'value_raw' => $value_raw,
'id' => absint( $field_id ),
'type' => $this->type,
];
if ( 'post_type' === $dynamic && ! empty( $field['dynamic_post_type'] ) ) {
// Dynamic population is enabled using post type.
$data['dynamic'] = 'post_type';
$data['dynamic_items'] = absint( $value_raw );
$data['dynamic_post_type'] = $field['dynamic_post_type'];
$post = get_post( $value_raw );
if ( ! empty( $post ) && ! is_wp_error( $post ) && $data['dynamic_post_type'] === $post->post_type ) {
$data['value'] = esc_html( wpforms_get_post_title( $post ) );
}
} elseif ( 'taxonomy' === $dynamic && ! empty( $field['dynamic_taxonomy'] ) ) {
// Dynamic population is enabled using taxonomy.
$data['dynamic'] = 'taxonomy';
$data['dynamic_items'] = absint( $value_raw );
$data['dynamic_taxonomy'] = $field['dynamic_taxonomy'];
$term = get_term( $value_raw, $data['dynamic_taxonomy'] );
if ( ! empty( $term ) && ! is_wp_error( $term ) ) {
$data['value'] = esc_html( wpforms_get_term_name( $term ) );
}
} else {
// Normal processing, dynamic population is off.
$choice_key = '';
// If show_values is true, that means value posted is the raw value
// and not the label. So we need to set label value. Also store
// the choice key.
if ( ! empty( $field['show_values'] ) ) {
foreach ( $field['choices'] as $key => $choice ) {
if ( $choice['value'] === $field_submit ) {
$data['value'] = sanitize_text_field( $choice['label'] );
$choice_key = $key;
break;
}
}
} else {
$data['value'] = $value_raw;
// Determine choice key, this is needed for image choices.
foreach ( $field['choices'] as $key => $choice ) {
/* translators: %s - choice number. */
if ( $field_submit === $choice['label'] || $value_raw === sprintf( esc_html__( 'Choice %s', 'wpforms-lite' ), $key ) ) {
$choice_key = $key;
break;
}
}
}
// Images choices are enabled, lookup and store image URL.
if ( ! empty( $choice_key ) && ! empty( $field['choices_images'] ) ) {
$data['image'] = ! empty( $field['choices'][ $choice_key ]['image'] ) ? esc_url_raw( $field['choices'][ $choice_key ]['image'] ) : '';
}
}
// Push field details to be saved.
wpforms()->process->fields[ $field_id ] = $data;
}
}
new WPForms_Field_Radio();

View File

@@ -0,0 +1,751 @@
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Dropdown field.
*
* @since 1.0.0
*/
class WPForms_Field_Select extends WPForms_Field {
/**
* Choices JS version.
*
* @since 1.6.3
*/
const CHOICES_VERSION = '9.0.1';
/**
* Classic (old) style.
*
* @since 1.6.1
*
* @var string
*/
const STYLE_CLASSIC = 'classic';
/**
* Modern style.
*
* @since 1.6.1
*
* @var string
*/
const STYLE_MODERN = 'modern';
/**
* Primary class constructor.
*
* @since 1.0.0
*/
public function init() {
// Define field type information.
$this->name = esc_html__( 'Dropdown', 'wpforms-lite' );
$this->keywords = esc_html__( 'choice', 'wpforms-lite' );
$this->type = 'select';
$this->icon = 'fa-caret-square-o-down';
$this->order = 70;
$this->defaults = [
1 => [
'label' => esc_html__( 'First Choice', 'wpforms-lite' ),
'value' => '',
'default' => '',
],
2 => [
'label' => esc_html__( 'Second Choice', 'wpforms-lite' ),
'value' => '',
'default' => '',
],
3 => [
'label' => esc_html__( 'Third Choice', 'wpforms-lite' ),
'value' => '',
'default' => '',
],
];
// 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' ] );
add_action( 'enqueue_block_editor_assets', [ $this, 'enqueue_block_editor_assets' ] );
}
/**
* Define additional field properties.
*
* @since 1.5.0
*
* @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'];
$dynamic = wpforms_get_field_dynamic_choices( $field, $form_id, $form_data );
if ( $dynamic !== false ) {
$choices = $dynamic;
$field['show_values'] = true;
}
// Set options container (<select>) properties.
$properties['input_container'] = [
'class' => [],
'data' => [],
'id' => "wpforms-{$form_id}-field_{$field_id}",
'attr' => [
'name' => "wpforms[fields][{$field_id}]",
],
];
// Set properties.
foreach ( $choices as $key => $choice ) {
// Used for dynamic choices.
$depth = isset( $choice['depth'] ) ? absint( $choice['depth'] ) : 1;
$properties['inputs'][ $key ] = [
'container' => [
'attr' => [],
'class' => [ "choice-{$key}", "depth-{$depth}" ],
'data' => [],
'id' => '',
],
'label' => [
'attr' => [
'for' => "wpforms-{$form_id}-field_{$field_id}_{$key}",
],
'class' => [ 'wpforms-field-label-inline' ],
'data' => [],
'id' => '',
'text' => $choice['label'],
],
'attr' => [
'name' => "wpforms[fields][{$field_id}]",
'value' => isset( $field['show_values'] ) ? $choice['value'] : $choice['label'],
],
'class' => [],
'data' => [],
'id' => "wpforms-{$form_id}-field_{$field_id}_{$key}",
'required' => ! empty( $field['required'] ) ? 'required' : '',
'default' => isset( $choice['default'] ),
];
}
// Add 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']}";
}
return $properties;
}
/**
* 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',
]
);
// Label.
$this->field_option( 'label', $field );
// Choices.
$this->field_option( 'choices', $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',
]
);
// Show Values toggle option. This option will only show if already used
// or if manually enabled by a filter.
if ( ! empty( $field['show_values'] ) || wpforms_show_fields_options_setting() ) {
$show_values = $this->field_element(
'toggle',
$field,
[
'slug' => 'show_values',
'value' => isset( $field['show_values'] ) ? $field['show_values'] : '0',
'desc' => esc_html__( 'Show Values', 'wpforms-lite' ),
'tooltip' => esc_html__( 'Check this option to manually set form field values.', 'wpforms-lite' ),
],
false
);
$this->field_element(
'row',
$field,
[
'slug' => 'show_values',
'content' => $show_values,
]
);
}
// Multiple options selection.
$fld = $this->field_element(
'toggle',
$field,
[
'slug' => 'multiple',
'value' => ! empty( $field['multiple'] ),
'desc' => esc_html__( 'Multiple Options Selection', 'wpforms-lite' ),
'tooltip' => esc_html__( 'Allow users to select multiple choices in this field.', 'wpforms-lite' ) . '<br>' .
sprintf(
wp_kses( /* translators: %s - URL to WPForms.com doc article. */
esc_html__( 'For details, including how this looks and works for your site\'s visitors, please check out <a href="%s" target="_blank" rel="noopener noreferrer">our doc</a>.', 'wpforms-lite' ),
[
'a' => [
'href' => [],
'target' => [],
'rel' => [],
],
]
),
esc_url( wpforms_utm_link( 'https://wpforms.com/docs/how-to-allow-multiple-selections-to-a-dropdown-field-in-wpforms/', 'Field Options', 'Multiple Options Selection Documentation' ) )
),
],
false
);
$this->field_element(
'row',
$field,
[
'slug' => 'multiple',
'content' => $fld,
]
);
// 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 );
// Dynamic choice auto-populating toggle.
$this->field_option( 'dynamic_choices', $field );
// Dynamic choice source.
$this->field_option( 'dynamic_choices_source', $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.0.0
* @since 1.6.1 Added a `Modern` style select support.
*
* @param array $field Field settings.
*/
public function field_preview( $field ) {
$args = [];
// Label.
$this->field_preview_option( 'label', $field );
// Prepare arguments.
$args['modern'] = false;
if (
! empty( $field['style'] ) &&
self::STYLE_MODERN === $field['style']
) {
$args['modern'] = true;
$args['class'] = 'choicesjs-select';
}
// Choices.
$this->field_preview_option( 'choices', $field, $args );
// Description.
$this->field_preview_option( 'description', $field );
}
/**
* Field display on the form front-end and admin entry edit page.
*
* @since 1.0.0
* @since 1.5.0 Converted to a new format, where all the data are taken not from $deprecated, but field properties.
* @since 1.6.1 Added a multiple select support.
*
* @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 ) {
$container = $field['properties']['input_container'];
$field_placeholder = ! empty( $field['placeholder'] ) ? $field['placeholder'] : '';
$is_multiple = ! empty( $field['multiple'] );
$is_modern = ! empty( $field['style'] ) && $field['style'] === self::STYLE_MODERN;
$choices = $field['properties']['inputs'];
// Do not display the field with empty choices on the frontend.
if ( ! $choices && ! is_admin() ) {
return;
}
// Display a warning message on Entry Edit page.
if ( ! $choices && is_admin() ) {
$this->display_empty_dynamic_choices_message( $field );
return;
}
if ( ! empty( $field['required'] ) ) {
$container['attr']['required'] = 'required';
}
// If it's a multiple select.
if ( $is_multiple ) {
$container['attr']['multiple'] = 'multiple';
// Change a name attribute.
if ( ! empty( $container['attr']['name'] ) ) {
$container['attr']['name'] .= '[]';
}
}
// 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;
}
}
// Fake placeholder for Modern style.
if ( $is_modern && empty( $field_placeholder ) ) {
$first_choices = reset( $choices );
$field_placeholder = $first_choices['label']['text'];
}
// Preselect default if no other choices were marked as default.
printf(
'<select %s>',
wpforms_html_attributes( $container['id'], $container['class'], $container['data'], $container['attr'] )
);
// Optional placeholder.
if ( ! empty( $field_placeholder ) ) {
printf(
'<option value="" class="placeholder" disabled %s>%s</option>',
selected( false, $has_default || $is_multiple, false ),
esc_html( $field_placeholder )
);
}
// Build the select options.
foreach ( $choices as $key => $choice ) {
printf(
'<option value="%s" %s>%s</option>',
esc_attr( $choice['attr']['value'] ),
selected( true, ! empty( $choice['default'] ), false ),
esc_html( $choice['label']['text'] )
);
}
echo '</select>';
}
/**
* Validate field.
*
* @since 1.8.2
*
* @param int $field_id Field ID.
* @param string|array $field_submit Submitted field value (selected option).
* @param array $form_data Form data and settings.
*/
public function validate( $field_id, $field_submit, $form_data ) {
$field = $form_data['fields'][ $field_id ];
// Skip validation if field is dynamic and choices are empty.
if ( $this->is_dynamic_choices_empty( $field, $form_data ) ) {
return;
}
parent::validate( $field_id, $field_submit, $form_data );
}
/**
* Format and sanitize field.
*
* @since 1.0.2
* @since 1.6.1 Added a support for multiple values.
*
* @param int $field_id Field ID.
* @param string|array $field_submit Submitted field value (selected option).
* @param array $form_data Form data and settings.
*/
public function format( $field_id, $field_submit, $form_data ) {
$field = $form_data['fields'][ $field_id ];
$dynamic = ! empty( $field['dynamic_choices'] ) ? $field['dynamic_choices'] : false;
$multiple = ! empty( $field['multiple'] );
$name = sanitize_text_field( $field['label'] );
$value = [];
// Convert submitted field value to array.
if ( ! is_array( $field_submit ) ) {
$field_submit = [ $field_submit ];
}
$value_raw = wpforms_sanitize_array_combine( $field_submit );
$data = [
'name' => $name,
'value' => '',
'value_raw' => $value_raw,
'id' => absint( $field_id ),
'type' => $this->type,
];
if ( 'post_type' === $dynamic && ! empty( $field['dynamic_post_type'] ) ) {
// Dynamic population is enabled using post type (like for a `Checkboxes` field).
$value_raw = implode( ',', array_map( 'absint', $field_submit ) );
$data['value_raw'] = $value_raw;
$data['dynamic'] = 'post_type';
$data['dynamic_items'] = $value_raw;
$data['dynamic_post_type'] = $field['dynamic_post_type'];
$posts = [];
foreach ( $field_submit as $id ) {
$post = get_post( $id );
if ( ! is_wp_error( $post ) && ! empty( $post ) && $data['dynamic_post_type'] === $post->post_type ) {
$posts[] = esc_html( wpforms_get_post_title( $post ) );
}
}
$data['value'] = ! empty( $posts ) ? wpforms_sanitize_array_combine( $posts ) : '';
} elseif ( 'taxonomy' === $dynamic && ! empty( $field['dynamic_taxonomy'] ) ) {
// Dynamic population is enabled using taxonomy (like for a `Checkboxes` field).
$value_raw = implode( ',', array_map( 'absint', $field_submit ) );
$data['value_raw'] = $value_raw;
$data['dynamic'] = 'taxonomy';
$data['dynamic_items'] = $value_raw;
$data['dynamic_taxonomy'] = $field['dynamic_taxonomy'];
$terms = [];
foreach ( $field_submit as $id ) {
$term = get_term( $id, $field['dynamic_taxonomy'] );
if ( ! is_wp_error( $term ) && ! empty( $term ) ) {
$terms[] = esc_html( wpforms_get_term_name( $term ) );
}
}
$data['value'] = ! empty( $terms ) ? wpforms_sanitize_array_combine( $terms ) : '';
} else {
// Normal processing, dynamic population is off.
// If show_values is true, that means values posted are the raw values
// and not the labels. So we need to get the label values.
if ( ! empty( $field['show_values'] ) && (int) $field['show_values'] === 1 ) {
foreach ( $field_submit as $item ) {
foreach ( $field['choices'] as $choice ) {
if ( $item === $choice['value'] ) {
$value[] = $choice['label'];
break;
}
}
}
$data['value'] = ! empty( $value ) ? wpforms_sanitize_array_combine( $value ) : '';
} else {
$data['value'] = $value_raw;
}
}
// Backward compatibility: for single dropdown save a string, for multiple - array.
if ( ! $multiple && is_array( $data ) && ( 1 === count( $data ) ) ) {
$data = reset( $data );
}
// Push field details to be saved.
wpforms()->process->fields[ $field_id ] = $data;
}
/**
* Form frontend CSS enqueues.
*
* @since 1.6.1
*
* @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()->frontend->assets_global() ) {
$min = wpforms_get_min_suffix();
wp_enqueue_style(
'wpforms-choicesjs',
WPFORMS_PLUGIN_URL . "assets/css/choices{$min}.css",
[],
self::CHOICES_VERSION
);
}
}
/**
* Form frontend JS enqueues.
*
* @since 1.6.1
*
* @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()->frontend->assets_global() ) {
$this->enqueue_choicesjs_once( $forms );
}
}
/**
* Load WPForms Gutenberg block scripts.
*
* @since 1.8.1
*/
public function enqueue_block_editor_assets() {
$min = wpforms_get_min_suffix();
wp_enqueue_style(
'wpforms-choicesjs',
WPFORMS_PLUGIN_URL . "assets/css/choices{$min}.css",
[],
self::CHOICES_VERSION
);
$this->enqueue_choicesjs_once( [] );
}
/**
* Whether the provided form has a dropdown field with a specified style.
*
* @since 1.6.1
*
* @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 $is_field_style;
}
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 ajax error message.
*
* @since 1.6.3
*
* @param string $name Field name for error triggered.
* @param array $field Field settings.
* @param array $props List of properties.
* @param string $error Error message.
*
* @return string
*/
public function ajax_error_field_name( $name, $field, $props, $error ) {
if ( ! isset( $field['type'] ) || 'select' !== $field['type'] ) {
return $name;
}
if ( ! empty( $field['multiple'] ) ) {
$input = isset( $props['inputs'] ) ? end( $props['inputs'] ) : [];
return isset( $input['attr']['name'] ) ? $input['attr']['name'] . '[]' : '';
}
return $name;
}
}
new WPForms_Field_Select();

View File

@@ -0,0 +1,548 @@
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Single line text field.
*
* @since 1.0.0
*/
class WPForms_Field_Text extends WPForms_Field {
/**
* Primary class constructor.
*
* @since 1.0.0
*/
public function init() {
// Define field type information.
$this->name = esc_html__( 'Single Line Text', 'wpforms-lite' );
$this->type = 'text';
$this->icon = 'fa-text-width';
$this->order = 30;
// Define additional field properties.
add_filter( 'wpforms_field_properties_text', [ $this, 'field_properties' ], 5, 3 );
add_action( 'wpforms_frontend_js', [ $this, 'frontend_js' ] );
}
/**
* Convert mask formatted for jquery.inputmask into the format used by amp-inputmask.
*
* Note that amp-inputmask does not yet support all of the options that jquery.inputmask provides.
* In particular, amp-inputmask doesn't provides:
* - Upper-alphabetical mask.
* - Upper-alphanumeric mask.
* - Advanced Input Masks with arbitrary repeating groups.
*
* @link https://amp.dev/documentation/components/amp-inputmask
* @link https://wpforms.com/docs/how-to-use-custom-input-masks/
*
* @param string $mask Mask formatted for jquery.inputmask.
* @return array {
* Mask and placeholder.
*
* @type string $mask Mask for amp-inputmask.
* @type string $placeholder Placeholder derived from mask if one is not supplied.
* }
*/
protected function convert_mask_to_amp_inputmask( $mask ) {
$placeholder = '';
// Convert jquery.inputmask format into amp-inputmask format.
$amp_mask = '';
$req_mask_mapping = [
'9' => '0', // Numeric.
'a' => 'L', // Alphabetical (a-z or A-Z).
'A' => 'L', // Upper-alphabetical (A-Z). Note: AMP does not have an uppercase-alphabetical mask type, so same as previous.
'*' => 'A', // Alphanumeric (0-9, a-z, A-Z).
'&' => 'A', // Upper-alphanumeric (A-Z, 0-9). Note: AMP does not have an uppercase-alphanumeric mask type, so same as previous.
' ' => '_', // Automatically insert spaces.
];
$opt_mask_mapping = [
'9' => '9', // The user may optionally add a numeric character.
'a' => 'l', // The user may optionally add an alphabetical character.
'A' => 'l', // The user may optionally add an alphabetical character.
'*' => 'a', // The user may optionally add an alphanumeric character.
'&' => 'a', // The user may optionally add an alphanumeric character.
];
$placeholder_mapping = [
'9' => '0',
'a' => 'a',
'A' => 'a',
'*' => '_',
'&' => '_',
];
$is_inside_optional = false;
$last_mask_token = null;
for ( $i = 0, $len = strlen( $mask ); $i < $len; $i++ ) {
if ( '[' === $mask[ $i ] ) {
$is_inside_optional = true;
$placeholder .= $mask[ $i ];
continue;
} elseif ( ']' === $mask[ $i ] ) {
$is_inside_optional = false;
$placeholder .= $mask[ $i ];
continue;
} elseif ( isset( $last_mask_token ) && preg_match( '/^\{(?P<n>\d+)(?:,(?P<m>\d+))?\}/', substr( $mask, $i ), $matches ) ) {
$amp_mask .= str_repeat( $req_mask_mapping[ $last_mask_token ], $matches['n'] );
$placeholder .= str_repeat( $placeholder_mapping[ $last_mask_token ], $matches['n'] );
if ( isset( $matches['m'] ) ) {
$amp_mask .= str_repeat( $opt_mask_mapping[ $last_mask_token ], $matches['m'] );
$placeholder .= str_repeat( $placeholder_mapping[ $last_mask_token ], $matches['m'] );
}
$i += strlen( $matches[0] ) - 1;
$last_mask_token = null; // Reset.
continue;
}
if ( '\\' === $mask[ $i ] ) {
$amp_mask .= '\\';
$i++;
if ( ! isset( $mask[ $i ] ) ) {
continue;
}
$amp_mask .= $mask[ $i ];
} else {
// Remember this token in case it is a mask.
if ( isset( $opt_mask_mapping[ $mask[ $i ] ] ) ) {
$last_mask_token = $mask[ $i ];
}
if ( $is_inside_optional && isset( $opt_mask_mapping[ $mask[ $i ] ] ) ) {
$amp_mask .= $opt_mask_mapping[ $mask[ $i ] ];
} elseif ( isset( $req_mask_mapping[ $mask[ $i ] ] ) ) {
$amp_mask .= $req_mask_mapping[ $mask[ $i ] ];
} else {
$amp_mask .= '\\' . $mask[ $i ];
}
}
if ( isset( $placeholder_mapping[ $mask[ $i ] ] ) ) {
$placeholder .= $placeholder_mapping[ $mask[ $i ] ];
} else {
$placeholder .= $mask[ $i ];
}
}
return [ $amp_mask, $placeholder ];
}
/**
* Define additional field properties.
*
* @since 1.4.5
*
* @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 ) {
// Input primary: Detect custom input mask.
if ( empty( $field['input_mask'] ) ) {
return $properties;
}
// Add class that will trigger custom mask.
$properties['inputs']['primary']['class'][] = 'wpforms-masked-input';
if ( wpforms_is_amp() ) {
return $this->get_amp_input_mask_properties( $properties, $field );
}
$properties['inputs']['primary']['data']['rule-inputmask-incomplete'] = true;
if ( strpos( $field['input_mask'], 'alias:' ) !== false ) {
$mask = str_replace( 'alias:', '', $field['input_mask'] );
$properties['inputs']['primary']['data']['inputmask-alias'] = $mask;
return $properties;
}
if ( strpos( $field['input_mask'], 'regex:' ) !== false ) {
$mask = str_replace( 'regex:', '', $field['input_mask'] );
$properties['inputs']['primary']['data']['inputmask-regex'] = $mask;
return $properties;
}
if ( strpos( $field['input_mask'], 'date:' ) !== false ) {
$mask = str_replace( 'date:', '', $field['input_mask'] );
$properties['inputs']['primary']['data']['inputmask-alias'] = 'datetime';
$properties['inputs']['primary']['data']['inputmask-inputformat'] = $mask;
return $properties;
}
$properties['inputs']['primary']['data']['inputmask-mask'] = $field['input_mask'];
return $properties;
}
/**
* Define additional field properties for the inputmask on AMP pages.
*
* @since 1.7.6
*
* @param array $properties Field properties.
* @param array $field Field settings.
*
* @return array
*/
private function get_amp_input_mask_properties( $properties, $field ) {
list( $amp_mask, $placeholder ) = $this->convert_mask_to_amp_inputmask( $field['input_mask'] );
$properties['inputs']['primary']['attr']['mask'] = $amp_mask;
if ( empty( $properties['inputs']['primary']['attr']['placeholder'] ) ) {
$properties['inputs']['primary']['attr']['placeholder'] = $placeholder;
}
return $properties;
}
/**
* 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',
]
);
// 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',
]
);
// Size.
$this->field_option( 'size', $field );
// Placeholder.
$this->field_option( 'placeholder', $field );
// Limit length.
$args = [
'slug' => 'limit_enabled',
'content' => $this->field_element(
'toggle',
$field,
[
'slug' => 'limit_enabled',
'value' => isset( $field['limit_enabled'] ),
'desc' => esc_html__( 'Limit Length', 'wpforms-lite' ),
'tooltip' => esc_html__( 'Check this option to limit text length by characters or words count.', 'wpforms-lite' ),
],
false
),
];
$this->field_element( 'row', $field, $args );
$count = $this->field_element(
'text',
$field,
[
'type' => 'number',
'slug' => 'limit_count',
'attrs' => [
'min' => 1,
'step' => 1,
'pattern' => '[0-9]',
],
'value' => ! empty( $field['limit_count'] ) ? absint( $field['limit_count'] ) : 1,
],
false
);
$mode = $this->field_element(
'select',
$field,
[
'slug' => 'limit_mode',
'value' => ! empty( $field['limit_mode'] ) ? esc_attr( $field['limit_mode'] ) : 'characters',
'options' => [
'characters' => esc_html__( 'Characters', 'wpforms-lite' ),
'words' => esc_html__( 'Words', 'wpforms-lite' ),
],
],
false
);
$args = [
'slug' => 'limit_controls',
'class' => ! isset( $field['limit_enabled'] ) ? 'wpforms-hide' : '',
'content' => $count . $mode,
];
$this->field_element( 'row', $field, $args );
// Default value.
$this->field_option( 'default_value', $field );
// Input Mask.
$lbl = $this->field_element(
'label',
$field,
[
'slug' => 'input_mask',
'value' => esc_html__( 'Input Mask', 'wpforms-lite' ),
'tooltip' => esc_html__( 'Enter your custom input mask.', 'wpforms-lite' ),
'after_tooltip' => '<a href="' . esc_url( wpforms_utm_link( 'https://wpforms.com/docs/how-to-use-custom-input-masks/', 'Field Options', 'Input Mask Documentation' ) ) . '" class="after-label-description" target="_blank" rel="noopener noreferrer">' . esc_html__( 'See Examples & Docs', 'wpforms-lite' ) . '</a>',
],
false
);
$fld = $this->field_element(
'text',
$field,
[
'slug' => 'input_mask',
'value' => ! empty( $field['input_mask'] ) ? esc_attr( $field['input_mask'] ) : '',
],
false
);
$this->field_element(
'row',
$field,
[
'slug' => 'input_mask',
'content' => $lbl . $fld,
]
);
// 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.0.0
*
* @param array $field Field settings.
*/
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 );
// Primary input.
echo '<input type="text" 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.0.0
*
* @param array $field Field settings.
* @param array $deprecated Deprecated.
* @param array $form_data Form data and settings.
*/
public function field_display( $field, $deprecated, $form_data ) {
// Define data.
$primary = $field['properties']['inputs']['primary'];
if ( isset( $field['limit_enabled'] ) ) {
$limit_count = isset( $field['limit_count'] ) ? absint( $field['limit_count'] ) : 0;
$limit_mode = isset( $field['limit_mode'] ) ? sanitize_key( $field['limit_mode'] ) : 'characters';
$primary['data']['form-id'] = $form_data['id'];
$primary['data']['field-id'] = $field['id'];
if ( 'characters' === $limit_mode ) {
$primary['class'][] = 'wpforms-limit-characters-enabled';
$primary['attr']['maxlength'] = $limit_count;
$primary['data']['text-limit'] = $limit_count;
} else {
$primary['class'][] = 'wpforms-limit-words-enabled';
$primary['data']['text-limit'] = $limit_count;
}
}
// Primary field.
printf(
'<input type="text" %s %s>',
wpforms_html_attributes( $primary['id'], $primary['class'], $primary['data'], $primary['attr'] ),
$primary['required'] // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
);
}
/**
* Enqueue frontend limit option js.
*
* @since 1.5.6
*
* @param array $forms Forms on the current page.
*/
public function frontend_js( $forms ) {
// Get fields.
$fields = array_map(
function( $form ) {
return empty( $form['fields'] ) ? [] : $form['fields'];
},
(array) $forms
);
// Make fields flat.
$fields = array_reduce(
$fields,
function( $accumulator, $current ) {
return array_merge( $accumulator, $current );
},
[]
);
// Leave only fields with limit.
$fields = array_filter(
$fields,
function( $field ) {
return $field['type'] === $this->type && isset( $field['limit_enabled'] ) && ! empty( $field['limit_count'] );
}
);
if ( count( $fields ) ) {
$min = wpforms_get_min_suffix();
wp_enqueue_script(
'wpforms-text-limit',
WPFORMS_PLUGIN_URL . "assets/js/text-limit.es5{$min}.js",
[],
WPFORMS_VERSION,
true
);
}
}
/**
* Format and sanitize field.
*
* @since 1.5.6
*
* @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 = $form_data['fields'][ $field_id ];
$name = ! empty( $field['label'] ) ? sanitize_text_field( $field['label'] ) : '';
// Sanitize.
$value = sanitize_text_field( $field_submit );
wpforms()->process->fields[ $field_id ] = [
'name' => $name,
'value' => $value,
'id' => absint( $field_id ),
'type' => $this->type,
];
}
/**
* Validate field on form submit.
*
* @since 1.6.2
*
* @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 validate( $field_id, $field_submit, $form_data ) {
parent::validate( $field_id, $field_submit, $form_data );
if ( empty( $form_data['fields'][ $field_id ] ) || empty( $form_data['fields'][ $field_id ]['limit_enabled'] ) ) {
return;
}
$field = $form_data['fields'][ $field_id ];
$limit = absint( $field['limit_count'] );
$mode = ! empty( $field['limit_mode'] ) ? sanitize_key( $field['limit_mode'] ) : 'characters';
$value = sanitize_text_field( $field_submit );
if ( 'characters' === $mode ) {
if ( mb_strlen( str_replace( "\r\n", "\n", $value ) ) > $limit ) {
/* translators: %s - limit characters number. */
wpforms()->process->errors[ $form_data['id'] ][ $field_id ] = sprintf( _n( 'Text can\'t exceed %d character.', 'Text can\'t exceed %d characters.', $limit, 'wpforms-lite' ), $limit );
return;
}
} else {
if ( wpforms_count_words( $value ) > $limit ) {
/* translators: %s - limit words number. */
wpforms()->process->errors[ $form_data['id'] ][ $field_id ] = sprintf( _n( 'Text can\'t exceed %d word.', 'Text can\'t exceed %d words.', $limit, 'wpforms-lite' ), $limit );
return;
}
}
}
}
new WPForms_Field_Text();

View File

@@ -0,0 +1,372 @@
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Paragraph text field.
*
* @since 1.0.0
*/
class WPForms_Field_Textarea extends WPForms_Field {
/**
* Primary class constructor.
*
* @since 1.0.0
*/
public function init() {
// Define field type information.
$this->name = esc_html__( 'Paragraph Text', 'wpforms-lite' );
$this->keywords = esc_html__( 'textarea', 'wpforms-lite' );
$this->type = 'textarea';
$this->icon = 'fa-paragraph';
$this->order = 50;
add_action( 'wpforms_frontend_js', [ $this, 'frontend_js' ] );
}
/**
* Get the value, that is used to prefill via dynamic or fallback population.
* Based on field data and current properties.
*
* @since 1.6.4
*
* @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 (
! empty( $input ) &&
isset( $properties['inputs'][ $input ] )
) {
$properties['inputs'][ $input ]['attr']['value'] = wpforms_sanitize_textarea_field( wp_unslash( $raw_value ) );
}
return $properties;
}
/**
* Field options panel inside the builder.
*
* @since 1.0.0
*
* @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',
]
);
// 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.
$args = [
'markup' => 'open',
];
$this->field_option( 'advanced-options', $field, $args );
// Size.
$this->field_option( 'size', $field );
// Placeholder.
$this->field_option( 'placeholder', $field );
// Limit length.
$args = [
'slug' => 'limit_enabled',
'content' => $this->field_element(
'toggle',
$field,
[
'slug' => 'limit_enabled',
'value' => isset( $field['limit_enabled'] ) ? '1' : '0',
'desc' => esc_html__( 'Limit Length', 'wpforms-lite' ),
'tooltip' => esc_html__( 'Check this option to limit text length by characters or words count.', 'wpforms-lite' ),
],
false
),
];
$this->field_element( 'row', $field, $args );
$count = $this->field_element(
'text',
$field,
[
'type' => 'number',
'slug' => 'limit_count',
'attrs' => [
'min' => 1,
'step' => 1,
'pattern' => '[0-9]',
],
'value' => ! empty( $field['limit_count'] ) ? $field['limit_count'] : 1,
],
false
);
$mode = $this->field_element(
'select',
$field,
[
'slug' => 'limit_mode',
'value' => ! empty( $field['limit_mode'] ) ? esc_attr( $field['limit_mode'] ) : 'characters',
'options' => [
'characters' => esc_html__( 'Characters', 'wpforms-lite' ),
'words' => esc_html__( 'Words', 'wpforms-lite' ),
],
],
false
);
$args = [
'slug' => 'limit_controls',
'class' => ! isset( $field['limit_enabled'] ) ? 'wpforms-hide' : '',
'content' => $count . $mode,
];
$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 );
// Options close markup.
$this->field_option(
'advanced-options',
$field,
[
'markup' => 'close',
]
);
}
/**
* Field preview inside the builder.
*
* @since 1.0.0
*
* @param array $field Field data and settings.
*/
public function field_preview( $field ) {
// Label.
$this->field_preview_option( 'label', $field );
// Primary input.
$placeholder = ! empty( $field['placeholder'] ) ? $field['placeholder'] : '';
$default_value = ! empty( $field['default_value'] ) ? $field['default_value'] : '';
echo '<textarea placeholder="' . esc_attr( $placeholder ) . '" class="primary-input" readonly>' . esc_textarea( $default_value ) . '</textarea>';
// 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.
* @param array $form_data Form data and settings.
*/
public function field_display( $field, $deprecated, $form_data ) {
// Define data.
$primary = $field['properties']['inputs']['primary'];
$value = '';
if ( isset( $primary['attr']['value'] ) ) {
$value = esc_textarea( $primary['attr']['value'] );
unset( $primary['attr']['value'] );
}
if ( isset( $field['limit_enabled'] ) ) {
$limit_count = isset( $field['limit_count'] ) ? absint( $field['limit_count'] ) : 0;
$limit_mode = isset( $field['limit_mode'] ) ? sanitize_key( $field['limit_mode'] ) : 'characters';
$primary['data']['form-id'] = $form_data['id'];
$primary['data']['field-id'] = $field['id'];
if ( 'characters' === $limit_mode ) {
$primary['class'][] = 'wpforms-limit-characters-enabled';
$primary['attr']['maxlength'] = $limit_count;
$primary['data']['text-limit'] = $limit_count;
} else {
$primary['class'][] = 'wpforms-limit-words-enabled';
$primary['data']['text-limit'] = $limit_count;
}
}
// Primary field.
printf(
'<textarea %s %s>%s</textarea>',
wpforms_html_attributes( $primary['id'], $primary['class'], $primary['data'], $primary['attr'] ),
$primary['required'], // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
$value // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
);
}
/**
* Enqueue frontend limit option js.
*
* @since 1.5.6
*
* @param array $forms Forms on the current page.
*/
public function frontend_js( $forms ) {
// Get fields.
$fields = array_map(
function( $form ) {
return empty( $form['fields'] ) ? [] : $form['fields'];
},
(array) $forms
);
// Make fields flat.
$fields = array_reduce(
$fields,
function( $accumulator, $current ) {
return array_merge( $accumulator, $current );
},
[]
);
// Leave only fields with limit.
$fields = array_filter(
$fields,
function( $field ) {
return $field['type'] === $this->type && isset( $field['limit_enabled'] );
}
);
if ( count( $fields ) ) {
$min = wpforms_get_min_suffix();
wp_enqueue_script( 'wpforms-text-limit', WPFORMS_PLUGIN_URL . "assets/js/text-limit.es5{$min}.js", [], WPFORMS_VERSION, true );
}
}
/**
* Format and sanitize field.
*
* @since 1.5.6
*
* @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 = $form_data['fields'][ $field_id ];
if ( is_array( $field_submit ) ) {
$field_submit = implode( "\r\n", array_filter( $field_submit ) );
}
$name = ! empty( $field['label'] ) ? sanitize_text_field( $field['label'] ) : '';
// Sanitize but keep line breaks.
$value = wpforms_sanitize_textarea_field( $field_submit );
wpforms()->process->fields[ $field_id ] = [
'name' => $name,
'value' => $value,
'id' => absint( $field_id ),
'type' => $this->type,
];
}
/**
* Validate field on form submit.
*
* @since 1.6.2
*
* @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 validate( $field_id, $field_submit, $form_data ) {
parent::validate( $field_id, $field_submit, $form_data );
if ( empty( $form_data['fields'][ $field_id ] ) || empty( $form_data['fields'][ $field_id ]['limit_enabled'] ) ) {
return;
}
if ( is_array( $field_submit ) ) {
$field_submit = implode( "\r\n", array_filter( $field_submit ) );
}
$field = $form_data['fields'][ $field_id ];
$limit = absint( $field['limit_count'] );
$mode = ! empty( $field['limit_mode'] ) ? sanitize_key( $field['limit_mode'] ) : 'characters';
$value = wpforms_sanitize_textarea_field( $field_submit );
if ( 'characters' === $mode ) {
if ( mb_strlen( str_replace( "\r\n", "\n", $value ) ) > $limit ) {
/* translators: %s - limit characters number. */
wpforms()->process->errors[ $form_data['id'] ][ $field_id ] = sprintf( _n( 'Text can\'t exceed %d character.', 'Text can\'t exceed %d characters.', $limit, 'wpforms-lite' ), $limit );
return;
}
} else {
if ( wpforms_count_words( $value ) > $limit ) {
/* translators: %s - limit words number. */
wpforms()->process->errors[ $form_data['id'] ][ $field_id ] = sprintf( _n( 'Text can\'t exceed %d word.', 'Text can\'t exceed %d words.', $limit, 'wpforms-lite' ), $limit );
return;
}
}
}
}
new WPForms_Field_Textarea();

View File

@@ -0,0 +1,7 @@
<?php
/**
* Helper functions to work with multidimensional arrays easier.
*
* @since 1.5.6
* @since 1.8.0 Moved to /includes/functions/list.php
*/

View File

@@ -0,0 +1,30 @@
<?php
/**
* Global functions used in core plugin and addons.
*
* @since 1.0.0
* @since 1.8.0 Split into multiple files, see `includes/functions/`.
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
require_once __DIR__ . '/functions/access.php';
require_once __DIR__ . '/functions/checks.php';
require_once __DIR__ . '/functions/colors.php';
require_once __DIR__ . '/functions/data-presets.php';
require_once __DIR__ . '/functions/date-time.php';
require_once __DIR__ . '/functions/debug.php';
require_once __DIR__ . '/functions/education.php';
require_once __DIR__ . '/functions/escape-sanitize.php';
require_once __DIR__ . '/functions/filesystem-media.php';
require_once __DIR__ . '/functions/form-fields.php';
require_once __DIR__ . '/functions/forms.php';
require_once __DIR__ . '/functions/list.php';
require_once __DIR__ . '/functions/payments.php';
require_once __DIR__ . '/functions/plugins.php';
require_once __DIR__ . '/functions/privacy.php';
require_once __DIR__ . '/functions/providers.php';
require_once __DIR__ . '/functions/unused.php';
require_once __DIR__ . '/functions/utilities.php';

View File

@@ -0,0 +1,364 @@
<?php
/**
* Helper functions to work with licenses, permissions and capabilities.
*
* @since 1.8.0
*/
/**
* Determine if the plugin/addon installations are allowed.
*
* @since 1.6.2.3
*
* @param string $type Should be `plugin` or `addon`.
*
* @return bool
*/
function wpforms_can_install( $type ) {
return wpforms_can_do( 'install', $type );
}
/**
* Determine if the plugin/addon activations are allowed.
*
* @since 1.7.3
*
* @param string $type Should be `plugin` or `addon`.
*
* @return bool
*/
function wpforms_can_activate( $type ) {
return wpforms_can_do( 'activate', $type );
}
/**
* Determine if the plugin/addon installations/activations are allowed.
*
* @since 1.7.3
*
* @internal Use wpforms_can_activate() or wpforms_can_install() instead.
*
* @param string $what Should be 'activate' or 'install'.
* @param string $type Should be `plugin` or `addon`.
*
* @return bool
*/
function wpforms_can_do( $what, $type ) {
if ( ! in_array( $what, [ 'install', 'activate' ], true ) ) {
return false;
}
if ( ! in_array( $type, [ 'plugin', 'addon' ], true ) ) {
return false;
}
$capability = $what . '_plugins';
if ( ! current_user_can( $capability ) ) {
return false;
}
// Determine whether file modifications are allowed and it is activation permissions checking.
if ( $what === 'install' && ! wp_is_file_mod_allowed( 'wpforms_can_install' ) ) {
return false;
}
// All plugin checks are done.
if ( $type === 'plugin' ) {
return true;
}
// Addons require additional license checks.
$license = get_option( 'wpforms_license', [] );
// Allow addons installation if license is not expired, enabled and valid.
return empty( $license['is_expired'] ) && empty( $license['is_disabled'] ) && empty( $license['is_invalid'] );
}
/**
* Get the current installation license type (always lowercase).
*
* @since 1.5.6
*
* @return string|false
*/
function wpforms_get_license_type() {
$type = wpforms_setting( 'type', '', 'wpforms_license' );
if ( empty( $type ) || ! wpforms()->is_pro() ) {
return false;
}
return strtolower( $type );
}
/**
* Get the current installation license key.
*
* @since 1.6.2.3
* @since 1.8.0 WPFORMS_LICENSE_KEY constant has higher priority than the DB option.
*
* @return string
*/
function wpforms_get_license_key() {
// Allow wp-config constant to pass key.
if ( defined( 'WPFORMS_LICENSE_KEY' ) && WPFORMS_LICENSE_KEY ) {
return WPFORMS_LICENSE_KEY;
}
return wpforms_setting( 'key', '', 'wpforms_license' );
}
/**
* Get when WPForms was first installed.
*
* @since 1.6.0
*
* @param string $type Specific install type to check for.
*
* @return int|false Unix timestamp. False on failure.
*/
function wpforms_get_activated_timestamp( $type = '' ) {
$activated = (array) get_option( 'wpforms_activated', [] );
if ( empty( $activated ) ) {
return false;
}
// When a passed install type is empty, then get it from a DB.
// If it is installed/activated first, it is saved first.
$type = empty( $type ) ? (string) array_keys( $activated )[0] : $type;
if ( ! empty( $activated[ $type ] ) ) {
return absint( $activated[ $type ] );
}
// Fallback.
$types = array_diff( [ 'lite', 'pro' ], [ $type ] );
foreach ( $types as $_type ) {
if ( ! empty( $activated[ $_type ] ) ) {
return absint( $activated[ $_type ] );
}
}
return false;
}
/**
* Retrieve a timestamp when WPForms was upgraded.
*
* @since 1.7.5
*
* @param string $version Specific plugin version to check for.
*
* @return int|false Unix timestamp or migration status. False on failure.
* Available migration statuses:
* -2 if migration is failed;
* -1 if migration is started (in progress);
* 0 if migration is completed, but no luck to set a timestamp.
*/
function wpforms_get_upgraded_timestamp( $version ) {
$option_name = wpforms()->is_pro() ? 'wpforms_versions' : 'wpforms_versions_lite';
$upgrades = (array) get_option( $option_name, [] );
if ( ! isset( $upgrades[ $version ] ) ) {
return false;
}
return (int) $upgrades[ $version ];
}
/**
* Get the default capability to manage everything for WPForms.
*
* @since 1.4.4
*
* @return string
*/
function wpforms_get_capability_manage_options() {
return apply_filters( 'wpforms_manage_cap', 'manage_options' );
}
/**
* Check WPForms permissions for currently logged in user.
* Both short (e.g. 'view_own_forms') or long (e.g. 'wpforms_view_own_forms') capability name can be used.
* Only WPForms capabilities get processed.
*
* @since 1.4.4
*
* @param array|string $caps Capability name(s).
* @param int $id ID of the specific object to check against if capability is a "meta" cap. "Meta"
* capabilities, e.g. 'edit_post', 'edit_user', etc., are capabilities used by
* map_meta_cap() to map to other "primitive" capabilities, e.g. 'edit_posts',
* edit_others_posts', etc. Accessed via func_get_args() and passed to
* WP_User::has_cap(), then map_meta_cap().
*
* @return bool
*/
function wpforms_current_user_can( $caps = [], $id = 0 ) {
$access = wpforms()->get( 'access' );
if ( ! method_exists( $access, 'current_user_can' ) ) {
return false;
}
$user_can = $access->current_user_can( $caps , $id );
return apply_filters( 'wpforms_current_user_can', $user_can, $caps, $id );
}
/**
* Search for posts editable by user.
*
* @since 1.7.9
*
* @param string $search_term Optional search term. Default ''.
* @param array $args Args {
* Optional. An array of arguments.
*
* @type string $post_type Post type to search for.
* @type string[] $post_status Post status to search for.
* @type int $count Number of results to return. Default 20.
* }
*
* @return array
* @noinspection PhpTernaryExpressionCanBeReducedToShortVersionInspection
* @noinspection ElvisOperatorCanBeUsedInspection
*/
function wpforms_search_posts( $search_term = '', $args = [] ) {
global $wpdb;
$default_args = [
'post_type' => 'page',
'post_status' => [ 'publish' ],
'count' => 20,
];
$args = wp_parse_args( $args, $default_args );
// @todo: add trash access capabilities to MySQL.
// See edit_post/edit_page case in map_meta_cap().
$args['post_status'] = array_diff( $args['post_status'], [ 'trash' ] );
$user = wp_get_current_user();
$user_id = $user ? $user->ID : 0;
$post_type = get_post_type_object( $args['post_type'] );
if ( ! $user_id || ! $post_type || $args['count'] <= 0 ) {
return [];
}
$last_changed = wp_cache_get_last_changed( 'posts' );
$key = __FUNCTION__ . ":$search_term:$last_changed";
$cache_posts = wp_cache_get( $key, '', false, $found );
if ( $found ) {
return $cache_posts;
}
$post_title_where = $search_term ? $wpdb->prepare(
'post_title LIKE %s AND',
'%' . $wpdb->esc_like( $search_term ) . '%'
) :
'';
$post_statuses = array_intersect( array_keys( get_post_statuses() ), $args['post_status'] );
$post_statuses = wpforms_wpdb_prepare_in( $post_statuses );
$policy_id = (int) get_option( 'wp_page_for_privacy_policy' );
$can_delete_published_posts = (int) $user->has_cap( $post_type->cap->delete_published_posts );
$can_delete_posts = (int) $user->has_cap( $post_type->cap->delete_posts );
$can_delete_others_posts = (int) $user->has_cap( $post_type->cap->delete_others_posts );
$can_delete_private_posts = (int) $user->has_cap( $post_type->cap->delete_private_posts );
$can_edit_policy = (int) $user->has_cap( map_meta_cap( 'manage_privacy_options', $user_id )[0] );
// For the case when user is post author.
$capability_author_where = "post_author = $user_id AND
( ( post_status IN ( 'publish', 'future' ) AND $can_delete_published_posts ) OR
( ( post_status NOT IN ( 'publish', 'future', 'trash' ) ) AND $can_delete_posts )
)";
// For the case when accessing someone other's post.
$capability_other_where = "post_author != $user_id AND
$can_delete_others_posts AND
( ( post_status IN ( 'publish', 'future' ) AND $can_delete_published_posts ) OR
( ( post_status IN ( 'private' ) ) AND $can_delete_private_posts )
)";
// For privacy policy page.
$capability_policy_where = "ID = $policy_id AND $can_edit_policy";
$capability_where = '( ' .
'(' . $capability_author_where . ') OR ' .
'(' . $capability_other_where . ') OR ' .
'(' . $capability_policy_where . ')' .
' )';
// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
$posts = $wpdb->get_results(
$wpdb->prepare(
"SELECT ID, post_title, post_author
FROM $wpdb->posts
WHERE $post_title_where
post_type = %s AND
post_status IN ( $post_statuses ) AND
$capability_where
ORDER BY post_title LIMIT %d",
$args['post_type'],
absint( $args['count'] )
)
);
// phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
$posts = $posts ? $posts : [];
$posts = array_map(
static function ( $post ) {
$post->post_title = wpforms_get_post_title( $post );
unset( $post->post_author );
return $post;
},
$posts
);
wp_cache_set( $key, $posts );
return $posts;
}
/**
* Search pages by search term and return an array containing
* `value` and `label` which is the post ID and post title respectively.
*
* @since 1.7.9
*
* @param string $search_term The search term.
* @param array $args Optional. An array of arguments.
*
* @return array
*/
function wpforms_search_pages_for_dropdown( $search_term, $args = [] ) {
$search_results = wpforms_search_posts( $search_term, $args );
$result_pages = [];
// Prepare for ChoicesJS render.
foreach ( $search_results as $search_result ) {
$result_pages[] = [
'value' => absint( $search_result->ID ),
'label' => esc_html( $search_result->post_title ),
];
}
return $result_pages;
}

View File

@@ -0,0 +1,402 @@
<?php
/**
* Helper functions to perform various checks across the core plugin and addons.
*
* @since 1.8.0
*/
use WPForms\Vendor\TrueBV\Punycode;
/**
* Check if a string is a valid URL.
*
* @since 1.0.0
* @since 1.5.8 Changed the pattern used to validate the URL.
*
* @param string $url Input URL.
*
* @return bool
*/
function wpforms_is_url( $url ) {
// The pattern taken from https://gist.github.com/dperini/729294.
// It is the best choice according to the https://mathiasbynens.be/demo/url-regex.
$pattern = '%^(?:(?:(?:https?|ftp):)?\/\/)(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z0-9\x{00a1}-\x{ffff}][a-z0-9\x{00a1}-\x{ffff}_-]{0,62})?[a-z0-9\x{00a1}-\x{ffff}]\.)+(?:[a-z\x{00a1}-\x{ffff}]{2,}\.?))(?::\d{2,5})?(?:[/?#]\S*)?$%iu';
if ( preg_match( $pattern, trim( $url ) ) ) {
return true;
}
return false;
}
/**
* Verify that an email is valid.
* See the linked RFC.
*
* @see https://www.rfc-editor.org/rfc/inline-errata/rfc3696.html
*
* @since 1.7.3
*
* @param string $email Email address to verify.
*
* @return string|false Returns a valid email address on success, false on failure.
*/
function wpforms_is_email( $email ) { // phpcs:ignore Generic.Metrics.CyclomaticComplexity.TooHigh
static $punycode;
// Do not allow callables, arrays and objects.
if ( ! is_scalar( $email ) ) {
return false;
}
// Allow smart tags in the email address.
if ( preg_match( '/{.+?}/', $email ) ) {
return $email;
}
// Email can't be longer than 254 octets,
// otherwise it can't be used to send an email address (limitation in the MAIL and RCPT commands).
// 1 octet = 8 bits = 1 byte.
if ( strlen( $email ) > 254 ) {
return false;
}
$email_arr = explode( '@', $email );
if ( count( $email_arr ) !== 2 ) {
return false;
}
list( $local, $domain ) = $email_arr;
/**
* RFC requires local part to be no longer than 64 octets.
* Punycode library checks for 63 octets.
*
* @link https://github.com/true/php-punycode/blob/master/src/Punycode.php#L182.
*/
if ( strlen( $local ) > 63 ) {
return false;
}
$domain_arr = explode( '.', $domain );
foreach ( $domain_arr as $domain_label ) {
$domain_label = trim( $domain_label );
if ( ! $domain_label ) {
return false;
}
// The RFC says: 'A DNS label may be no more than 63 octets long'.
if ( strlen( $domain_label ) > 63 ) {
return false;
}
}
if ( ! $punycode ) {
$punycode = new Punycode();
}
/**
* The wp_mail() uses phpMailer, which uses is_email() as verification callback.
* For verification, phpMailer sends the email address where the domain part is punycode encoded only.
* We follow here the same principle.
*/
$email_check = $local . '@' . $punycode->encode( $domain );
// Other limitations are checked by the native WordPress function is_email().
return is_email( $email_check ) ? $local . '@' . $domain : false;
}
/**
* Check whether the string is json-encoded.
*
* @since 1.7.5
*
* @param string $string A string.
*
* @return bool
*/
function wpforms_is_json( $string ) {
return (
is_string( $string ) &&
is_array( json_decode( $string, true ) ) &&
json_last_error() === JSON_ERROR_NONE
);
}
/**
* Check whether the current page is in AMP mode or not.
* We need to check for specific functions, as there is no special AMP header.
*
* @since 1.4.1
*
* @param bool $check_theme_support Whether theme support should be checked. Defaults to true.
*
* @return bool
*/
function wpforms_is_amp( $check_theme_support = true ) {
$is_amp = false;
if (
// AMP by Automattic; ampforwp.
( function_exists( 'is_amp_endpoint' ) && is_amp_endpoint() ) ||
// Better AMP.
( function_exists( 'is_better_amp' ) && is_better_amp() )
) {
$is_amp = true;
}
if ( $is_amp && $check_theme_support ) {
$is_amp = current_theme_supports( 'amp' );
}
return apply_filters( 'wpforms_is_amp', $is_amp );
}
/**
* Helper function to determine if loading on WPForms related admin page.
*
* Here we determine if the current administration page is owned/created by
* WPForms. This is done in compliance with WordPress best practices for
* development, so that we only load required WPForms CSS and JS files on pages
* we create. As a result we do not load our assets admin wide, where they might
* conflict with other plugins needlessly, also leading to a better, faster user
* experience for our users.
*
* @since 1.3.9
*
* @param string $slug Slug identifier for a specific WPForms admin page.
* @param string $view Slug identifier for a specific WPForms admin page view ("subpage").
*
* @return bool
*/
function wpforms_is_admin_page( $slug = '', $view = '' ) {
// phpcs:disable WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.ValidatedSanitizedInput.MissingUnslash
// Check against basic requirements.
if (
! is_admin() ||
empty( $_REQUEST['page'] ) ||
strpos( $_REQUEST['page'], 'wpforms' ) === false
) {
return false;
}
// Check against page slug identifier.
if (
( ! empty( $slug ) && $_REQUEST['page'] !== 'wpforms-' . $slug ) ||
( empty( $slug ) && $_REQUEST['page'] === 'wpforms-builder' )
) {
return false;
}
// Check against sub-level page view.
if (
! empty( $view ) &&
( empty( $_REQUEST['view'] ) || $_REQUEST['view'] !== $view )
) {
return false;
}
// phpcs:enable WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.ValidatedSanitizedInput.MissingUnslash
return true;
}
/**
* Check if a string is empty.
*
* @since 1.5.0
*
* @param string $string String to test.
*
* @return bool
*/
function wpforms_is_empty_string( $string ) {
return is_string( $string ) && $string === '';
}
/**
* Determine if the request is WPForms AJAX.
*
* @since 1.8.0
*
* @return bool
*/
function wpforms_is_ajax() {
if ( ! wp_doing_ajax() ) {
return false;
}
// Make sure the request targets admin-ajax.php.
if ( isset( $_SERVER['SCRIPT_FILENAME'] ) && basename( sanitize_text_field( wp_unslash( $_SERVER['SCRIPT_FILENAME'] ) ) ) !== 'admin-ajax.php' ) {
return false;
}
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
$action = isset( $_REQUEST['action'] ) ? sanitize_key( $_REQUEST['action'] ) : '';
return strpos( $action, 'wpforms_' ) === 0;
}
/**
* Determine if request is frontend AJAX.
*
* @since 1.5.8.2
* @since 1.6.5 Added filterable frontend ajax actions list as a fallback to missing referer cases.
* @since 1.6.7.1 Removed a requirement for an AJAX action to be a WPForms action if referer is not missing.
* @since 1.8.0 Added clear separation between frontend and admin AJAX requests, see `wpforms_is_admin_ajax()`.
*
* @return bool
*/
function wpforms_is_frontend_ajax() {
if ( wpforms_is_ajax() && ! wpforms_is_admin_ajax() ) {
return true;
}
// Try detecting a frontend AJAX call indirectly by comparing the current action
// with a known frontend actions list in case there's no HTTP referer.
$ref = wp_get_raw_referer();
if ( $ref ) {
return false;
}
$frontend_actions = [
'wpforms_submit',
'wpforms_file_upload_speed_test',
'wpforms_upload_chunk_init',
'wpforms_upload_chunk',
'wpforms_file_chunks_uploaded',
'wpforms_remove_file',
'wpforms_restricted_email',
'wpforms_form_locker_unique_answer',
'wpforms_form_abandonment',
];
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
$action = isset( $_REQUEST['action'] ) ? sanitize_key( $_REQUEST['action'] ) : '';
/**
* Allow modifying the list of frontend AJAX actions.
*
* This filter may be running as early as `plugins_loaded` hook.
* Please mind the hooks order when using it.
*
* @since 1.6.5
*
* @param array $frontend_actions A list of frontend actions.
*/
$frontend_actions = (array) apply_filters( 'wpforms_is_frontend_ajax_frontend_actions', $frontend_actions );
return in_array( $action, $frontend_actions, true );
}
/**
* Determine if request is admin AJAX.
*
* @since 1.8.0
*
* @return bool
*/
function wpforms_is_admin_ajax() {
if ( ! wpforms_is_ajax() ) {
return false;
}
$ref = wp_get_raw_referer();
if ( ! $ref ) {
return false;
}
$path = wp_parse_url( $ref, PHP_URL_PATH );
$admin_path = wp_parse_url( admin_url(), PHP_URL_PATH );
// Is an admin AJAX call if HTTP referer contain an admin path.
return strpos( $path, $admin_path ) !== false;
}
/**
* Check if Gutenberg is active.
*
* @since 1.6.2
*
* @return bool True if Gutenberg is active.
*/
function wpforms_is_gutenberg_active() {
$gutenberg = false;
$block_editor = false;
if ( has_filter( 'replace_editor', 'gutenberg_init' ) ) {
// Gutenberg is installed and activated.
$gutenberg = true;
}
if ( version_compare( $GLOBALS['wp_version'], '5.0-beta', '>' ) ) {
// Block editor.
$block_editor = true;
}
if ( ! $gutenberg && ! $block_editor ) {
return false;
}
include_once ABSPATH . 'wp-admin/includes/plugin.php';
if ( is_plugin_active( 'disable-gutenberg/disable-gutenberg.php' ) ) {
return ! disable_gutenberg();
}
if ( is_plugin_active( 'classic-editor/classic-editor.php' ) ) {
return get_option( 'classic-editor-replace' ) === 'block';
}
return true;
}
/**
* Determines whether the current request is a WP CLI request.
*
* @since 1.7.6
*
* @return bool
*/
function wpforms_doing_wp_cli() {
return defined( 'WP_CLI' ) && WP_CLI;
}
/**
* Determines whether search functionality is enabled for Choices.js elements in the admin area.
*
* @since 1.8.3
*
* @param array $data Data to be displayed in the dropdown.
*
* @return string
*/
function wpforms_choices_js_is_search_enabled( $data ) {
/**
* Filter max amount of items at which no search box is displayed.
*
* @since 1.8.3
*
* @param int $count Max items count.
*/
return count( $data ) >= apply_filters( 'wpforms_choices_js_is_search_enabled_max_limit', 20 ) ? 'true' : 'false';
}

View File

@@ -0,0 +1,165 @@
<?php
/**
* Helper functions to work with colors.
*
* @since 1.8.0
*/
/**
* Detect if we should use a light or dark color based on the color given.
*
* @link https://docs.woocommerce.com/wc-apidocs/source-function-wc_light_or_dark.html#608-627
*
* @since 1.2.5
*
* @param mixed $color Color value.
* @param string $dark Dark color value (default: '#000000').
* @param string $light Light color value (default: '#FFFFFF').
*
* @return string
*/
function wpforms_light_or_dark( $color, $dark = '#000000', $light = '#FFFFFF' ) {
$hex = str_replace( '#', '', $color );
$c_r = hexdec( substr( $hex, 0, 2 ) );
$c_g = hexdec( substr( $hex, 2, 2 ) );
$c_b = hexdec( substr( $hex, 4, 2 ) );
$brightness = ( ( $c_r * 299 ) + ( $c_g * 587 ) + ( $c_b * 114 ) ) / 1000;
return $brightness > 155 ? $dark : $light;
}
/**
* Convert hex color value to RGB.
*
* @since 1.7.9
* @since 1.8.5 New param and return type were added.
*
* @param string $hex Color value in hex format.
* @param bool $as_string Whether to return the RGB value as a string or array.
*
* @return string|array Color value in RGB format.
*/
function wpforms_hex_to_rgb( $hex, $as_string = true ) {
$hex = ltrim( $hex, '#' );
// Convert shorthand colors to full format, e.g. "FFF" -> "FFFFFF".
$rgb_parts = preg_replace( '~^(.)(.)(.)$~', '$1$1$2$2$3$3', $hex );
$rgb = [];
$rgb['R'] = hexdec( $rgb_parts[0] . $rgb_parts[1] );
$rgb['G'] = hexdec( $rgb_parts[2] . $rgb_parts[3] );
$rgb['B'] = hexdec( $rgb_parts[4] . $rgb_parts[5] );
// Return the RGB value as a string.
if ( $as_string ) {
return sprintf(
'%1$d, %2$d, %3$d',
$rgb['R'],
$rgb['G'],
$rgb['B']
);
}
return $rgb; // This is an array.
}
/**
* Get a lighter color hex value.
*
* @since 1.8.5
*
* @param string $color Color hex value.
* @param int $factor Factor to lighten the color.
*
* @return string Lighter color hex value.
*/
function wpforms_hex_lighter( $color, $factor = 30 ) {
$base = wpforms_hex_to_rgb( $color, false );
// Leave if we can't get the RGB values.
if ( empty( $base ) || count( $base ) !== 3 ) {
return '';
}
$hex = '#';
foreach ( $base as $channel ) {
$amount = 255 - $channel;
$amount = $amount / 100;
$amount = round( floatval( $amount * $factor ) );
$new_decimal = $channel + $amount;
$new_hex_component = dechex( $new_decimal );
if ( strlen( $new_hex_component ) < 2 ) {
$new_hex_component = '0' . $new_hex_component;
}
$hex .= $new_hex_component;
}
return $hex;
}
/**
* Get a darker color hex value.
*
* @since 1.8.5
*
* @param string $color Color hex value.
* @param int $factor Factor to darken the color.
*
* @return string Darker color hex value.
*/
function wpforms_hex_darker( $color, $factor = 30 ) {
$base = wpforms_hex_to_rgb( $color, false );
// Leave if we can't get the RGB values.
if ( empty( $base ) || count( $base ) !== 3 ) {
return '';
}
$hex = '#';
foreach ( $base as $channel ) {
$amount = $channel / 100;
$amount = round( floatval( $amount * $factor ) );
$new_decimal = $channel - $amount;
$new_hex_component = dechex( $new_decimal );
if ( strlen( $new_hex_component ) < 2 ) {
$new_hex_component = '0' . $new_hex_component;
}
$hex .= $new_hex_component;
}
return $hex;
}
/**
* Generate a contrasting color based on the given color.
*
* This function calculates a contrasting color to ensure readability based on the provided color.
*
* @since 1.8.5
*
* @param string $color The original color value. Color hex value.
* @param int $light_factor The factor to lighten the color.
* @param int $dark_factor The factor to darken the color.
*
* @return string The contrasting color value.
*/
function wpforms_generate_contrasting_color( $color, $light_factor = 30, $dark_factor = 30 ) {
$is_dark = wpforms_light_or_dark( $color, 'light', 'dark' ) === 'dark';
return $is_dark ? wpforms_hex_lighter( $color, $light_factor ) : wpforms_hex_darker( $color, $dark_factor );
}

View File

@@ -0,0 +1,441 @@
<?php
/**
* Helper functions to get data presets.
*
* @since 1.8.0
*/
/**
* US States.
*
* @since 1.0.0
*
* @return array
*/
function wpforms_us_states() {
$states = [
'AL' => esc_html__( 'Alabama', 'wpforms-lite' ),
'AK' => esc_html__( 'Alaska', 'wpforms-lite' ),
'AZ' => esc_html__( 'Arizona', 'wpforms-lite' ),
'AR' => esc_html__( 'Arkansas', 'wpforms-lite' ),
'CA' => esc_html__( 'California', 'wpforms-lite' ),
'CO' => esc_html__( 'Colorado', 'wpforms-lite' ),
'CT' => esc_html__( 'Connecticut', 'wpforms-lite' ),
'DE' => esc_html__( 'Delaware', 'wpforms-lite' ),
'DC' => esc_html__( 'District of Columbia', 'wpforms-lite' ),
'FL' => esc_html__( 'Florida', 'wpforms-lite' ),
'GA' => esc_html_x( 'Georgia', 'US State', 'wpforms-lite' ),
'HI' => esc_html__( 'Hawaii', 'wpforms-lite' ),
'ID' => esc_html__( 'Idaho', 'wpforms-lite' ),
'IL' => esc_html__( 'Illinois', 'wpforms-lite' ),
'IN' => esc_html__( 'Indiana', 'wpforms-lite' ),
'IA' => esc_html__( 'Iowa', 'wpforms-lite' ),
'KS' => esc_html__( 'Kansas', 'wpforms-lite' ),
'KY' => esc_html__( 'Kentucky', 'wpforms-lite' ),
'LA' => esc_html__( 'Louisiana', 'wpforms-lite' ),
'ME' => esc_html__( 'Maine', 'wpforms-lite' ),
'MD' => esc_html__( 'Maryland', 'wpforms-lite' ),
'MA' => esc_html__( 'Massachusetts', 'wpforms-lite' ),
'MI' => esc_html__( 'Michigan', 'wpforms-lite' ),
'MN' => esc_html__( 'Minnesota', 'wpforms-lite' ),
'MS' => esc_html__( 'Mississippi', 'wpforms-lite' ),
'MO' => esc_html__( 'Missouri', 'wpforms-lite' ),
'MT' => esc_html__( 'Montana', 'wpforms-lite' ),
'NE' => esc_html__( 'Nebraska', 'wpforms-lite' ),
'NV' => esc_html__( 'Nevada', 'wpforms-lite' ),
'NH' => esc_html__( 'New Hampshire', 'wpforms-lite' ),
'NJ' => esc_html__( 'New Jersey', 'wpforms-lite' ),
'NM' => esc_html__( 'New Mexico', 'wpforms-lite' ),
'NY' => esc_html__( 'New York', 'wpforms-lite' ),
'NC' => esc_html__( 'North Carolina', 'wpforms-lite' ),
'ND' => esc_html__( 'North Dakota', 'wpforms-lite' ),
'OH' => esc_html__( 'Ohio', 'wpforms-lite' ),
'OK' => esc_html__( 'Oklahoma', 'wpforms-lite' ),
'OR' => esc_html__( 'Oregon', 'wpforms-lite' ),
'PA' => esc_html__( 'Pennsylvania', 'wpforms-lite' ),
'RI' => esc_html__( 'Rhode Island', 'wpforms-lite' ),
'SC' => esc_html__( 'South Carolina', 'wpforms-lite' ),
'SD' => esc_html__( 'South Dakota', 'wpforms-lite' ),
'TN' => esc_html__( 'Tennessee', 'wpforms-lite' ),
'TX' => esc_html__( 'Texas', 'wpforms-lite' ),
'UT' => esc_html__( 'Utah', 'wpforms-lite' ),
'VT' => esc_html__( 'Vermont', 'wpforms-lite' ),
'VA' => esc_html__( 'Virginia', 'wpforms-lite' ),
'WA' => esc_html__( 'Washington', 'wpforms-lite' ),
'WV' => esc_html__( 'West Virginia', 'wpforms-lite' ),
'WI' => esc_html__( 'Wisconsin', 'wpforms-lite' ),
'WY' => esc_html__( 'Wyoming', 'wpforms-lite' ),
];
return apply_filters( 'wpforms_us_states', $states );
}
/**
* Countries.
*
* @since 1.0.0
*
* @return array
*/
function wpforms_countries() {
$countries = [
'AF' => esc_html__( 'Afghanistan', 'wpforms-lite' ),
'AX' => esc_html__( 'Åland Islands', 'wpforms-lite' ),
'AL' => esc_html__( 'Albania', 'wpforms-lite' ),
'DZ' => esc_html__( 'Algeria', 'wpforms-lite' ),
'AS' => esc_html__( 'American Samoa', 'wpforms-lite' ),
'AD' => esc_html__( 'Andorra', 'wpforms-lite' ),
'AO' => esc_html__( 'Angola', 'wpforms-lite' ),
'AI' => esc_html__( 'Anguilla', 'wpforms-lite' ),
'AQ' => esc_html__( 'Antarctica', 'wpforms-lite' ),
'AG' => esc_html__( 'Antigua and Barbuda', 'wpforms-lite' ),
'AR' => esc_html__( 'Argentina', 'wpforms-lite' ),
'AM' => esc_html__( 'Armenia', 'wpforms-lite' ),
'AW' => esc_html__( 'Aruba', 'wpforms-lite' ),
'AU' => esc_html__( 'Australia', 'wpforms-lite' ),
'AT' => esc_html__( 'Austria', 'wpforms-lite' ),
'AZ' => esc_html__( 'Azerbaijan', 'wpforms-lite' ),
'BS' => esc_html__( 'Bahamas', 'wpforms-lite' ),
'BH' => esc_html__( 'Bahrain', 'wpforms-lite' ),
'BD' => esc_html__( 'Bangladesh', 'wpforms-lite' ),
'BB' => esc_html__( 'Barbados', 'wpforms-lite' ),
'BY' => esc_html__( 'Belarus', 'wpforms-lite' ),
'BE' => esc_html__( 'Belgium', 'wpforms-lite' ),
'BZ' => esc_html__( 'Belize', 'wpforms-lite' ),
'BJ' => esc_html__( 'Benin', 'wpforms-lite' ),
'BM' => esc_html__( 'Bermuda', 'wpforms-lite' ),
'BT' => esc_html__( 'Bhutan', 'wpforms-lite' ),
'BO' => esc_html__( 'Bolivia (Plurinational State of)', 'wpforms-lite' ),
'BQ' => esc_html__( 'Bonaire, Saint Eustatius and Saba', 'wpforms-lite' ),
'BA' => esc_html__( 'Bosnia and Herzegovina', 'wpforms-lite' ),
'BW' => esc_html__( 'Botswana', 'wpforms-lite' ),
'BV' => esc_html__( 'Bouvet Island', 'wpforms-lite' ),
'BR' => esc_html__( 'Brazil', 'wpforms-lite' ),
'IO' => esc_html__( 'British Indian Ocean Territory', 'wpforms-lite' ),
'BN' => esc_html__( 'Brunei Darussalam', 'wpforms-lite' ),
'BG' => esc_html__( 'Bulgaria', 'wpforms-lite' ),
'BF' => esc_html__( 'Burkina Faso', 'wpforms-lite' ),
'BI' => esc_html__( 'Burundi', 'wpforms-lite' ),
'CV' => esc_html__( 'Cabo Verde', 'wpforms-lite' ),
'KH' => esc_html__( 'Cambodia', 'wpforms-lite' ),
'CM' => esc_html__( 'Cameroon', 'wpforms-lite' ),
'CA' => esc_html__( 'Canada', 'wpforms-lite' ),
'KY' => esc_html__( 'Cayman Islands', 'wpforms-lite' ),
'CF' => esc_html__( 'Central African Republic', 'wpforms-lite' ),
'TD' => esc_html__( 'Chad', 'wpforms-lite' ),
'CL' => esc_html__( 'Chile', 'wpforms-lite' ),
'CN' => esc_html__( 'China', 'wpforms-lite' ),
'CX' => esc_html__( 'Christmas Island', 'wpforms-lite' ),
'CC' => esc_html__( 'Cocos (Keeling) Islands', 'wpforms-lite' ),
'CO' => esc_html__( 'Colombia', 'wpforms-lite' ),
'KM' => esc_html__( 'Comoros', 'wpforms-lite' ),
'CG' => esc_html__( 'Congo', 'wpforms-lite' ),
'CD' => esc_html__( 'Congo (Democratic Republic of the)', 'wpforms-lite' ),
'CK' => esc_html__( 'Cook Islands', 'wpforms-lite' ),
'CR' => esc_html__( 'Costa Rica', 'wpforms-lite' ),
'CI' => esc_html__( 'Côte d\'Ivoire', 'wpforms-lite' ),
'HR' => esc_html__( 'Croatia', 'wpforms-lite' ),
'CU' => esc_html__( 'Cuba', 'wpforms-lite' ),
'CW' => esc_html__( 'Curaçao', 'wpforms-lite' ),
'CY' => esc_html__( 'Cyprus', 'wpforms-lite' ),
'CZ' => esc_html__( 'Czech Republic', 'wpforms-lite' ),
'DK' => esc_html__( 'Denmark', 'wpforms-lite' ),
'DJ' => esc_html__( 'Djibouti', 'wpforms-lite' ),
'DM' => esc_html__( 'Dominica', 'wpforms-lite' ),
'DO' => esc_html__( 'Dominican Republic', 'wpforms-lite' ),
'EC' => esc_html__( 'Ecuador', 'wpforms-lite' ),
'EG' => esc_html__( 'Egypt', 'wpforms-lite' ),
'SV' => esc_html__( 'El Salvador', 'wpforms-lite' ),
'GQ' => esc_html__( 'Equatorial Guinea', 'wpforms-lite' ),
'ER' => esc_html__( 'Eritrea', 'wpforms-lite' ),
'EE' => esc_html__( 'Estonia', 'wpforms-lite' ),
'ET' => esc_html__( 'Ethiopia', 'wpforms-lite' ),
'FK' => esc_html__( 'Falkland Islands (Malvinas)', 'wpforms-lite' ),
'FO' => esc_html__( 'Faroe Islands', 'wpforms-lite' ),
'FJ' => esc_html__( 'Fiji', 'wpforms-lite' ),
'FI' => esc_html__( 'Finland', 'wpforms-lite' ),
'FR' => esc_html__( 'France', 'wpforms-lite' ),
'GF' => esc_html__( 'French Guiana', 'wpforms-lite' ),
'PF' => esc_html__( 'French Polynesia', 'wpforms-lite' ),
'TF' => esc_html__( 'French Southern Territories', 'wpforms-lite' ),
'GA' => esc_html__( 'Gabon', 'wpforms-lite' ),
'GM' => esc_html__( 'Gambia', 'wpforms-lite' ),
'GE' => esc_html_x( 'Georgia', 'Country', 'wpforms-lite' ),
'DE' => esc_html__( 'Germany', 'wpforms-lite' ),
'GH' => esc_html__( 'Ghana', 'wpforms-lite' ),
'GI' => esc_html__( 'Gibraltar', 'wpforms-lite' ),
'GR' => esc_html__( 'Greece', 'wpforms-lite' ),
'GL' => esc_html__( 'Greenland', 'wpforms-lite' ),
'GD' => esc_html__( 'Grenada', 'wpforms-lite' ),
'GP' => esc_html__( 'Guadeloupe', 'wpforms-lite' ),
'GU' => esc_html__( 'Guam', 'wpforms-lite' ),
'GT' => esc_html__( 'Guatemala', 'wpforms-lite' ),
'GG' => esc_html__( 'Guernsey', 'wpforms-lite' ),
'GN' => esc_html__( 'Guinea', 'wpforms-lite' ),
'GW' => esc_html__( 'Guinea-Bissau', 'wpforms-lite' ),
'GY' => esc_html__( 'Guyana', 'wpforms-lite' ),
'HT' => esc_html__( 'Haiti', 'wpforms-lite' ),
'HM' => esc_html__( 'Heard Island and McDonald Islands', 'wpforms-lite' ),
'HN' => esc_html__( 'Honduras', 'wpforms-lite' ),
'HK' => esc_html__( 'Hong Kong', 'wpforms-lite' ),
'HU' => esc_html__( 'Hungary', 'wpforms-lite' ),
'IS' => esc_html__( 'Iceland', 'wpforms-lite' ),
'IN' => esc_html__( 'India', 'wpforms-lite' ),
'ID' => esc_html__( 'Indonesia', 'wpforms-lite' ),
'IR' => esc_html__( 'Iran (Islamic Republic of)', 'wpforms-lite' ),
'IQ' => esc_html__( 'Iraq', 'wpforms-lite' ),
'IE' => esc_html__( 'Ireland (Republic of)', 'wpforms-lite' ),
'IM' => esc_html__( 'Isle of Man', 'wpforms-lite' ),
'IL' => esc_html__( 'Israel', 'wpforms-lite' ),
'IT' => esc_html__( 'Italy', 'wpforms-lite' ),
'JM' => esc_html__( 'Jamaica', 'wpforms-lite' ),
'JP' => esc_html__( 'Japan', 'wpforms-lite' ),
'JE' => esc_html__( 'Jersey', 'wpforms-lite' ),
'JO' => esc_html__( 'Jordan', 'wpforms-lite' ),
'KZ' => esc_html__( 'Kazakhstan', 'wpforms-lite' ),
'KE' => esc_html__( 'Kenya', 'wpforms-lite' ),
'KI' => esc_html__( 'Kiribati', 'wpforms-lite' ),
'KP' => esc_html__( 'Korea (Democratic People\'s Republic of)', 'wpforms-lite' ),
'KR' => esc_html__( 'Korea (Republic of)', 'wpforms-lite' ),
'XK' => esc_html__( 'Kosovo', 'wpforms-lite' ),
'KW' => esc_html__( 'Kuwait', 'wpforms-lite' ),
'KG' => esc_html__( 'Kyrgyzstan', 'wpforms-lite' ),
'LA' => esc_html__( 'Lao People\'s Democratic Republic', 'wpforms-lite' ),
'LV' => esc_html__( 'Latvia', 'wpforms-lite' ),
'LB' => esc_html__( 'Lebanon', 'wpforms-lite' ),
'LS' => esc_html__( 'Lesotho', 'wpforms-lite' ),
'LR' => esc_html__( 'Liberia', 'wpforms-lite' ),
'LY' => esc_html__( 'Libya', 'wpforms-lite' ),
'LI' => esc_html__( 'Liechtenstein', 'wpforms-lite' ),
'LT' => esc_html__( 'Lithuania', 'wpforms-lite' ),
'LU' => esc_html__( 'Luxembourg', 'wpforms-lite' ),
'MO' => esc_html__( 'Macao', 'wpforms-lite' ),
'MK' => esc_html__( 'North Macedonia (Republic of)', 'wpforms-lite' ),
'MG' => esc_html__( 'Madagascar', 'wpforms-lite' ),
'MW' => esc_html__( 'Malawi', 'wpforms-lite' ),
'MY' => esc_html__( 'Malaysia', 'wpforms-lite' ),
'MV' => esc_html__( 'Maldives', 'wpforms-lite' ),
'ML' => esc_html__( 'Mali', 'wpforms-lite' ),
'MT' => esc_html__( 'Malta', 'wpforms-lite' ),
'MH' => esc_html__( 'Marshall Islands', 'wpforms-lite' ),
'MQ' => esc_html__( 'Martinique', 'wpforms-lite' ),
'MR' => esc_html__( 'Mauritania', 'wpforms-lite' ),
'MU' => esc_html__( 'Mauritius', 'wpforms-lite' ),
'YT' => esc_html__( 'Mayotte', 'wpforms-lite' ),
'MX' => esc_html__( 'Mexico', 'wpforms-lite' ),
'FM' => esc_html__( 'Micronesia (Federated States of)', 'wpforms-lite' ),
'MD' => esc_html__( 'Moldova (Republic of)', 'wpforms-lite' ),
'MC' => esc_html__( 'Monaco', 'wpforms-lite' ),
'MN' => esc_html__( 'Mongolia', 'wpforms-lite' ),
'ME' => esc_html__( 'Montenegro', 'wpforms-lite' ),
'MS' => esc_html__( 'Montserrat', 'wpforms-lite' ),
'MA' => esc_html__( 'Morocco', 'wpforms-lite' ),
'MZ' => esc_html__( 'Mozambique', 'wpforms-lite' ),
'MM' => esc_html__( 'Myanmar', 'wpforms-lite' ),
'NA' => esc_html__( 'Namibia', 'wpforms-lite' ),
'NR' => esc_html__( 'Nauru', 'wpforms-lite' ),
'NP' => esc_html__( 'Nepal', 'wpforms-lite' ),
'NL' => esc_html__( 'Netherlands', 'wpforms-lite' ),
'NC' => esc_html__( 'New Caledonia', 'wpforms-lite' ),
'NZ' => esc_html__( 'New Zealand', 'wpforms-lite' ),
'NI' => esc_html__( 'Nicaragua', 'wpforms-lite' ),
'NE' => esc_html__( 'Niger', 'wpforms-lite' ),
'NG' => esc_html__( 'Nigeria', 'wpforms-lite' ),
'NU' => esc_html__( 'Niue', 'wpforms-lite' ),
'NF' => esc_html__( 'Norfolk Island', 'wpforms-lite' ),
'MP' => esc_html__( 'Northern Mariana Islands', 'wpforms-lite' ),
'NO' => esc_html__( 'Norway', 'wpforms-lite' ),
'OM' => esc_html__( 'Oman', 'wpforms-lite' ),
'PK' => esc_html__( 'Pakistan', 'wpforms-lite' ),
'PW' => esc_html__( 'Palau', 'wpforms-lite' ),
'PS' => esc_html__( 'Palestine (State of)', 'wpforms-lite' ),
'PA' => esc_html__( 'Panama', 'wpforms-lite' ),
'PG' => esc_html__( 'Papua New Guinea', 'wpforms-lite' ),
'PY' => esc_html__( 'Paraguay', 'wpforms-lite' ),
'PE' => esc_html__( 'Peru', 'wpforms-lite' ),
'PH' => esc_html__( 'Philippines', 'wpforms-lite' ),
'PN' => esc_html__( 'Pitcairn', 'wpforms-lite' ),
'PL' => esc_html__( 'Poland', 'wpforms-lite' ),
'PT' => esc_html__( 'Portugal', 'wpforms-lite' ),
'PR' => esc_html__( 'Puerto Rico', 'wpforms-lite' ),
'QA' => esc_html__( 'Qatar', 'wpforms-lite' ),
'RE' => esc_html__( 'Réunion', 'wpforms-lite' ),
'RO' => esc_html__( 'Romania', 'wpforms-lite' ),
'RU' => esc_html__( 'Russian Federation', 'wpforms-lite' ),
'RW' => esc_html__( 'Rwanda', 'wpforms-lite' ),
'BL' => esc_html__( 'Saint Barthélemy', 'wpforms-lite' ),
'SH' => esc_html__( 'Saint Helena, Ascension and Tristan da Cunha', 'wpforms-lite' ),
'KN' => esc_html__( 'Saint Kitts and Nevis', 'wpforms-lite' ),
'LC' => esc_html__( 'Saint Lucia', 'wpforms-lite' ),
'MF' => esc_html__( 'Saint Martin (French part)', 'wpforms-lite' ),
'PM' => esc_html__( 'Saint Pierre and Miquelon', 'wpforms-lite' ),
'VC' => esc_html__( 'Saint Vincent and the Grenadines', 'wpforms-lite' ),
'WS' => esc_html__( 'Samoa', 'wpforms-lite' ),
'SM' => esc_html__( 'San Marino', 'wpforms-lite' ),
'ST' => esc_html__( 'Sao Tome and Principe', 'wpforms-lite' ),
'SA' => esc_html__( 'Saudi Arabia', 'wpforms-lite' ),
'SN' => esc_html__( 'Senegal', 'wpforms-lite' ),
'RS' => esc_html__( 'Serbia', 'wpforms-lite' ),
'SC' => esc_html__( 'Seychelles', 'wpforms-lite' ),
'SL' => esc_html__( 'Sierra Leone', 'wpforms-lite' ),
'SG' => esc_html__( 'Singapore', 'wpforms-lite' ),
'SX' => esc_html__( 'Sint Maarten (Dutch part)', 'wpforms-lite' ),
'SK' => esc_html__( 'Slovakia', 'wpforms-lite' ),
'SI' => esc_html__( 'Slovenia', 'wpforms-lite' ),
'SB' => esc_html__( 'Solomon Islands', 'wpforms-lite' ),
'SO' => esc_html__( 'Somalia', 'wpforms-lite' ),
'ZA' => esc_html__( 'South Africa', 'wpforms-lite' ),
'GS' => esc_html__( 'South Georgia and the South Sandwich Islands', 'wpforms-lite' ),
'SS' => esc_html__( 'South Sudan', 'wpforms-lite' ),
'ES' => esc_html__( 'Spain', 'wpforms-lite' ),
'LK' => esc_html__( 'Sri Lanka', 'wpforms-lite' ),
'SD' => esc_html__( 'Sudan', 'wpforms-lite' ),
'SR' => esc_html__( 'Suriname', 'wpforms-lite' ),
'SJ' => esc_html__( 'Svalbard and Jan Mayen', 'wpforms-lite' ),
'SZ' => esc_html__( 'Eswatini (Kingdom of)', 'wpforms-lite' ),
'SE' => esc_html__( 'Sweden', 'wpforms-lite' ),
'CH' => esc_html__( 'Switzerland', 'wpforms-lite' ),
'SY' => esc_html__( 'Syrian Arab Republic', 'wpforms-lite' ),
'TW' => esc_html__( 'Taiwan, Republic of China', 'wpforms-lite' ),
'TJ' => esc_html__( 'Tajikistan', 'wpforms-lite' ),
'TZ' => esc_html__( 'Tanzania (United Republic of)', 'wpforms-lite' ),
'TH' => esc_html__( 'Thailand', 'wpforms-lite' ),
'TL' => esc_html__( 'Timor-Leste', 'wpforms-lite' ),
'TG' => esc_html__( 'Togo', 'wpforms-lite' ),
'TK' => esc_html__( 'Tokelau', 'wpforms-lite' ),
'TO' => esc_html__( 'Tonga', 'wpforms-lite' ),
'TT' => esc_html__( 'Trinidad and Tobago', 'wpforms-lite' ),
'TN' => esc_html__( 'Tunisia', 'wpforms-lite' ),
'TR' => esc_html__( 'Türkiye', 'wpforms-lite' ),
'TM' => esc_html__( 'Turkmenistan', 'wpforms-lite' ),
'TC' => esc_html__( 'Turks and Caicos Islands', 'wpforms-lite' ),
'TV' => esc_html__( 'Tuvalu', 'wpforms-lite' ),
'UG' => esc_html__( 'Uganda', 'wpforms-lite' ),
'UA' => esc_html__( 'Ukraine', 'wpforms-lite' ),
'AE' => esc_html__( 'United Arab Emirates', 'wpforms-lite' ),
'GB' => esc_html__( 'United Kingdom of Great Britain and Northern Ireland', 'wpforms-lite' ),
'US' => esc_html__( 'United States of America', 'wpforms-lite' ),
'UM' => esc_html__( 'United States Minor Outlying Islands', 'wpforms-lite' ),
'UY' => esc_html__( 'Uruguay', 'wpforms-lite' ),
'UZ' => esc_html__( 'Uzbekistan', 'wpforms-lite' ),
'VU' => esc_html__( 'Vanuatu', 'wpforms-lite' ),
'VA' => esc_html__( 'Vatican City State', 'wpforms-lite' ),
'VE' => esc_html__( 'Venezuela (Bolivarian Republic of)', 'wpforms-lite' ),
'VN' => esc_html__( 'Vietnam', 'wpforms-lite' ),
'VG' => esc_html__( 'Virgin Islands (British)', 'wpforms-lite' ),
'VI' => esc_html__( 'Virgin Islands (U.S.)', 'wpforms-lite' ),
'WF' => esc_html__( 'Wallis and Futuna', 'wpforms-lite' ),
'EH' => esc_html__( 'Western Sahara', 'wpforms-lite' ),
'YE' => esc_html__( 'Yemen', 'wpforms-lite' ),
'ZM' => esc_html__( 'Zambia', 'wpforms-lite' ),
'ZW' => esc_html__( 'Zimbabwe', 'wpforms-lite' ),
];
return apply_filters( 'wpforms_countries', $countries );
}
/**
* Calendar Months.
*
* @since 1.3.7
*
* @return array
*/
function wpforms_months() {
$months = [
'Jan' => esc_html__( 'January', 'wpforms-lite' ),
'Feb' => esc_html__( 'February', 'wpforms-lite' ),
'Mar' => esc_html__( 'March', 'wpforms-lite' ),
'Apr' => esc_html__( 'April', 'wpforms-lite' ),
'May' => esc_html__( 'May', 'wpforms-lite' ),
'Jun' => esc_html__( 'June', 'wpforms-lite' ),
'Jul' => esc_html__( 'July', 'wpforms-lite' ),
'Aug' => esc_html__( 'August', 'wpforms-lite' ),
'Sep' => esc_html__( 'September', 'wpforms-lite' ),
'Oct' => esc_html__( 'October', 'wpforms-lite' ),
'Nov' => esc_html__( 'November', 'wpforms-lite' ),
'Dec' => esc_html__( 'December', 'wpforms-lite' ),
];
return apply_filters( 'wpforms_months', $months );
}
/**
* Calendar Days.
*
* @since 1.3.7
*
* @return array
*/
function wpforms_days() {
$days = [
'Sun' => esc_html__( 'Sunday', 'wpforms-lite' ),
'Mon' => esc_html__( 'Monday', 'wpforms-lite' ),
'Tue' => esc_html__( 'Tuesday', 'wpforms-lite' ),
'Wed' => esc_html__( 'Wednesday', 'wpforms-lite' ),
'Thu' => esc_html__( 'Thursday', 'wpforms-lite' ),
'Fri' => esc_html__( 'Friday', 'wpforms-lite' ),
'Sat' => esc_html__( 'Saturday', 'wpforms-lite' ),
];
return apply_filters( 'wpforms_days', $days );
}
/**
* Return available date formats.
*
* @since 1.7.5
*
* @return array
*/
function wpforms_date_formats() {
/**
* Filters available date formats.
*
* @since 1.3.0
*
* @param array $date_formats Default date formats.
* Item key is JS date character - see https://flatpickr.js.org/formatting/
* Item value is in PHP format - see http://php.net/manual/en/function.date.php.
*/
return (array) apply_filters(
'wpforms_datetime_date_formats',
[
'm/d/Y' => 'm/d/Y',
'd/m/Y' => 'd/m/Y',
'F j, Y' => 'F j, Y',
]
);
}
/**
* Return available time formats.
*
* @since 1.7.7
*
* @return array
*/
function wpforms_time_formats() {
/**
* Filters available time formats.
*
* @since 1.5.9
*
* @param array $time_formats Default time formats.
* Item key is in PHP format which it used in jquery.timepicker as well,
* see http://php.net/manual/en/function.date.php.
*/
return (array) apply_filters(
'wpforms_datetime_time_formats',
[
'g:i A' => '12 H',
'H:i' => '24 H',
]
);
}

View File

@@ -0,0 +1,151 @@
<?php
/**
* Helper functions to work with dates, time and timezones.
*
* @since 1.8.0
*/
/**
* Return date and time formatted as expected.
*
* @since 1.6.3
*
* @param string|int $date Date to format.
* @param string $format Optional. Format for the date and time.
* @param bool $gmt_offset Optional. GTM offset.
*
* @return string
*/
function wpforms_datetime_format( $date, $format = '', $gmt_offset = false ) {
if ( is_numeric( $date ) ) {
$date = (int) $date;
}
if ( is_string( $date ) ) {
$date = strtotime( $date );
}
if ( $gmt_offset ) {
$date += (int) ( get_option( 'gmt_offset' ) * HOUR_IN_SECONDS );
}
if ( $format === '' ) {
return sprintf( /* translators: %1$s - formatted date, %2$s - formatted time. */
__( '%1$s at %2$s', 'wpforms-lite' ),
date_i18n( get_option( 'date_format' ), $date ),
date_i18n( get_option( 'time_format' ), $date )
);
}
return date_i18n( $format, $date );
}
/**
* Return date formatted as expected.
*
* @since 1.6.3
*
* @param string|int $date Date to format.
* @param string $format Optional. Format for the date.
* @param bool $gmt_offset Optional. GTM offset.
*
* @return string
*/
function wpforms_date_format( $date, $format = '', $gmt_offset = false ) {
if ( $format === '' ) {
$format = (string) get_option( 'date_format', 'M j, Y' );
}
return wpforms_datetime_format( $date, $format, $gmt_offset );
}
/**
* Return time formatted as expected.
*
* @since 1.8.5
*
* @param string|int $date Date to format.
* @param string $format Optional. Format for the time.
* @param bool $gmt_offset Optional. GTM offset.
*
* @return string
*/
function wpforms_time_format( $date, $format = '', $gmt_offset = false ) {
if ( $format === '' ) {
$format = (string) get_option( 'time_format', 'g:ia' );
}
return wpforms_datetime_format( $date, $format, $gmt_offset );
}
/**
* Get the certain date of a specified day in a specified format.
*
* @since 1.4.4
* @since 1.6.3 Added $use_gmt_offset parameter.
*
* @param string $period Supported values: start, end.
* @param string $timestamp Default is the current timestamp, if left empty.
* @param string $format Default is a MySQL format.
* @param bool $use_gmt_offset Use GTM offset.
*
* @return string
*/
function wpforms_get_day_period_date( $period, $timestamp = '', $format = 'Y-m-d H:i:s', $use_gmt_offset = false ) {
$date = '';
if ( empty( $timestamp ) ) {
$timestamp = time();
}
$offset_sec = $use_gmt_offset ? get_option( 'gmt_offset' ) * 3600 : 0;
switch ( $period ) {
case 'start_of_day':
$date = gmdate( $format, strtotime( 'today', $timestamp ) - $offset_sec );
break;
case 'end_of_day':
$date = gmdate( $format, strtotime( 'tomorrow', $timestamp ) - 1 - $offset_sec );
break;
}
return $date;
}
/**
* Retrieve a timezone from the site settings as a `DateTimeZone` object.
*
* Timezone can be based on a PHP timezone string or a ±HH:MM offset.
*
* @since 1.6.6
*
* @return DateTimeZone Timezone object.
*/
function wpforms_get_timezone() {
if ( function_exists( 'wp_timezone' ) ) {
return wp_timezone();
}
// Fallback for WordPress version < 5.3.
$timezone_string = get_option( 'timezone_string' );
if ( ! $timezone_string ) {
$offset = (float) get_option( 'gmt_offset' );
$hours = (int) $offset;
$minutes = ( $offset - $hours );
$sign = ( $offset < 0 ) ? '-' : '+';
$abs_hour = abs( $hours );
$abs_mins = abs( $minutes * 60 );
$timezone_string = sprintf( '%s%02d:%02d', $sign, $abs_hour, $abs_mins );
}
return timezone_open( $timezone_string );
}

View File

@@ -0,0 +1,219 @@
<?php
/**
* Helper logging and debug functions.
*
* @since 1.8.0
*/
use WPForms\Logger\Log;
/**
* Check whether plugin works in a debug mode.
*
* @since 1.2.3
*
* @return bool
*/
function wpforms_debug() {
$debug = false;
if ( ( defined( 'WPFORMS_DEBUG' ) && true === WPFORMS_DEBUG ) && is_super_admin() ) {
$debug = true;
}
return apply_filters( 'wpforms_debug', $debug );
}
/**
* Helper function to display debug data.
*
* @since 1.0.0
*
* @param mixed $data What to dump, can be any type.
* @param bool $echo Whether to print or return. Default is to print.
*
* @return string|void
*/
function wpforms_debug_data( $data, $echo = true ) {
if ( ! wpforms_debug() ) {
return;
}
if ( is_array( $data ) || is_object( $data ) ) {
// phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_print_r
$data = print_r( $data, true );
}
$output = sprintf(
'<style>
.wpforms-debug {
line-height: 0;
}
.wpforms-debug textarea {
background: #f6f7f7 !important;
margin: 20px 0 0 0;
width: 100%%;
height: 500px;
font-size: 12px;
font-family: Consolas, Menlo, Monaco, monospace;
direction: ltr;
unicode-bidi: embed;
line-height: 1.4;
padding: 10px;
border-radius: 0;
border-color: #c3c4c7;
box-sizing: border-box;
}
.postbox .wpforms-debug {
padding-top: 12px;
}
.postbox .wpforms-debug:first-of-type {
padding-top: 6px;
}
.postbox .wpforms-debug textarea {
margin-top: 0 !important;
}
</style>
<div class="wpforms-debug">
<textarea readonly>=================== WPFORMS DEBUG ===================%s</textarea>
</div>',
"\n\n" . esc_html( $data )
);
/**
* Allow developers to determine whether the debug data should be displayed.
* Works only in debug mode (`WPFORMS_DEBUG` constant is `true`).
*
* @since 1.6.8
*
* @param bool $allow_display True by default.
*/
$allow_display = apply_filters( 'wpforms_debug_data_allow_display', true );
if ( $echo && $allow_display ) {
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
echo $output;
} else {
return $output;
}
}
/**
* Log helper.
*
* @since 1.0.0
*
* @param string $title Title of a log message.
* @param mixed $message Content of a log message.
* @param array $args Expected keys: form_id, meta, parent.
*/
function wpforms_log( $title = '', $message = '', $args = [] ) {
// Skip if logs disabled in Tools -> Logs.
if ( ! wpforms_setting( 'logs-enable', false ) ) {
return;
}
// Require log title.
if ( empty( $title ) ) {
return;
}
/**
* Compare error levels to determine if we should log.
* Current supported levels:
* - Conditional Logic (conditional_logic)
* - Entries (entry)
* - Errors (error)
* - Payments (payment)
* - Providers (provider)
* - Security (security)
* - Spam (spam)
* - Log (log)
*/
$types = ! empty( $args['type'] ) ? (array) $args['type'] : [ 'error' ];
// Skip invalid logs types.
$log_types = Log::get_log_types();
foreach ( $types as $key => $type ) {
if ( ! isset( $log_types[ $type ] ) ) {
unset( $types[ $key ] );
}
}
if ( empty( $types ) ) {
return;
}
/**
* Filter log message.
*
* @since 1.8.2
*
* @param mixed $message Log message.
* @param string $title Log title.
* @param array $args Log arguments.
*/
$message = apply_filters( 'wpforms_log_message', $message, $title, $args );
// Make arrays and objects look nice.
if ( is_array( $message ) || is_object( $message ) ) {
$message = '<pre>' . print_r( $message, true ) . '</pre>'; // phpcs:ignore
}
// Filter logs types from Tools -> Logs page.
$logs_types = wpforms_setting( 'logs-types', false );
if ( $logs_types && empty( array_intersect( $logs_types, $types ) ) ) {
return;
}
// Filter user roles from Tools -> Logs page.
$current_user = function_exists( 'wp_get_current_user' ) ? wp_get_current_user() : null;
$current_user_id = $current_user ? $current_user->ID : 0;
$current_user_roles = $current_user ? $current_user->roles : [];
$logs_user_roles = wpforms_setting( 'logs-user-roles', false );
if ( $logs_user_roles && empty( array_intersect( $logs_user_roles, $current_user_roles ) ) ) {
return;
}
// Filter logs users from Tools -> Logs page.
$logs_users = wpforms_setting( 'logs-users', false );
if ( $logs_users && ! in_array( $current_user_id, $logs_users, true ) ) {
return;
}
$log = wpforms()->get( 'log' );
if ( ! method_exists( $log, 'add' ) ) {
return;
}
// Create log entry.
$log->add(
$title,
$message,
$types,
isset( $args['form_id'] ) ? absint( $args['form_id'] ) : 0,
isset( $args['parent'] ) ? absint( $args['parent'] ) : 0,
$current_user_id
);
}
/**
* Wrapper for set_time_limit to see if it is enabled.
*
* @since 1.6.4
*
* @param int $limit Time limit.
*/
function wpforms_set_time_limit( $limit = 0 ) {
if ( function_exists( 'set_time_limit' ) && false === strpos( ini_get( 'disable_functions' ), 'set_time_limit' ) && ! ini_get( 'safe_mode' ) ) { // phpcs:ignore PHPCompatibility.IniDirectives.RemovedIniDirectives.safe_modeDeprecatedRemoved
@set_time_limit( $limit ); // @codingStandardsIgnoreLine
}
}

View File

@@ -0,0 +1,74 @@
<?php
/**
* Helpers functions for the Education pages.
*
* @since 1.8.2.2
*/
/**
* Get the button.
*
* @since 1.8.2.2
*
* @param string $action Action to perform.
* @param bool $plugin_allow Is plugin allowed.
* @param string $path Plugin file.
* @param string $url URL for download plugin.
* @param array $utm UTM parameters.
*/
function wpforms_edu_get_button( $action, $plugin_allow, $path, $url, $utm ) {
// If the user is not allowed to use the plugin, show the upgrade button.
if ( ! $plugin_allow ) {
wpforms_edu_get_upgrade_button( $utm );
return;
}
$status = 'inactive';
$data_plugin = $path;
$title = esc_html__( 'Activate', 'wpforms-lite' );
if ( $action === 'install' ) {
$status = 'download';
$data_plugin = $url;
$title = esc_html__( 'Install & Activate', 'wpforms-lite' );
}
?>
<button
class="status-<?php echo esc_attr( $status ); ?> wpforms-btn wpforms-btn-lg wpforms-btn-blue wpforms-education-toggle-plugin-btn"
data-type="addon"
data-action="<?php echo esc_attr( $action ); ?>"
data-plugin="<?php echo esc_attr( $data_plugin ); ?>">
<i></i><?php echo esc_html( $title ); ?>
<?php
}
/**
* Get the upgrade button.
*
* @since 1.8.2.2
*
* @param array $utm UTM parameters.
* @param array $classes Classes.
*/
function wpforms_edu_get_upgrade_button( $utm, $classes = [] ) {
$utm_medium = isset( $utm['medium'] ) ? $utm['medium'] : '';
$utm_content = isset( $utm['content'] ) ? $utm['content'] : '';
$default_classes = [ 'wpforms-btn', 'wpforms-btn-lg', 'wpforms-btn-orange' ];
$default_classes[] = ! wpforms()->is_pro() ? 'wpforms-upgrade-modal' : '';
$btn_classes = array_merge( $default_classes, (array) $classes );
?>
<a
href="<?php echo esc_url( wpforms_admin_upgrade_link( $utm_medium, $utm_content ) ); ?>"
target="_blank"
rel="noopener noreferrer"
class="<?php echo esc_attr( implode( ' ', array_filter( $btn_classes ) ) ); ?>">
<?php esc_html_e( 'Upgrade to WPForms Pro', 'wpforms-lite' ); ?>
</a>
<?php
}

View File

@@ -0,0 +1,485 @@
<?php
/**
* Helper functions to clean and sanitize data, escape it and prepare the output.
*
* @since 1.8.0
*/
use WPForms\Helpers\Templates;
/**
* Decode special characters, both alpha- (<) and numeric-based (').
* Sanitize recursively, preserve new lines.
* Handle all the possible mixed variations of < and `&lt;` that can be processed into tags.
*
* @since 1.4.1
* @since 1.6.0 Sanitize recursively, preserve new lines.
*
* @param string $string Raw string to decode.
*
* @return string
*/
function wpforms_decode_string( $string ) {
if ( ! is_string( $string ) ) {
return $string;
}
/*
* Sanitization should be done first, so tags are stripped and < is converted to &lt; etc.
* This iteration may do nothing when the string already comes with &lt; and &gt; only.
*/
$string = wpforms_sanitize_text_deeply( $string, true );
// Now we need to convert the string without tags: &lt; back to < (same for quotes).
$string = wp_kses_decode_entities( html_entity_decode( $string, ENT_QUOTES ) );
// And now we need to sanitize AGAIN, to avoid unwanted tags that appeared after decoding.
return wpforms_sanitize_text_deeply( $string, true );
}
/**
* Sanitize key, primarily used for looking up options.
*
* @since 1.3.9
*
* @param string $key Key name.
*
* @return string
*/
function wpforms_sanitize_key( $key = '' ) {
return preg_replace( '/[^a-zA-Z0-9_\-\.\:\/]/', '', $key );
}
/**
* Sanitize hex color.
*
* @since 1.2.1
*
* @param string $color Color value.
*
* @return string
*/
function wpforms_sanitize_hex_color( $color ) {
if ( empty( $color ) ) {
return '';
}
// 3 or 6 hex digits, or the empty string.
if ( preg_match( '|^#([A-Fa-f0-9]{3}){1,2}$|', $color ) ) {
return $color;
}
return '';
}
/**
* Sanitize error message, primarily used during form frontend output.
*
* @since 1.3.7
* @since 1.7.6 Expand list of allowed HTML tags and attributes.
*
* @param string $error Error message.
*
* @return string
*/
function wpforms_sanitize_error( $error = '' ) {
$allow = [
'a' => [
'href' => [],
'title' => [],
'target' => [],
'rel' => [],
],
'br' => [],
'em' => [],
'strong' => [],
'del' => [],
'p' => [
'style' => [],
],
'blockquote' => [],
'ul' => [],
'ol' => [],
'li' => [],
'span' => [
'style' => [],
],
];
return wp_kses( $error, $allow );
}
/**
* Sanitize a string, that can be a multiline.
*
* @uses wpforms_sanitize_text_deeply()
*
* @since 1.4.1
*
* @param string $string String to deeply sanitize.
*
* @return string Sanitized string, or empty string if not a string provided.
*/
function wpforms_sanitize_textarea_field( $string ) {
return wpforms_sanitize_text_deeply( $string, true );
}
/**
* Deeply sanitize the string, preserve newlines if needed.
* Prevent maliciously prepared strings from containing HTML tags.
*
* @since 1.6.0
*
* @param string $string String to deeply sanitize.
* @param bool $keep_newlines Whether to keep newlines. Default: false.
*
* @return string Sanitized string, or empty string if not a string provided.
*/
function wpforms_sanitize_text_deeply( $string, $keep_newlines = false ) {
if ( is_object( $string ) || is_array( $string ) ) {
return '';
}
$string = (string) $string;
$keep_newlines = (bool) $keep_newlines;
$new_value = _sanitize_text_fields( $string, $keep_newlines );
if ( strlen( $new_value ) !== strlen( $string ) ) {
$new_value = wpforms_sanitize_text_deeply( $new_value, $keep_newlines );
}
return $new_value;
}
/**
* Sanitize an HTML string with a set of allowed HTML tags.
*
* @since 1.7.0
*
* @param string $value String to sanitize.
*
* @return string Sanitized string.
*/
function wpforms_sanitize_richtext_field( $value ) {
$count = 1;
$value = convert_invalid_entities( $value );
// Remove 'script' and 'style' tags recursively.
while ( $count ) {
$value = preg_replace( '@<(script|style)[^>]*?>.*?</\\1>@si', '', $value, - 1, $count );
}
// Make sure we have allowed tags only.
$value = wp_kses( $value, wpforms_get_allowed_html_tags_for_richtext_field() );
// Make sure that all tags are balanced.
return force_balance_tags( $value );
}
/**
* Escaping for Rich Text field values.
*
* @since 1.7.0
*
* @param string $value Text to escape.
*
* @return string Escaped text.
*/
function wpforms_esc_richtext_field( $value ) {
return wpautop( wpforms_sanitize_richtext_field( $value ) );
}
/**
* Retrieve allowed HTML tags for Rich Text field.
*
* @since 1.7.0
*
* @return array Array of allowed tags.
*/
function wpforms_get_allowed_html_tags_for_richtext_field() {
$allowed_tags = array_fill_keys(
[
'img',
'h1',
'h2',
'h3',
'h4',
'h5',
'h6',
'p',
'a',
'ul',
'ol',
'li',
'dl',
'dt',
'dd',
'hr',
'br',
'code',
'pre',
'strong',
'b',
'em',
'i',
'blockquote',
'cite',
'q',
'del',
'span',
'small',
'table',
'thead',
'tbody',
'th',
'tr',
'td',
'abbr',
'address',
'sub',
'sup',
'ins',
'figure',
'figcaption',
'div',
],
array_fill_keys(
[ 'align', 'class', 'id', 'style', 'src', 'rel', 'alt', 'href', 'target', 'width', 'height', 'title', 'cite', 'start', 'reversed', 'datetime' ],
[]
)
);
/**
* Allowed HTML tags for Rich Text field.
*
* @since 1.7.0
*
* @param array $allowed_tags Allowed HTML tags.
*/
$tags = (array) apply_filters( 'wpforms_get_allowed_html_tags_for_richtext_field', $allowed_tags );
// Force unset iframes, script and style no matter when we get back
// from apply_filters, as they are a huge security risk.
unset( $tags['iframe'], $tags['script'], $tags['style'] );
return $tags;
}
/**
* Sanitize an array, that consists of values as strings.
* After that - merge all array values into multiline string.
*
* @since 1.4.1
*
* @param array $array Data to sanitize.
*
* @return mixed If not an array is passed (or empty var) - return unmodified var. Otherwise - a merged array into multiline string.
*/
function wpforms_sanitize_array_combine( $array ) {
if ( empty( $array ) || ! is_array( $array ) ) {
return $array;
}
return implode( "\n", array_map( 'sanitize_text_field', $array ) );
}
/**
* Format, sanitize, and return/echo HTML element ID, classes, attributes,
* and data attributes.
*
* @since 1.3.7
*
* @param string $id HTML id attribute value.
* @param array $class A list of classnames for the class attribute.
* @param array $datas Data attributes.
* @param array $atts Any additional HTML attributes and their values.
* @param bool $echo Whether to echo the output or just return it. Defaults to return.
*
* @return string|void
*/
function wpforms_html_attributes( $id = '', $class = [], $datas = [], $atts = [], $echo = false ) {
$id = trim( $id );
$parts = [];
if ( ! empty( $id ) ) {
$id = sanitize_html_class( $id );
if ( ! empty( $id ) ) {
$parts[] = 'id="' . $id . '"';
}
}
if ( ! empty( $class ) ) {
$class = wpforms_sanitize_classes( $class, true );
if ( ! empty( $class ) ) {
$parts[] = 'class="' . $class . '"';
}
}
if ( ! empty( $datas ) ) {
foreach ( $datas as $data => $val ) {
$parts[] = 'data-' . sanitize_html_class( $data ) . '="' . esc_attr( $val ) . '"';
}
}
if ( ! empty( $atts ) ) {
foreach ( $atts as $att => $val ) {
if ( '0' === (string) $val || ! empty( $val ) ) {
if ( $att[0] === '[' ) {
// Handle special case for bound attributes in AMP.
$escaped_att = '[' . sanitize_html_class( trim( $att, '[]' ) ) . ']';
} else {
$escaped_att = sanitize_html_class( $att );
}
$parts[] = $escaped_att . '="' . esc_attr( $val ) . '"';
}
}
}
$output = implode( ' ', $parts );
if ( $echo ) {
echo trim( $output ); // phpcs:ignore
} else {
return trim( $output );
}
}
/**
* Sanitize string of CSS classes.
*
* @since 1.2.1
*
* @param array|string $classes CSS classes.
* @param bool $convert True will convert strings to array and vice versa.
*
* @return string|array
*/
function wpforms_sanitize_classes( $classes, $convert = false ) {
$array = is_array( $classes );
$css = [];
if ( ! empty( $classes ) ) {
if ( ! $array ) {
$classes = explode( ' ', trim( $classes ) );
}
foreach ( array_unique( $classes ) as $class ) {
if ( ! empty( $class ) ) {
$css[] = sanitize_html_class( $class );
}
}
}
if ( $array ) {
return $convert ? implode( ' ', $css ) : $css;
}
return $convert ? $css : implode( ' ', $css );
}
/**
* Include a template - alias to \WPForms\Helpers\Template::get_html.
* Use 'require' if $args are passed or 'load_template' if not.
*
* @since 1.5.6
*
* @param string $template_name Template name.
* @param array $args Arguments.
* @param bool $extract Extract arguments.
*
* @throws RuntimeException If extract() tries to modify the scope.
*
* @return string Compiled HTML.
*/
function wpforms_render( $template_name, $args = [], $extract = false ) {
return Templates::get_html( $template_name, $args, $extract );
}
/**
* Alias for default readonly function.
*
* @since 1.6.9
*
* @param mixed $readonly One of the values to compare.
* @param mixed $current The other value to compare if not just true.
* @param bool $echo Whether to echo or just return the string.
*
* @return string HTML attribute or empty string.
*/
function wpforms_readonly( $readonly, $current = true, $echo = true ) {
if ( function_exists( 'wp_readonly' ) ) {
return wp_readonly( $readonly, $current, $echo );
}
return __checked_selected_helper( $readonly, $current, $echo, 'readonly' );
}
/**
* Get the required label text, with a filter.
*
* @since 1.4.4
*
* @return string
*/
function wpforms_get_required_label() {
return apply_filters( 'wpforms_required_label', esc_html__( 'This field is required.', 'wpforms-lite' ) );
}
/**
* Get the required field label HTML, with a filter.
*
* @since 1.4.8
*
* @return string
*/
function wpforms_get_field_required_label() {
$label_html = apply_filters_deprecated(
'wpforms_field_required_label',
[ ' <span class="wpforms-required-label">*</span>' ],
'1.4.8 of the WPForms plugin',
'wpforms_get_field_required_label'
);
return apply_filters( 'wpforms_get_field_required_label', $label_html );
}
/**
* Escape unselected choices for radio/checkbox fields.
*
* @since 1.8.3
*
* @param string $formatted_field HTML field.
*
* @return string
*/
function wpforms_esc_unselected_choices( $formatted_field ) {
$allowed_html = wp_kses_allowed_html( 'post' );
$allowed_html['input'] = [
'type' => [],
'disabled' => [],
'checked' => [],
];
$allowed_html['label'] = [];
return wp_kses( $formatted_field, $allowed_html );
}

View File

@@ -0,0 +1,224 @@
<?php
/**
* Helper functions to work with filesystem, uploads and media files.
*
* @since 1.8.0
*/
/**
* Get WPForms upload root path (e.g. /wp-content/uploads/wpforms).
*
* As of 1.7.0, you can pass in your own value that matches the output of wp_upload_dir()
* in order to use this function inside of a filter without infinite looping.
*
* @since 1.6.1
*
* @return array WPForms upload root path (no trailing slash).
*/
function wpforms_upload_dir() {
$upload_dir = wp_upload_dir();
if ( ! empty( $upload_dir['error'] ) ) {
return [ 'error' => $upload_dir['error'] ];
}
$basedir = wp_is_stream( $upload_dir['basedir'] ) ? $upload_dir['basedir'] : realpath( $upload_dir['basedir'] );
$wpforms_upload_root = trailingslashit( $basedir ) . 'wpforms';
/**
* Allow developers to change a directory where cache and uploaded files will be stored.
*
* @since 1.5.2
*
* @param string $wpforms_upload_root WPForms upload root directory.
*/
$custom_uploads_root = apply_filters( 'wpforms_upload_root', $wpforms_upload_root );
if ( is_dir( $custom_uploads_root ) && wp_is_writable( $custom_uploads_root ) ) {
$wpforms_upload_root = wp_is_stream( $custom_uploads_root )
? $custom_uploads_root
: realpath( $custom_uploads_root );
}
return [
'path' => $wpforms_upload_root,
'url' => trailingslashit( $upload_dir['baseurl'] ) . 'wpforms',
'error' => false,
];
}
/**
* Create index.html file in the specified directory if it doesn't exist.
*
* @since 1.6.1
*
* @param string $path Path to the directory.
*
* @return int|false Number of bytes that were written to the file, or false on failure.
*/
function wpforms_create_index_html_file( $path ) {
if ( ! is_dir( $path ) || is_link( $path ) ) {
return false;
}
$index_file = wp_normalize_path( trailingslashit( $path ) . 'index.html' );
// Do nothing if index.html exists in the directory.
if ( file_exists( $index_file ) ) {
return false;
}
// Create empty index.html.
return file_put_contents( $index_file, '' ); // phpcs:ignore WordPress.WP.AlternativeFunctions
}
/**
* Create .htaccess file in the WPForms upload directory.
*
* @since 1.6.1
*
* @return bool True when the .htaccess file exists, false on failure.
*/
function wpforms_create_upload_dir_htaccess_file() {
if ( ! apply_filters( 'wpforms_create_upload_dir_htaccess_file', true ) ) {
return false;
}
$upload_dir = wpforms_upload_dir();
if ( ! empty( $upload_dir['error'] ) ) {
return false;
}
$htaccess_file = wp_normalize_path( trailingslashit( $upload_dir['path'] ) . '.htaccess' );
$cache_key = 'wpforms_htaccess_file';
if ( is_file( $htaccess_file ) ) {
$cached_stat = get_transient( $cache_key );
$stat = array_intersect_key(
stat( $htaccess_file ),
[
'size' => 0,
'mtime' => 0,
'ctime' => 0,
]
);
if ( $cached_stat === $stat ) {
return true;
}
@unlink( $htaccess_file ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged
}
if ( ! function_exists( 'insert_with_markers' ) ) {
require_once ABSPATH . 'wp-admin/includes/misc.php';
}
$contents = apply_filters(
'wpforms_create_upload_dir_htaccess_file_content',
'# Disable PHP and Python scripts parsing.
<Files *>
SetHandler none
SetHandler default-handler
RemoveHandler .cgi .php .php3 .php4 .php5 .phtml .pl .py .pyc .pyo
RemoveType .cgi .php .php3 .php4 .php5 .phtml .pl .py .pyc .pyo
</Files>
<IfModule mod_php5.c>
php_flag engine off
</IfModule>
<IfModule mod_php7.c>
php_flag engine off
</IfModule>
<IfModule mod_php8.c>
php_flag engine off
</IfModule>
<IfModule headers_module>
Header set X-Robots-Tag "noindex"
</IfModule>'
);
$created = insert_with_markers( $htaccess_file, 'WPForms', $contents );
if ( $created ) {
clearstatcache( true, $htaccess_file );
$stat = array_intersect_key(
stat( $htaccess_file ),
[
'size' => 0,
'mtime' => 0,
'ctime' => 0,
]
);
set_transient( $cache_key, $stat );
}
return $created;
}
/**
* Convert a file size provided, such as "2M", to bytes.
*
* @link http://stackoverflow.com/a/22500394
*
* @since 1.0.0
*
* @param string $size File size.
*
* @return int
*/
function wpforms_size_to_bytes( $size ) {
if ( is_numeric( $size ) ) {
return $size;
}
$suffix = substr( $size, - 1 );
$value = substr( $size, 0, - 1 );
switch ( strtoupper( $suffix ) ) {
case 'P':
$value *= 1024;
case 'T':
$value *= 1024;
case 'G':
$value *= 1024;
case 'M':
$value *= 1024;
case 'K':
$value *= 1024;
break;
}
return $value;
}
/**
* Convert a file size provided, such as "2M", to bytes.
*
* @link http://stackoverflow.com/a/22500394
*
* @since 1.0.0
*
* @param bool $bytes Whether the value should be in bytes or formatted.
*
* @return false|string|int
*/
function wpforms_max_upload( $bytes = false ) {
$max = wp_max_upload_size();
if ( $bytes ) {
return $max;
}
return size_format( $max );
}

View File

@@ -0,0 +1,505 @@
<?php
/**
* Helper functions to work with form fields, generic and specific to certain field types.
*
* @since 1.8.0
*/
/**
* Determine if we should show the "Show Values" toggle for checkbox, radio, or
* select fields in form builder. Legacy.
*
* @since 1.5.0
*
* @return bool
*/
function wpforms_show_fields_options_setting() {
return apply_filters( 'wpforms_fields_show_options_setting', false );
}
/**
* Return field choice properties for field configured with dynamic choices.
*
* @since 1.4.5
*
* @param array $field Field settings.
* @param int $form_id Form ID.
* @param array $form_data Form data and settings.
*
* @return false|array
*/
function wpforms_get_field_dynamic_choices( $field, $form_id, $form_data = [] ) {
if ( empty( $field['dynamic_choices'] ) ) {
return false;
}
$choices = [];
if ( $field['dynamic_choices'] === 'post_type' ) {
if ( empty( $field['dynamic_post_type'] ) ) {
return false;
}
$posts = wpforms_get_hierarchical_object(
apply_filters(
'wpforms_dynamic_choice_post_type_args',
[
'post_type' => $field['dynamic_post_type'],
'posts_per_page' => -1,
'orderby' => 'title',
'order' => 'ASC',
],
$field,
$form_id
),
true
);
foreach ( $posts as $post ) {
$choices[] = [
'value' => $post->ID,
'label' => wpforms_get_post_title( $post ),
'depth' => isset( $post->depth ) ? absint( $post->depth ) : 1,
];
}
} elseif ( $field['dynamic_choices'] === 'taxonomy' ) {
if ( empty( $field['dynamic_taxonomy'] ) ) {
return false;
}
$terms = wpforms_get_hierarchical_object(
apply_filters(
'wpforms_dynamic_choice_taxonomy_args',
[
'taxonomy' => $field['dynamic_taxonomy'],
'hide_empty' => false,
],
$field,
$form_data
),
true
);
foreach ( $terms as $term ) {
$choices[] = [
'value' => $term->term_id,
'label' => wpforms_get_term_name( $term ),
'depth' => isset( $term->depth ) ? absint( $term->depth ) : 1,
];
}
}
return $choices;
}
/**
* Build and return either a taxonomy or post type object that is
* nested to accommodate any hierarchy.
*
* @since 1.3.9
* @since 1.5.0 Return array only. Empty array of no data.
*
* @param array $args Object arguments to pass to data retrieval function.
* @param bool $flat Preserve hierarchy or not. False by default - preserve it.
*
* @return array
*/
function wpforms_get_hierarchical_object( $args = [], $flat = false ) { // phpcs:ignore Generic.Metrics.CyclomaticComplexity.MaxExceeded
if ( empty( $args['taxonomy'] ) && empty( $args['post_type'] ) ) {
return [];
}
$children = [];
$parents = [];
$ref_parent = '';
$ref_name = '';
$number = 0;
if ( ! empty( $args['post_type'] ) ) {
$defaults = [
'posts_per_page' => - 1,
'orderby' => 'title',
'order' => 'ASC',
];
$args = wp_parse_args( $args, $defaults );
$items = get_posts( $args );
$ref_parent = 'post_parent';
$ref_id = 'ID';
$ref_name = 'post_title';
$number = ! empty( $args['posts_per_page'] ) ? $args['posts_per_page'] : 0;
} elseif ( ! empty( $args['taxonomy'] ) ) {
$defaults = [
'hide_empty' => false,
'orderby' => 'name',
'order' => 'ASC',
];
$args = wp_parse_args( $args, $defaults );
$items = get_terms( $args );
$ref_parent = 'parent';
$ref_id = 'term_id';
$ref_name = 'name';
$number = ! empty( $args['number'] ) ? $args['number'] : 0;
}
if ( empty( $items ) || is_wp_error( $items ) ) {
return [];
}
foreach ( $items as $item ) {
if ( $item->{$ref_parent} ) {
$children[ $item->{$ref_id} ] = $item;
$children[ $item->{$ref_id} ]->ID = (int) $item->{$ref_id};
} else {
$parents[ $item->{$ref_id} ] = $item;
$parents[ $item->{$ref_id} ]->ID = (int) $item->{$ref_id};
}
}
$children_count = count( $children );
$is_limited = $number > 1;
// We can't guarantee that all children have a parent if there is a limit in the request.
// Hence, we have to make sure that there is a parent for every child.
if ( $is_limited && $children_count ) {
foreach ( $children as $child ) {
// The current WP_Post or WP_Term object to operate on.
$current = $child;
// The current object's parent is already in the list of parents or children.
if ( ! empty( $parents[ $child->{$ref_parent} ] ) || ! empty( $children[ $child->{$ref_parent} ] ) ) {
continue;
}
do {
// Set the current object to the previous iteration's parent object.
$current = ! empty( $args['post_type'] ) ? get_post( $current->{$ref_parent} ) : get_term( $current->{$ref_parent} );
if ( $current->{$ref_parent} === 0 ) {
// We've reached the top of the hierarchy.
$parents[ $current->{$ref_id} ] = $current;
$parents[ $current->{$ref_id} ]->ID = (int) $current->{$ref_id};
} else {
// We're still in the middle of the hierarchy.
$children[ $current->{$ref_id} ] = $current;
$children[ $current->{$ref_id} ]->ID = (int) $current->{$ref_id};
}
} while ( $current->{$ref_parent} > 0 );
}
}
while ( $children_count >= 1 ) {
foreach ( $children as $child ) {
_wpforms_get_hierarchical_object_search( $child, $parents, $children, $ref_parent );
// $children is modified by reference, so we need to recount to make sure we met the limits.
$children_count = count( $children );
}
}
// Sort nested child objects alphabetically using natural order, applies only
// to ordering by entry title or term name.
if ( in_array( $args['orderby'], [ 'title', 'name' ], true ) ) {
_wpforms_sort_hierarchical_object( $parents, $args['orderby'], $args['order'] );
}
if ( $flat ) {
$parents_flat = [];
_wpforms_get_hierarchical_object_flatten( $parents, $parents_flat, $ref_name );
$parents = $parents_flat;
}
return $is_limited ? array_slice( $parents, 0, $number ) : $parents;
}
/**
* Sort a nested array of objects.
*
* @since 1.6.5
*
* @param array $objects An array of objects to sort.
* @param string $orderby The object field to order by.
* @param string $order Order direction.
*/
function _wpforms_sort_hierarchical_object( &$objects, $orderby, $order ) {
// Map WP_Query/WP_Term_Query orderby to WP_Post/WP_Term property.
$map = [
'title' => 'post_title',
'name' => 'name',
];
foreach ( $objects as $object ) {
if ( ! isset( $object->children ) ) {
continue;
}
uasort(
$object->children,
static function ( $a, $b ) use ( $map, $orderby, $order ) {
/**
* This covers most cases and works for most languages. For some e.g. European languages
* that use extended latin charset (Polish, German etc) it will sort the objects into 2
* groups base and extended, properly sorted within each group. Making it even more
* robust requires either additional PHP extensions to be installed on the server
* or using heavy (and slow) conversions and computations.
*/
return $order === 'ASC' ?
strnatcasecmp( $a->{$map[ $orderby ]}, $b->{$map[ $orderby ]} ) :
strnatcasecmp( $b->{$map[ $orderby ]}, $a->{$map[ $orderby ]} );
}
);
_wpforms_sort_hierarchical_object( $object->children, $orderby, $order );
}
}
/**
* Search a given array and find the parent of the provided object.
*
* @since 1.3.9
*
* @param object $child Current child.
* @param array $parents Parents list.
* @param array $children Children list.
* @param string $ref_parent Parent reference.
*/
function _wpforms_get_hierarchical_object_search( $child, &$parents, &$children, $ref_parent ) {
foreach ( $parents as $id => $parent ) {
if ( $parent->ID === $child->{$ref_parent} ) {
if ( empty( $parent->children ) ) {
$parents[ $id ]->children = [
$child->ID => $child,
];
} else {
$parents[ $id ]->children[ $child->ID ] = $child;
}
unset( $children[ $child->ID ] );
} elseif ( ! empty( $parent->children ) && is_array( $parent->children ) ) {
_wpforms_get_hierarchical_object_search( $child, $parent->children, $children, $ref_parent );
}
}
}
/**
* Flatten a hierarchical object.
*
* @since 1.3.9
*
* @param array $array Array to process.
* @param array $output Processed output.
* @param string $ref_name Name reference.
* @param int $level Nesting level.
*/
function _wpforms_get_hierarchical_object_flatten( $array, &$output, $ref_name = 'name', $level = 0 ) {
foreach ( $array as $key => $item ) {
$indicator = apply_filters( 'wpforms_hierarchical_object_indicator', '&mdash;' );
$item->{$ref_name} = str_repeat( $indicator, $level ) . ' ' . $item->{$ref_name};
$item->depth = $level + 1;
$output[ $item->ID ] = $item;
if ( ! empty( $item->children ) ) {
_wpforms_get_hierarchical_object_flatten( $item->children, $output, $ref_name, $level + 1 );
unset( $output[ $item->ID ]->children );
}
}
}
/**
* Get sanitized post title or "no title" placeholder.
*
* The placeholder is prepended with post ID.
*
* @since 1.7.6
*
* @param WP_Post|object $post Post object.
*
* @return string Post title.
*/
function wpforms_get_post_title( $post ) {
/* translators: %d - post ID. */
return wpforms_is_empty_string( trim( $post->post_title ) ) ? sprintf( __( '#%d (no title)', 'wpforms-lite' ), absint( $post->ID ) ) : $post->post_title;
}
/**
* Get sanitized term name or "no name" placeholder.
*
* The placeholder is prepended with term ID.
*
* @since 1.7.6
*
* @param WP_Term $term Term object.
*
* @return string Term name.
*/
function wpforms_get_term_name( $term ) {
/* translators: %d - taxonomy term ID. */
return wpforms_is_empty_string( trim( $term->name ) ) ? sprintf( __( '#%d (no name)', 'wpforms-lite' ), absint( $term->term_id ) ) : trim( $term->name );
}
/**
* Return information about pages if the form has multiple pages.
*
* @since 1.3.7
*
* @param WP_Post|array $form Form data.
*
* @return false|array Page Break details or false.
*/
function wpforms_get_pagebreak_details( $form = false ) {
if ( ! wpforms()->is_pro() ) {
return false;
}
$details = [];
$pages = 1;
if ( is_object( $form ) && ! empty( $form->post_content ) ) {
$form_data = wpforms_decode( $form->post_content );
} elseif ( is_array( $form ) ) {
$form_data = $form;
}
if ( empty( $form_data['fields'] ) ) {
return false;
}
foreach ( $form_data['fields'] as $field ) {
if ( $field['type'] !== 'pagebreak' ) {
continue;
}
if ( empty( $field['position'] ) ) {
$pages ++;
$details['total'] = $pages;
$details['pages'][] = $field;
} elseif ( $field['position'] === 'top' ) {
$details['top'] = $field;
} elseif ( $field['position'] === 'bottom' ) {
$details['bottom'] = $field;
}
}
if ( ! empty( $details ) ) {
$details['top'] = empty( $details['top'] ) ? [] : $details['top'];
$details['bottom'] = empty( $details['bottom'] ) ? [] : $details['bottom'];
$details['current'] = 1;
return $details;
}
return false;
}
/**
* Return available builder fields.
*
* @since 1.8.5
*
* @param string $group Group name.
*
* @return array
*/
function wpforms_get_builder_fields( $group = '' ) {
$fields = [
'standard' => [
'group_name' => esc_html__( 'Standard Fields', 'wpforms-lite' ),
'fields' => [],
],
'fancy' => [
'group_name' => esc_html__( 'Fancy Fields', 'wpforms-lite' ),
'fields' => [],
],
'payment' => [
'group_name' => esc_html__( 'Payment Fields', 'wpforms-lite' ),
'fields' => [],
],
];
/**
* Allows developers to modify content of the the Add Field tab.
*
* With this filter developers can add their own fields or even fields groups.
*
* @since 1.4.0
*
* @param array $fields {
* Fields data multidimensional array.
*
* @param array $standard Standard fields group.
* @param string $group_name Group name.
* @param array $fields Fields array.
*
* @param array $fancy Fancy fields group.
* @param string $group_name Group name.
* @param array $fields Fields array.
*
* @param array $payment Payment fields group.
* @param string $group_name Group name.
* @param array $fields Fields array.
* }
*/
$fields = apply_filters( 'wpforms_builder_fields_buttons', $fields ); // phpcs:ignore WPForms.Comments.ParamTagHooks.InvalidParamTagsQuantity
// If a group is not specified, return all fields.
if ( empty( $group ) ) {
return $fields;
}
// If a group is specified, return only fields from that group.
if ( isset( $fields[ $group ] ) ) {
return $fields[ $group ]['fields'];
}
return [];
}
/**
* Get payments fields.
*
* @since 1.8.5
*
* @return array
*/
function wpforms_get_payments_fields() {
// Some fields are added dynamically only when the corresponding payment add-on is active.
// However, we need to be aware of all possible payment fields, even if they are not currently available.
return [
'payment-single',
'payment-multiple',
'payment-checkbox',
'payment-select',
'payment-total',
'payment-coupon',
'credit-card', // Legacy Credit Card field.
'authorize_net',
'paypal-commerce',
'square',
'stripe-credit-card',
];
}

View File

@@ -0,0 +1,508 @@
<?php
/**
* Helper functions to work with forms and form data.
*
* @since 1.8.0
*/
/**
* Helper function to trigger displaying a form.
*
* @since 1.0.2
*
* @param mixed $form_id Form ID.
* @param bool $title Form title.
* @param bool $desc Form description.
*/
function wpforms_display( $form_id = false, $title = false, $desc = false ) {
$frontend = wpforms()->get( 'frontend' );
if ( empty( $frontend ) ) {
return;
}
$frontend->output( $form_id, $title, $desc );
}
/**
* Return URL to form preview page.
*
* @since 1.5.1
*
* @param int $form_id Form ID.
* @param bool $new_window New window flag.
*
* @return string
*/
function wpforms_get_form_preview_url( $form_id, $new_window = false ) {
$url = add_query_arg(
[
'wpforms_form_preview' => absint( $form_id ),
],
home_url()
);
if ( $new_window ) {
$url = add_query_arg(
[
'new_window' => 1,
],
$url
);
}
return $url;
}
/**
* Perform json_decode and unslash.
*
* IMPORTANT: This function decodes the result of wpforms_encode() properly only if
* wp_insert_post() or wp_update_post() were used after the data is encoded.
* Both wp_insert_post() and wp_update_post() remove excessive slashes added by wpforms_encode().
*
* Using wpforms_decode() on wpforms_encode() result directly
* (without using wp_insert_post() or wp_update_post() first) always returns null or false.
*
* @since 1.0.0
*
* @param string $data Data to decode.
*
* @return array|false|null
*/
function wpforms_decode( $data ) {
if ( ! $data || empty( $data ) ) {
return false;
}
return wp_unslash( json_decode( $data, true ) );
}
/**
* Perform json_encode and wp_slash.
*
* IMPORTANT: This function adds excessive slashes to prevent data damage
* by wp_insert_post() or wp_update_post() that use wp_unslash() on all the incoming data.
*
* Decoding the result of this function by wpforms_decode() directly
* (without using wp_insert_post() or wp_update_post() first) always returns null or false.
*
* @since 1.3.1.3
*
* @param mixed $data Data to encode.
*
* @return string|false
*/
function wpforms_encode( $data = false ) {
if ( empty( $data ) ) {
return false;
}
return wp_slash( wp_json_encode( $data ) );
}
/**
* Decode json-encoded string if it is in json format.
*
* @since 1.7.5
*
* @param string $string A string.
* @param bool $associative Decode to the associative array if true. Decode to object if false.
*
* @return array|string
*/
function wpforms_json_decode( $string, $associative = false ) {
$string = html_entity_decode( $string );
if ( ! wpforms_is_json( $string ) ) {
return $string;
}
return json_decode( $string, $associative );
}
/**
* Get the value of a specific WPForms setting.
*
* @since 1.0.0
*
* @param string $key Setting name.
* @param mixed $default Default value to return if the setting is not available.
* @param string $option Option key, defaults to `wpforms_settings` in the `wp_options` table.
*
* @return mixed
*/
function wpforms_setting( $key, $default = false, $option = 'wpforms_settings' ) {
$key = wpforms_sanitize_key( $key );
$options = get_option( $option, false );
$value = is_array( $options ) && ! empty( $options[ $key ] ) ? wp_unslash( $options[ $key ] ) : $default;
/**
* Allows plugin setting to be modified.
*
* @since 1.7.8
*
* @param mixed $value Setting value.
* @param string $key Setting key.
* @param mixed $default Setting default value.
* @param string $option Settings option name.
*/
$value = apply_filters( 'wpforms_setting', $value, $key, $default, $option );
return $value;
}
/**
* Update plugin settings option and allow it to be filterable.
*
* The purpose of this function is to save settings when the "Save Settings" button is clicked.
* If you are programmatically saving setting in the database in cases not triggered by user,
* use update_option() instead.
*
* @since 1.6.6
*
* @param array $settings A plugin settings array that is saved into options table.
*
* @return bool
*/
function wpforms_update_settings( $settings ) {
$old_settings = (array) get_option( 'wpforms_settings', [] );
/**
* Allows plugin settings to be modified before persisting in the database.
*
* @since 1.6.6
*
* @param array $settings An array of plugin settings to modify.
*/
$settings = (array) apply_filters( 'wpforms_update_settings', $settings );
$updated = update_option( 'wpforms_settings', $settings );
/**
* Fires after the plugin settings were persisted in the database.
*
* The `$updated` parameter allows to check whether the update was actually successful.
*
* @since 1.6.1
* @since 1.8.4 The `$old_settings` parameter was added.
*
* @param array $settings An array of plugin settings.
* @param bool $updated Whether an option was updated or not.
* @param array $old_settings An old array of plugin settings.
*/
do_action( 'wpforms_settings_updated', $settings, $updated, $old_settings );
return $updated;
}
/**
* Check if form provided contains the specified field type.
*
* @since 1.0.5
*
* @param array|string $type Field type or types.
* @param array|object $form Form data object.
* @param bool $multiple Whether to check multiple field types.
*
* @return bool
*/
function wpforms_has_field_type( $type, $form, $multiple = false ) {
$form_data = '';
$field = false;
$type = (array) $type;
if ( $multiple ) {
foreach ( $form as $single_form ) {
$field = wpforms_has_field_type( $type, $single_form );
if ( $field ) {
break;
}
}
return $field;
}
if ( is_object( $form ) && ! empty( $form->post_content ) ) {
$form_data = wpforms_decode( $form->post_content );
} elseif ( is_array( $form ) ) {
$form_data = $form;
}
if ( empty( $form_data['fields'] ) ) {
return false;
}
foreach ( $form_data['fields'] as $single_field ) {
if ( ! empty( $single_field['type'] ) && in_array( $single_field['type'], $type, true ) ) {
$field = true;
break;
}
}
return $field;
}
/**
* Check if form provided contains a field which a specific setting.
*
* @since 1.4.5
*
* @param string $setting Setting key.
* @param object|array $form Form data.
* @param bool $multiple Whether to check multiple settings.
*
* @return bool
*/
function wpforms_has_field_setting( $setting, $form, $multiple = false ) {
$form_data = '';
$field = false;
if ( $multiple ) {
foreach ( $form as $single_form ) {
$field = wpforms_has_field_setting( $setting, $single_form );
if ( $field ) {
break;
}
}
return $field;
}
if ( is_object( $form ) && ! empty( $form->post_content ) ) {
$form_data = wpforms_decode( $form->post_content );
} elseif ( is_array( $form ) ) {
$form_data = $form;
}
if ( empty( $form_data['fields'] ) ) {
return false;
}
foreach ( $form_data['fields'] as $single_field ) {
if ( ! empty( $single_field[ $setting ] ) ) {
$field = true;
break;
}
}
return $field;
}
/**
* Retrieve actual fields from a form.
*
* Non-posting elements such as section divider, page break, and HTML are
* automatically excluded. Optionally, a whitelist can be provided.
*
* @since 1.0.0
*
* @param mixed $form Form data.
* @param array $allowlist A list of allowed fields.
*
* @return mixed boolean false or array
*/
function wpforms_get_form_fields( $form = false, $allowlist = [] ) {
// Accept form (post) object or form ID.
if ( is_object( $form ) ) {
$form = wpforms_decode( $form->post_content );
} elseif ( is_numeric( $form ) ) {
$form = wpforms()->get( 'form' )->get(
absint( $form ),
[
'content_only' => true,
]
);
}
$allowed_form_fields = [
'address',
'checkbox',
'date-time',
'email',
'file-upload',
'gdpr-checkbox',
'hidden',
'likert_scale',
'name',
'net_promoter_score',
'number',
'number-slider',
'payment-checkbox',
'payment-multiple',
'payment-select',
'payment-single',
'payment-total',
'phone',
'radio',
'rating',
'richtext',
'select',
'signature',
'text',
'textarea',
'url',
];
/**
* Filter the list of allowed form fields.
*
* @since 1.0.0
*
* @param array $allowed_form_fields List of allowed form fields.
*/
$allowed_form_fields = apply_filters( 'wpforms_get_form_fields_allowed', $allowed_form_fields );
if ( ! is_array( $form ) || empty( $form['fields'] ) ) {
return false;
}
$allowlist = ! empty( $allowlist ) ? $allowlist : $allowed_form_fields;
$form_fields = $form['fields'];
foreach ( $form_fields as $id => $form_field ) {
if ( ! in_array( $form_field['type'], $allowlist, true ) ) {
unset( $form_fields[ $id ] );
}
}
return $form_fields;
}
/**
* Conditional logic form fields supported.
*
* @since 1.5.2
*
* @return array
*/
function wpforms_get_conditional_logic_form_fields_supported() {
$fields_supported = [
'checkbox',
'email',
'hidden',
'net_promoter_score',
'number',
'number-slider',
'payment-checkbox',
'payment-multiple',
'payment-select',
'radio',
'rating',
'richtext',
'select',
'text',
'textarea',
'url',
];
return apply_filters( 'wpforms_get_conditional_logic_form_fields_supported', $fields_supported );
}
/**
* Get meta key value for a form field.
*
* @since 1.3.1
* @since 1.5.0 More strict parameters. Always return an array.
*
* @param string $key Meta key.
* @param string $value Meta value to check against.
* @param array $form_data Form data array.
*
* @return array Empty array, when no data is found.
*/
function wpforms_get_form_fields_by_meta( $key, $value, $form_data ) {
$found = [];
if ( empty( $key ) || empty( $value ) || empty( $form_data['fields'] ) ) {
return $found;
}
foreach ( $form_data['fields'] as $id => $field ) {
if ( ! empty( $field['meta'][ $key ] ) && $value === $field['meta'][ $key ] ) {
$found[ $id ] = $field;
}
}
return $found;
}
/**
* Retrieve the full config for CAPTCHA.
*
* @since 1.6.4
*
* @return array
*/
function wpforms_get_captcha_settings() {
$allowed_captcha_list = [ 'hcaptcha', 'recaptcha', 'turnstile' ];
$captcha_provider = wpforms_setting( 'captcha-provider', 'recaptcha' );
if ( ! in_array( $captcha_provider, $allowed_captcha_list, true ) ) {
return [
'provider' => 'none',
];
}
return [
'provider' => $captcha_provider,
'site_key' => sanitize_text_field( wpforms_setting( "{$captcha_provider}-site-key", '' ) ),
'secret_key' => sanitize_text_field( wpforms_setting( "{$captcha_provider}-secret-key", '' ) ),
'recaptcha_type' => wpforms_setting( 'recaptcha-type', 'v2' ),
'theme' => sanitize_text_field( wpforms_setting( "{$captcha_provider}-theme", '' ) ),
];
}
/**
* Process smart tags.
*
* @since 1.7.1
*
* @param string $content Content.
* @param array $form_data Form data.
* @param array $fields List of fields.
* @param string $entry_id Entry ID.
*
* @return string
*/
function wpforms_process_smart_tags( $content, $form_data, $fields = [], $entry_id = '' ) {
// Skip it if variables have invalid format.
if ( ! is_string( $content ) || ! is_array( $form_data ) || ! is_array( $fields ) ) {
return $content;
}
/**
* Process smart tags.
*
* @since 1.4.0
*
* @param string $content Content.
* @param array $form_data Form data.
* @param array $fields List of fields.
* @param string $entry_id Entry ID.
*
* @return string
*/
return apply_filters( 'wpforms_process_smart_tags', $content, $form_data, $fields, $entry_id );
}

View File

@@ -0,0 +1,281 @@
<?php
/**
* Helper functions to work with multidimensional arrays easier.
*
* @since 1.5.6
*/
/**
* Determine whether the given value is array accessible.
*
* @since 1.5.6
*
* @param mixed $value Checkin to accessible.
*
* @return bool
*/
function wpforms_list_accessible( $value ) {
return is_array( $value ) || $value instanceof ArrayAccess;
}
/**
* Set an array item to a given value using "dot" notation.
*
* If no key is given to the method, the entire array will be replaced.
*
* @since 1.5.6
*
* @param array $array Existing array.
* @param string $key Path to set.
* @param mixed $value Value to set.
* @param string $separator Separator.
*
* @return array New array.
*/
function wpforms_list_set( $array, $key, $value, $separator = '.' ) {
if ( ! wpforms_list_accessible( $array ) ) {
return $value;
}
if ( $key === null ) {
$array = $value;
return $array;
}
$keys = explode( $separator, $key );
$count_keys = count( $keys );
$values = array_values( $keys );
$last_key = $values[ $count_keys - 1 ];
$tmp_array = &$array;
for ( $i = 0; $i < $count_keys - 1; $i ++ ) {
$k = $keys[ $i ];
$tmp_array = &$tmp_array[ $k ];
}
$tmp_array[ $last_key ] = $value;
return $array;
}
/**
* Determine if the given key exists in the provided array.
*
* @since 1.5.6
*
* @param ArrayAccess|array $array Existing array.
* @param string|int $key To check.
*
* @return bool
*/
function wpforms_list_exists( $array, $key ) {
if ( ! wpforms_list_accessible( $array ) ) {
return false;
}
if ( $array instanceof ArrayAccess ) {
return $array->offsetExists( $key );
}
return array_key_exists( $key, $array );
}
/**
* Get an item from an array using "dot" notation.
*
* @since 1.5.6
*
* @param ArrayAccess|array $array Where we want to get.
* @param string $key Key with dot's.
* @param mixed $default Value.
*
* @return mixed
*/
function wpforms_list_get( $array, $key, $default = null ) {
if ( ! wpforms_list_accessible( $array ) ) {
return $default;
}
if ( $key === null ) {
return $array;
}
if ( ! is_string( $key ) ) {
return $default;
}
if ( wpforms_list_exists( $array, $key ) ) {
return $array[ $key ];
}
foreach ( explode( '.', $key ) as $segment ) {
if ( wpforms_list_accessible( $array ) && wpforms_list_exists( $array, $segment ) ) {
$array = $array[ $segment ];
} else {
return $default;
}
}
return $array;
}
/**
* Check if an item exists in an array using "dot" notation.
*
* @since 1.5.6
*
* @param ArrayAccess|array $array To check.
* @param string $key Keys with dot's.
*
* @return bool
*/
function wpforms_list_has( $array, $key ) {
if ( ! $array ) {
return false;
}
if ( $key === null || ! is_string( $key ) ) {
return false;
}
if ( wpforms_list_exists( $array, $key ) ) {
return true;
}
foreach ( explode( '.', $key ) as $segment ) {
if ( wpforms_list_accessible( $array ) && wpforms_list_exists( $array, $segment ) ) {
$array = $array[ $segment ];
} else {
return false;
}
}
return true;
}
/**
* Determine if an array is associative.
*
* An array is "associative" if it doesn't have sequential numerical keys beginning with zero.
*
* @since 1.5.6
*
* @param array $array To check.
*
* @return bool
*/
function wpforms_list_is_assoc( $array ) {
$keys = array_keys( $array );
return array_keys( $keys ) !== $keys;
}
/**
* Get a subset of the items from the given array.
*
* @since 1.5.6
*
* @param array $array To get.
* @param array|string $keys To filter.
*
* @return array
*/
function wpforms_list_only( $array, $keys ) {
return array_intersect_key( $array, array_flip( (array) $keys ) );
}
/**
* Remove one or many array items from a given array using "dot" notation.
*
* @since 1.5.6
*
* @param array $array To forget.
* @param array|string $keys To exclude.
*
* @return array
*/
function wpforms_list_forget( $array, $keys ) {
$tmp_array = &$array;
$keys = (array) $keys;
if ( count( $keys ) === 0 ) {
return $array;
}
foreach ( $keys as $key ) {
// if the exact key exists in the top-level, remove it.
if ( wpforms_list_exists( $array, $key ) ) {
unset( $array[ $key ] );
continue;
}
$parts = explode( '.', $key );
$count_keys = count( $parts );
$values = array_values( $parts );
$last_key = $values[ $count_keys - 1 ];
for ( $i = 0; $i < $count_keys - 1; $i ++ ) {
$k = $parts[ $i ];
$tmp_array = &$tmp_array[ $k ];
}
unset( $tmp_array[ $last_key ] );
}
return $array;
}
/**
* Insert a value or key/value pair after a specific key in an array.
* If key doesn't exist, value is appended to the end of the array.
*
* @since 1.5.8
*
* @param array $array Array where to insert.
* @param string $key Insert after key.
* @param array $new Array to insert.
*
* @return array
*/
function wpforms_list_insert_after( $array, $key, $new ) {
$keys = array_keys( $array );
$index = array_search( $key, $keys, true );
$pos = $index === false ? count( $array ) : $index + 1;
return array_merge( array_slice( $array, 0, $pos ), $new, array_slice( $array, $pos ) );
}
/**
* Cleanup $items array recursively removing from it all keys not existing in the $default array.
*
* @since 1.7.2
*
* @param array $items Items.
* @param array $default Default items.
*
* @return array
*/
function wpforms_list_intersect_key( $items, $default ) {
if ( ! is_array( $items ) ) {
return $items;
}
$items = array_intersect_key( $items, $default );
foreach ( $items as $key => &$item ) {
$item = wpforms_list_intersect_key( $item, $default[ $key ] );
}
return $items;
}

View File

@@ -0,0 +1,579 @@
<?php
/**
* Payment related functions.
*
* @since 1.8.2
*/
/**
* Get supported currencies.
*
* @since 1.2.4
*
* @return array
*/
function wpforms_get_currencies() {
$currencies = [
'USD' => [
'name' => esc_html__( 'U.S. Dollar', 'wpforms-lite' ),
'symbol' => '&#36;',
'symbol_pos' => 'left',
'thousands_separator' => ',',
'decimal_separator' => '.',
'decimals' => 2,
],
'GBP' => [
'name' => esc_html__( 'Pound Sterling', 'wpforms-lite' ),
'symbol' => '&pound;',
'symbol_pos' => 'left',
'thousands_separator' => ',',
'decimal_separator' => '.',
'decimals' => 2,
],
'EUR' => [
'name' => esc_html__( 'Euro', 'wpforms-lite' ),
'symbol' => '&euro;',
'symbol_pos' => 'right',
'thousands_separator' => '.',
'decimal_separator' => ',',
'decimals' => 2,
],
'AUD' => [
'name' => esc_html__( 'Australian Dollar', 'wpforms-lite' ),
'symbol' => '&#36;',
'symbol_pos' => 'left',
'thousands_separator' => ',',
'decimal_separator' => '.',
'decimals' => 2,
],
'BRL' => [
'name' => esc_html__( 'Brazilian Real', 'wpforms-lite' ),
'symbol' => 'R$',
'symbol_pos' => 'left',
'thousands_separator' => '.',
'decimal_separator' => ',',
'decimals' => 2,
],
'CAD' => [
'name' => esc_html__( 'Canadian Dollar', 'wpforms-lite' ),
'symbol' => '&#36;',
'symbol_pos' => 'left',
'thousands_separator' => ',',
'decimal_separator' => '.',
'decimals' => 2,
],
'CZK' => [
'name' => esc_html__( 'Czech Koruna', 'wpforms-lite' ),
'symbol' => '&#75;&#269;',
'symbol_pos' => 'right',
'thousands_separator' => '.',
'decimal_separator' => ',',
'decimals' => 2,
],
'DKK' => [
'name' => esc_html__( 'Danish Krone', 'wpforms-lite' ),
'symbol' => 'kr.',
'symbol_pos' => 'right',
'thousands_separator' => '.',
'decimal_separator' => ',',
'decimals' => 2,
],
'HKD' => [
'name' => esc_html__( 'Hong Kong Dollar', 'wpforms-lite' ),
'symbol' => '&#36;',
'symbol_pos' => 'left',
'thousands_separator' => ',',
'decimal_separator' => '.',
'decimals' => 2,
],
'HUF' => [
'name' => esc_html__( 'Hungarian Forint', 'wpforms-lite' ),
'symbol' => 'Ft',
'symbol_pos' => 'right',
'thousands_separator' => '.',
'decimal_separator' => ',',
'decimals' => 2,
],
'ILS' => [
'name' => esc_html__( 'Israeli New Sheqel', 'wpforms-lite' ),
'symbol' => '&#8362;',
'symbol_pos' => 'left',
'thousands_separator' => ',',
'decimal_separator' => '.',
'decimals' => 2,
],
'MYR' => [
'name' => esc_html__( 'Malaysian Ringgit', 'wpforms-lite' ),
'symbol' => '&#82;&#77;',
'symbol_pos' => 'left',
'thousands_separator' => ',',
'decimal_separator' => '.',
'decimals' => 2,
],
'MXN' => [
'name' => esc_html__( 'Mexican Peso', 'wpforms-lite' ),
'symbol' => '&#36;',
'symbol_pos' => 'left',
'thousands_separator' => ',',
'decimal_separator' => '.',
'decimals' => 2,
],
'NOK' => [
'name' => esc_html__( 'Norwegian Krone', 'wpforms-lite' ),
'symbol' => 'Kr',
'symbol_pos' => 'left',
'thousands_separator' => '.',
'decimal_separator' => ',',
'decimals' => 2,
],
'NZD' => [
'name' => esc_html__( 'New Zealand Dollar', 'wpforms-lite' ),
'symbol' => '&#36;',
'symbol_pos' => 'left',
'thousands_separator' => ',',
'decimal_separator' => '.',
'decimals' => 2,
],
'PHP' => [
'name' => esc_html__( 'Philippine Peso', 'wpforms-lite' ),
'symbol' => 'Php',
'symbol_pos' => 'left',
'thousands_separator' => ',',
'decimal_separator' => '.',
'decimals' => 2,
],
'PLN' => [
'name' => esc_html__( 'Polish Zloty', 'wpforms-lite' ),
'symbol' => '&#122;&#322;',
'symbol_pos' => 'left',
'thousands_separator' => '.',
'decimal_separator' => ',',
'decimals' => 2,
],
'RUB' => [
'name' => esc_html__( 'Russian Ruble', 'wpforms-lite' ),
'symbol' => 'pyб',
'symbol_pos' => 'right',
'thousands_separator' => ' ',
'decimal_separator' => '.',
'decimals' => 2,
],
'SGD' => [
'name' => esc_html__( 'Singapore Dollar', 'wpforms-lite' ),
'symbol' => '&#36;',
'symbol_pos' => 'left',
'thousands_separator' => ',',
'decimal_separator' => '.',
'decimals' => 2,
],
'ZAR' => [
'name' => esc_html__( 'South African Rand', 'wpforms-lite' ),
'symbol' => 'R',
'symbol_pos' => 'left',
'thousands_separator' => ',',
'decimal_separator' => '.',
'decimals' => 2,
],
'SEK' => [
'name' => esc_html__( 'Swedish Krona', 'wpforms-lite' ),
'symbol' => 'Kr',
'symbol_pos' => 'right',
'thousands_separator' => '.',
'decimal_separator' => ',',
'decimals' => 2,
],
'CHF' => [
'name' => esc_html__( 'Swiss Franc', 'wpforms-lite' ),
'symbol' => 'CHF',
'symbol_pos' => 'left',
'thousands_separator' => ',',
'decimal_separator' => '.',
'decimals' => 2,
],
'TWD' => [
'name' => esc_html__( 'Taiwan New Dollar', 'wpforms-lite' ),
'symbol' => '&#36;',
'symbol_pos' => 'left',
'thousands_separator' => ',',
'decimal_separator' => '.',
'decimals' => 2,
],
'THB' => [
'name' => esc_html__( 'Thai Baht', 'wpforms-lite' ),
'symbol' => '&#3647;',
'symbol_pos' => 'left',
'thousands_separator' => ',',
'decimal_separator' => '.',
'decimals' => 2,
],
];
/**
* Filter for currencies supported in WPForms payments.
*
* @since 1.2.4
*
* @param array $currencies List of currencies.
*/
return array_change_key_case( (array) apply_filters( 'wpforms_currencies', $currencies ), CASE_UPPER );
}
/**
* Sanitize amount by stripping out thousands separators.
*
* @link https://github.com/easydigitaldownloads/easy-digital-downloads/blob/master/includes/formatting.php#L24
*
* @since 1.2.6
*
* @param string $amount Price amount.
* @param string $currency Currency ISO code (USD, EUR, etc).
*
* @return string $amount
*/
function wpforms_sanitize_amount( $amount, $currency = '' ) { // phpcs:ignore Generic.Metrics.CyclomaticComplexity.TooHigh
if ( empty( $currency ) ) {
$currency = wpforms_get_currency();
}
$currency = strtoupper( $currency );
$currencies = wpforms_get_currencies();
$thousands_sep = isset( $currencies[ $currency ]['thousands_separator'] ) ? $currencies[ $currency ]['thousands_separator'] : ',';
$decimal_sep = isset( $currencies[ $currency ]['decimal_separator'] ) ? $currencies[ $currency ]['decimal_separator'] : '.';
// Sanitize the amount.
if ( $decimal_sep === ',' && strpos( $amount, $decimal_sep ) !== false ) {
if ( ( $thousands_sep === '.' || $thousands_sep === ' ' ) && strpos( $amount, $thousands_sep ) !== false ) {
$amount = str_replace( $thousands_sep, '', $amount );
} elseif ( empty( $thousands_sep ) && strpos( $amount, '.' ) !== false ) {
$amount = str_replace( '.', '', $amount );
}
$amount = str_replace( $decimal_sep, '.', $amount );
} elseif ( $thousands_sep === ',' && strpos( $amount, $thousands_sep ) !== false ) {
$amount = str_replace( $thousands_sep, '', $amount );
}
/**
* Remove any characters that are not a digit, a decimal point, or a minus sign.
*
* E is exponent notation. Float number can be written in the form 2E-13, which means 2 * 10^-13.
* 0-9 is digits.
* . is decimal point.
* - is minus sign.
*/
$amount = (string) preg_replace( '/[^E0-9.-]/', '', $amount );
/**
* Set correct currency decimals.
*
* @since 1.6.6
*
* @param int $decimals Default number of decimals.
* @param string $amount Price amount.
*/
$decimals = (int) apply_filters(
'wpforms_sanitize_amount_decimals',
wpforms_get_currency_decimals( $currency ),
$amount
);
return number_format( (float) $amount, $decimals, '.', '' );
}
/**
* Return a nicely formatted amount.
*
* @since 1.2.6
*
* @param string $amount Price amount.
* @param bool $symbol Currency symbol ($, €).
* @param string $currency Currency ISO code (USD, EUR, etc).
*
* @return string $amount Newly formatted amount or Price Not Available
*/
function wpforms_format_amount( $amount, $symbol = false, $currency = '' ) { // phpcs:ignore Generic.Metrics.CyclomaticComplexity.TooHigh
if ( empty( $currency ) ) {
$currency = wpforms_get_currency();
}
$currency = strtoupper( $currency );
$currencies = wpforms_get_currencies();
$thousands_sep = isset( $currencies[ $currency ]['thousands_separator'] ) ? $currencies[ $currency ]['thousands_separator'] : ',';
$decimal_sep = isset( $currencies[ $currency ]['decimal_separator'] ) ? $currencies[ $currency ]['decimal_separator'] : '.';
$sep_found = ! empty( $decimal_sep ) ? strpos( $amount, $decimal_sep ) : false;
// Format the amount.
if (
$decimal_sep === ',' &&
$sep_found !== false
) {
$whole = substr( $amount, 0, $sep_found );
$part = substr( $amount, $sep_found + 1, ( strlen( $amount ) - 1 ) );
$amount = $whole . '.' . $part;
}
// Strip "," (comma) from the amount (if set as the thousands' separator).
if (
$thousands_sep === ',' &&
strpos( $amount, $thousands_sep ) !== false
) {
$amount = (float) str_replace( ',', '', $amount );
}
if ( empty( $amount ) ) {
$amount = 0;
}
/** This filter is documented in wpforms_sanitize_amount function above. */
$decimals = (int) apply_filters(
'wpforms_sanitize_amount_decimals',
wpforms_get_currency_decimals( $currency ),
$amount
);
$number = number_format( (float) $amount, $decimals, $decimal_sep, $thousands_sep );
// Display a symbol, if any.
if ( $symbol && isset( $currencies[ $currency ]['symbol_pos'] ) ) {
/**
* Filter for currency symbol padding.
*
* @since 1.2.6
*
* @param string $symbol_padding Currency symbol padding.
*/
$symbol_padding = apply_filters( 'wpforms_currency_symbol_padding', ' ' );
if ( $currencies[ $currency ]['symbol_pos'] === 'right' ) {
$number .= $symbol_padding . $currencies[ $currency ]['symbol'];
} else {
$number = $currencies[ $currency ]['symbol'] . $symbol_padding . $number;
}
}
return $number;
}
/**
* Get default number of decimals for a given currency.
* If not provided inside the currency, default value is used, which is 2.
*
* @since 1.6.6
*
* @param array|string $currency Currency data we are getting decimals for.
*
* @return int
*/
function wpforms_get_currency_decimals( $currency ) {
if ( is_string( $currency ) ) {
$currencies = wpforms_get_currencies();
$currency_code = strtoupper( $currency );
$currency = isset( $currencies[ $currency_code ] ) ? $currencies[ $currency_code ] : [];
}
/**
* Get currency decimals.
*
* @since 1.6.6
*
* @param int $decimals Default number of decimals.
* @param array|string $currency Currency data we are getting decimals for.
*/
return (int) apply_filters(
'wpforms_get_currency_decimals',
isset( $currency['decimals'] ) ? $currency['decimals'] : 2,
$currency
);
}
/**
* Get payments currency.
* If the currency not available anymore 'USD' used as default.
*
* @since 1.6.6
*
* @return string
*/
function wpforms_get_currency() {
$currency = wpforms_setting( 'currency' );
$currencies = wpforms_get_currencies();
/**
* Get payments currency.
*
* @since 1.6.6
*
* @param string $currency Payments currency.
* @param array $currencies Available currencies.
*/
return apply_filters(
'wpforms_get_currency',
isset( $currencies[ $currency ] ) ? $currency : 'USD',
$currencies
);
}
/**
* Return recognized payment field types.
*
* @since 1.0.0
*
* @return array
*/
function wpforms_payment_fields() {
/**
* Filters the recognized payment field types.
*
* @since 1.0.0
*
* @param array $fields Payment field types.
*/
return (array) apply_filters(
'wpforms_payment_fields',
[ 'payment-single', 'payment-multiple', 'payment-checkbox', 'payment-select' ]
);
}
/**
* Check if form or entry contains payment.
*
* @since 1.0.0
*
* @param string $type Either 'entry' or 'form'.
* @param array $data List of form fields.
*
* @return bool
*/
function wpforms_has_payment( $type = 'entry', $data = [] ) {
$payment = false;
$payment_fields = wpforms_payment_fields();
if ( ! empty( $data['fields'] ) ) {
$data = $data['fields'];
}
if ( empty( $data ) ) {
return false;
}
foreach ( $data as $field ) {
if ( isset( $field['type'] ) && in_array( $field['type'], $payment_fields, true ) ) {
// For entries, only return true if the payment field has an amount.
if (
$type === 'form' ||
(
$type === 'entry' &&
! empty( $field['amount'] ) &&
! empty( wpforms_sanitize_amount( $field['amount'] ) )
)
) {
$payment = true;
break;
}
}
}
return $payment;
}
/**
* Check to see if a form has an active payment gateway configured.
*
* @since 1.4.5
*
* @param array $form_data Form data and settings.
*
* @return bool
*/
function wpforms_has_payment_gateway( $form_data ) {
// PayPal Standard check.
if ( ! empty( $form_data['payments']['paypal_standard']['enable'] ) ) {
return true;
}
// Stripe Check.
if ( ! empty( $form_data['payments']['stripe']['enable'] ) ) {
return true;
}
/**
* Allow modifying whether a form has an active payment gateway.
*
* @since 1.4.5
*
* @param bool $result True if a form has an active payment gateway.
* @param array $form_data Form data and settings.
*/
return (bool) apply_filters( 'wpforms_has_payment_gateway', false, $form_data );
}
/**
* Get payment total amount from entry.
*
* @since 1.0.0
* @since 1.8.2.2 Added PHP max() function before returning a total.
*
* @param array $fields List of fields.
*
* @return string
*/
function wpforms_get_total_payment( $fields ) {
$fields = wpforms_get_payment_items( $fields );
$total = 0;
if ( empty( $fields ) ) {
return false;
}
foreach ( $fields as $field ) {
if ( ! empty( $field['amount'] ) ) {
$amount = wpforms_sanitize_amount( $field['amount'] );
$total += $amount;
}
}
$total = max( 0, $total );
return wpforms_sanitize_amount( $total );
}
/**
* Get payment fields in an entry.
*
* @since 1.0.0
*
* @param array $fields List of fields.
*
* @return array|bool False if no fields provided, otherwise array.
*/
function wpforms_get_payment_items( $fields = [] ) {
if ( empty( $fields ) ) {
return false;
}
$payment_fields = wpforms_payment_fields();
foreach ( $fields as $id => $field ) {
if (
empty( $field['type'] ) ||
empty( $field['amount'] ) ||
! in_array( $field['type'], $payment_fields, true ) ||
empty( wpforms_sanitize_amount( $field['amount'] ) )
) {
// Remove all non-payment fields as well as payment fields with no amount.
unset( $fields[ $id ] );
}
}
return $fields;
}

View File

@@ -0,0 +1,56 @@
<?php
/**
* Helper functions to perform various plugins and addons related actions.
*
* @since 1.8.2.2
*/
use WPForms\Requirements\Requirements;
/**
* Check if addon met requirements.
*
* @since 1.8.2.2
*
* @param array $requirements Addon requirements.
*
* @return bool
*/
function wpforms_requirements( array $requirements ): bool {
// phpcs:ignore WPForms.Formatting.EmptyLineBeforeReturn.RemoveEmptyLineBeforeReturnStatement
return Requirements::get_instance()->validate( $requirements );
}
/**
* Check addon requirements and activate addon or plugin.
*
* @since 1.8.4
*
* @param string $plugin Path to the plugin file relative to the plugins' directory.
*
* @return null|WP_Error Null on success, WP_Error on invalid file.
*/
function wpforms_activate_plugin( string $plugin ) {
$activate = activate_plugin( $plugin );
if ( is_wp_error( $activate ) ) {
return $activate;
}
$requirements = Requirements::get_instance();
if ( ! $requirements->deactivate_not_valid_addon( $plugin ) ) {
return null;
}
// Addon was deactivated due to requirements issues.
return new WP_Error(
'wpforms_addon_incompatible',
implode(
"\n",
$requirements->get_notices()
)
);
}

View File

@@ -0,0 +1,103 @@
<?php
/**
* Helper functions related to privacy, geolocation and user data.
*
* @since 1.8.0
*/
/**
* Get the user IP address.
*
* @since 1.2.5
* @since 1.7.3 Improve the IP detection quality by taking care of proxies (e.g. when the site is behind Cloudflare).
*
* Code based on the:
* - WordPress method \WP_Community_Events::get_unsafe_client_ip
* - Cloudflare documentation https://support.cloudflare.com/hc/en-us/articles/206776727
*
* @return string
*/
function wpforms_get_ip() {
$ip = '127.0.0.1';
$address_headers = [
'HTTP_TRUE_CLIENT_IP',
'HTTP_CF_CONNECTING_IP',
'HTTP_X_REAL_IP',
'HTTP_CLIENT_IP',
'HTTP_X_FORWARDED_FOR',
'HTTP_X_FORWARDED',
'HTTP_X_CLUSTER_CLIENT_IP',
'HTTP_FORWARDED_FOR',
'HTTP_FORWARDED',
'REMOTE_ADDR',
];
foreach ( $address_headers as $header ) {
if ( empty( $_SERVER[ $header ] ) ) {
continue;
}
/*
* HTTP_X_FORWARDED_FOR can contain a chain of comma-separated addresses, with or without spaces.
* The first address is the original client. It can't be trusted for authenticity,
* but we don't need to for this purpose.
*/
// phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
$address_chain = explode( ',', wp_unslash( $_SERVER[ $header ] ) );
$ip = filter_var( trim( $address_chain[0] ), FILTER_VALIDATE_IP );
break;
}
/**
* Filter detected IP address.
*
* @since 1.2.5
*
* @param string $ip IP address.
*/
return filter_var( apply_filters( 'wpforms_get_ip', $ip ), FILTER_VALIDATE_IP );
}
/**
* Determine if collecting user's IP is allowed by GDPR setting (globally or per form).
* Majority of our users have GDPR disabled.
* So we remove this data from the request only when it's not needed:
* 1) when GDPR is enabled AND globally disabled user details storage;
* 2) when GDPR is enabled AND IP address processing is disabled on per form basis.
*
* @since 1.6.6
*
* @param array $form_data Form settings.
*
* @return bool
*/
function wpforms_is_collecting_ip_allowed( $form_data = [] ) {
if (
wpforms_setting( 'gdpr', false ) &&
(
wpforms_setting( 'gdpr-disable-details', false ) ||
( ! empty( $form_data ) && ! empty( $form_data['settings']['disable_ip'] ) )
)
) {
return false;
}
return true;
}
/**
* Determine if collecting cookies is allowed by GDPR setting.
*
* @since 1.7.5
*
* @return bool
*/
function wpforms_is_collecting_cookies_allowed() {
return ! ( wpforms_setting( 'gdpr', false ) && wpforms_setting( 'gdpr-disable-uuid', false ) );
}

View File

@@ -0,0 +1,64 @@
<?php
/**
* Helper functions to work with Providers API.
*
* @since 1.8.0
*/
/**
* Get an array of all the active provider addons.
*
* @since 1.4.7
*
* @return array
*/
function wpforms_get_providers_available() {
return (array) apply_filters( 'wpforms_providers_available', [] );
}
/**
* Get options for all providers.
*
* @since 1.4.7
*
* @param string $provider Define a single provider to get options for this one only.
*
* @return array
*/
function wpforms_get_providers_options( $provider = '' ) {
$options = get_option( 'wpforms_providers', [] );
$provider = sanitize_key( $provider );
$data = $options;
if ( ! empty( $provider ) && isset( $options[ $provider ] ) ) {
$data = $options[ $provider ];
}
return (array) apply_filters( 'wpforms_get_providers_options', $data, $provider );
}
/**
* Update options for all providers.
*
* @since 1.4.7
*
* @param string $provider Provider slug.
* @param array|false $options If false is passed - provider will be removed. Otherwise saved.
* @param string $key Optional key to identify which connection to update. If empty - generate a new one.
*/
function wpforms_update_providers_options( $provider, $options, $key = '' ) {
$providers = wpforms_get_providers_options();
$id = ! empty( $key ) ? $key : uniqid();
$provider = sanitize_key( $provider );
if ( $options ) {
$providers[ $provider ][ $id ] = (array) $options;
} else {
unset( $providers[ $provider ] );
}
update_option( 'wpforms_providers', $providers );
}

View File

@@ -0,0 +1,332 @@
<?php
/**
* Helper functions that were deprecated and can be removed in future.
*
* @since 1.8.0
*/
/**
* Get formatted [ id => title ] pages list.
*
* @since 1.7.2
* @deprecated 1.7.9
*
* @todo Move to includes/deprecated.php
*
* @param array|string $args Array or string of arguments to retrieve pages.
*
* @return array
*/
function wpforms_get_pages_list( $args = [] ) {
_deprecated_function( __FUNCTION__, '1.7.9 of the WPForms plugin' );
$defaults = [
'number' => 20,
];
$args = wp_parse_args( $args, $defaults );
$pages = get_pages( $args );
$list = [];
if ( empty( $pages ) ) {
return $list;
}
foreach ( $pages as $page ) {
$title = wpforms_get_post_title( $page );
$depth = count( $page->ancestors );
$list[ $page->ID ] = str_repeat( '&nbsp;', $depth * 3 ) . $title;
}
return $list;
}
/**
* Dequeue enqueues by URI list.
* Parts of URI (e.g. filename) is also supported.
*
* @since 1.6.1
*
* @param array|string $uris List of URIs or individual URI to dequeue.
* @param WP_Scripts|WP_Styles $enqueues Enqueues list to dequeue from.
*/
function wpforms_dequeue_by_uri( $uris, $enqueues ) {
if ( empty( $enqueues->queue ) ) {
return;
}
foreach ( $enqueues->queue as $handle ) {
if ( empty( $enqueues->registered[ $handle ]->src ) ) {
continue;
}
$src = wp_make_link_relative( $enqueues->registered[ $handle ]->src );
// Support full URLs.
$src = site_url( $src );
foreach ( (array) $uris as $uri ) {
if ( strpos( $src, $uri ) !== false ) {
wp_dequeue_script( $handle );
break;
}
}
}
}
/**
* Dequeue scripts by URI list.
* Parts of URI (e.g. filename) is also supported.
*
* @since 1.6.1
*
* @param array|string $uris List of URIs or individual URI to dequeue.
*/
function wpforms_dequeue_scripts_by_uri( $uris ) {
wpforms_dequeue_by_uri( $uris, wp_scripts() );
}
/**
* Dequeue styles by URI list.
* Parts of URI (e.g. filename) is also supported.
*
* @since 1.6.1
*
* @param array|string $uris List of URIs or individual URI to dequeue.
*/
function wpforms_dequeue_styles_by_uri( $uris ) {
wpforms_dequeue_by_uri( $uris, wp_styles() );
}
/**
* Check if form provided contains Page Break, if so give details.
*
* @since 1.0.0
*
* @todo It is not used since 1.4.0. Probably, it should be deprecated and suggest using the wpforms_get_pagebreak_details() function.
*
* @param WP_Post|array $form Form data.
*
* @return int|bool Pages count or false.
*/
function wpforms_has_pagebreak( $form = false ) { // phpcs:ignore Generic.Metrics.CyclomaticComplexity.TooHigh
if ( ! wpforms()->is_pro() ) {
return false;
}
$form_data = '';
$pagebreak = false;
$pages = 1;
if ( is_object( $form ) && ! empty( $form->post_content ) ) {
$form_data = wpforms_decode( $form->post_content );
} elseif ( is_array( $form ) ) {
$form_data = $form;
}
if ( empty( $form_data['fields'] ) ) {
return false;
}
$fields = $form_data['fields'];
foreach ( $fields as $field ) {
if ( $field['type'] === 'pagebreak' && empty( $field['position'] ) ) {
$pagebreak = true;
$pages ++;
}
}
if ( $pagebreak ) {
return $pages;
}
return false;
}
/**
* Try to find and return a top or bottom Page Break.
*
* @since 1.2.1
*
* @todo It is not used since 1.4.0. Probably, it should be deprecated and suggest using the wpforms_get_pagebreak_details() function.
*
* @param WP_Post|array $form Form data.
* @param string|bool $type Type of Page Break fields (top, bottom, pages or false).
*
* @return array|bool
*/
function wpforms_get_pagebreak( $form = false, $type = false ) { // phpcs:ignore Generic.Metrics.CyclomaticComplexity.MaxExceeded
if ( ! wpforms()->is_pro() ) {
return false;
}
$form_data = '';
if ( is_object( $form ) && ! empty( $form->post_content ) ) {
$form_data = wpforms_decode( $form->post_content );
} elseif ( is_array( $form ) ) {
$form_data = $form;
}
if ( empty( $form_data['fields'] ) ) {
return false;
}
$fields = $form_data['fields'];
$pages = [];
foreach ( $fields as $field ) {
if ( $field['type'] !== 'pagebreak' ) {
continue;
}
$position = ! empty( $field['position'] ) ? $field['position'] : false;
if ( $type === 'pages' && $position !== 'bottom' ) {
$pages[] = $field;
} elseif ( $position === $type ) {
return $field;
}
}
if ( ! empty( $pages ) ) {
return $pages;
}
return false;
}
/**
* Get meta key value for a form field.
*
* @since 1.1.9
*
* @param int|string $id Field ID.
* @param string $key Meta key.
* @param mixed $form_data Form data array.
*
* @return string
*/
function wpforms_get_form_field_meta( $id = '', $key = '', $form_data = '' ) {
if ( empty( $id ) || empty( $key ) || empty( $form_data ) ) {
return '';
}
if ( ! empty( $form_data['fields'][ $id ]['meta'][ $key ] ) ) {
return $form_data['fields'][ $id ]['meta'][ $key ];
}
return '';
}
/**
* Get an array of all possible provider addons.
*
* @since 1.5.5
*
* @return array
*/
function wpforms_get_providers_all() {
return [
[
'name' => 'ActiveCampaign',
'slug' => 'activecampaign',
'img' => 'addon-icon-activecampaign.png',
'plugin' => 'wpforms-activecampaign/wpforms-activecampaign.php',
'plugin_slug' => 'wpforms-activecampaign',
'license' => 'elite',
],
[
'name' => 'AWeber',
'slug' => 'aweber',
'img' => 'addon-icon-aweber.png',
'plugin' => 'wpforms-aweber/wpforms-aweber.php',
'plugin_slug' => 'wpforms-aweber',
'license' => 'pro',
],
[
'name' => 'Campaign Monitor',
'slug' => 'campaign-monitor',
'img' => 'addon-icon-campaign-monitor.png',
'plugin' => 'wpforms-campaign-monitor/wpforms-campaign-monitor.php',
'plugin_slug' => 'wpforms-campaign-monitor',
'license' => 'pro',
],
[
'name' => 'Drip',
'slug' => 'drip',
'img' => 'addon-icon-drip.png',
'plugin' => 'wpforms-drip/wpforms-drip.php',
'plugin_slug' => 'wpforms-drip',
'license' => 'pro',
],
[
'name' => 'GetResponse',
'slug' => 'getresponse',
'img' => 'addon-icon-getresponse.png',
'plugin' => 'wpforms-getresponse/wpforms-getresponse.php',
'plugin_slug' => 'wpforms-getresponse',
'license' => 'pro',
],
[
'name' => 'Mailchimp',
'slug' => 'mailchimp',
'img' => 'addon-icon-mailchimp.png',
'plugin' => 'wpforms-mailchimp/wpforms-mailchimp.php',
'plugin_slug' => 'wpforms-mailchimp',
'license' => 'pro',
],
[
'name' => 'Salesforce',
'slug' => 'salesforce',
'img' => 'addon-icon-salesforce.png',
'plugin' => 'wpforms-salesforce/wpforms-salesforce.php',
'plugin_slug' => 'wpforms-salesforce',
'license' => 'elite',
],
[
'name' => 'Brevo',
'slug' => 'sendinblue',
'img' => 'addon-icon-brevo.png',
'plugin' => 'wpforms-sendinblue/wpforms-sendinblue.php',
'plugin_slug' => 'wpforms-sendinblue',
'license' => 'pro',
],
[
'name' => 'Zapier',
'slug' => 'zapier',
'img' => 'addon-icon-zapier.png',
'plugin' => 'wpforms-zapier/wpforms-zapier.php',
'plugin_slug' => 'wpforms-zapier',
'license' => 'pro',
],
[
'name' => 'HubSpot',
'slug' => 'hubspot',
'img' => 'addon-icon-hubspot.png',
'plugin' => 'wpforms-hubspot/wpforms-hubspot.php',
'plugin_slug' => 'wpforms-hubspot',
'license' => 'pro',
],
[
'name' => 'ConvertKit',
'slug' => 'convertkit',
'img' => 'addon-icon-convertkit.png',
'plugin' => 'wpforms-convertkit/wpforms-convertkit.php',
'plugin_slug' => 'wpforms-convertkit',
'license' => 'pro',
],
];
}

View File

@@ -0,0 +1,321 @@
<?php
/**
* Generic helper functions.
*
* @since 1.8.0
*/
use WPForms\Helpers\Chain;
/**
* Get a suffix for assets, `.min` if debug is disabled.
*
* @since 1.4.1
*
* @return string
*/
function wpforms_get_min_suffix() {
return wpforms_debug() ? '' : '.min';
}
/**
* Chain monad, useful for chaining certain array or string related functions.
*
* @since 1.5.6
*
* @param mixed $value Any data.
*
* @return Chain
*/
function wpforms_chain( $value ) {
return Chain::of( $value );
}
/**
* Convert object to an array.
*
* @since 1.1.7
*
* @param object $object Object to convert.
*
* @return mixed
*/
function wpforms_object_to_array( $object ) {
if ( ! is_object( $object ) && ! is_array( $object ) ) {
return $object;
}
if ( is_object( $object ) ) {
$object = get_object_vars( $object );
}
return array_map( 'wpforms_object_to_array', $object );
}
/**
* Insert an array into another array before/after a certain key.
*
* @link https://gist.github.com/scribu/588429
*
* @since 1.3.9
*
* @param array $array The initial array.
* @param array $pairs The array to insert.
* @param string $key The certain key.
* @param string $position Where to insert the array - before or after the key.
*
* @return array
*/
function wpforms_array_insert( $array, $pairs, $key, $position = 'after' ) {
$key_pos = array_search( $key, array_keys( $array ), true );
if ( $position === 'after' ) {
$key_pos ++;
}
if ( $key_pos !== false ) {
$result = array_slice( $array, 0, $key_pos );
$result = array_merge( $result, $pairs );
$result = array_merge( $result, array_slice( $array, $key_pos ) );
} else {
$result = array_merge( $array, $pairs );
}
return $result;
}
/**
* Recursively remove empty strings from an array.
*
* @since 1.3.9.1
*
* @param array $data Any data.
*
* @return array
*/
function wpforms_array_remove_empty_strings( $data ) {
foreach ( $data as $key => $value ) {
if ( is_array( $value ) ) {
$data[ $key ] = wpforms_array_remove_empty_strings( $data[ $key ] );
}
if ( $data[ $key ] === '' ) {
unset( $data[ $key ] );
}
}
return $data;
}
/**
* Count words in the string.
*
* @since 1.6.2
*
* @param string $string String value.
*
* @return integer Words count.
*/
function wpforms_count_words( $string ) {
if ( ! is_string( $string ) ) {
return 0;
}
$patterns = [
'/([A-Z]+),([A-Z]+)/i',
'/([0-9]+),([A-Z]+)/i',
'/([A-Z]+),([0-9]+)/i',
];
foreach ( $patterns as $pattern ) {
$string = preg_replace_callback(
$pattern,
function( $matches ) {
return $matches[1] . ', ' . $matches[2];
},
$string
);
}
$words = preg_split( '/[\s]+/', $string );
return is_array( $words ) ? count( $words ) : 0;
}
/**
* Link a list of words or phrases with commas, but the last one with a conjunction.
*
* For example:
* [ 'Sullie', 'Pattie', 'me' ] with 'and' conjunction becomes 'Sullie, Pattie and me'.
* [ 'Sullie', 'Pattie', 'me' ] with 'or' conjunction becomes 'Sullie, Pattie or me'.
*
* @since 1.8.0
*
* @param array $list A list of words or phrases to link together.
* @param string $conjunction Coordinating conjunction to use for last word or phrase (usually and, or).
* The string is expected to be translatable.
*
* @return string Linked words and/or phrases.
*/
function wpforms_conjunct( $list, $conjunction ) {
$last_chunk = array_pop( $list );
return $list ?
sprintf( '%s %s %s', implode( ', ', $list ), $conjunction, $last_chunk ) :
$last_chunk;
}
/**
* Get the current URL.
*
* @since 1.0.0
* @since 1.7.2 Refactored based on the `home_url` function.
*
* @return string
*/
function wpforms_current_url() {
$parsed_home_url = wp_parse_url( home_url() );
$url = $parsed_home_url['scheme'] . '://' . $parsed_home_url['host'];
if ( ! empty( $parsed_home_url['port'] ) ) {
$url .= ':' . $parsed_home_url['port'];
}
// phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotValidated, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
$url .= wp_unslash( $_SERVER['REQUEST_URI'] );
return esc_url_raw( $url );
}
/**
* Add UTM tags to a link that allows detecting traffic sources for our or partners' websites.
*
* @since 1.7.5
*
* @param string $link Link to which you need to add UTM tags.
* @param string $medium The page or location description. Check your current page and try to find
* and use an already existing medium for links otherwise, use a page name.
* @param string $content The feature's name, the button's content, the link's text, or something
* else that describes the element that contains the link.
* @param string $term Additional information for the content that makes the link more unique.
*
* @return string
*/
function wpforms_utm_link( $link, $medium, $content = '', $term = '' ) {
return add_query_arg(
array_filter(
[
'utm_campaign' => wpforms()->is_pro() ? 'plugin' : 'liteplugin',
'utm_source' => strpos( $link, 'https://wpforms.com' ) === 0 ? 'WordPress' : 'wpformsplugin',
'utm_medium' => rawurlencode( $medium ),
'utm_content' => rawurlencode( $content ),
'utm_term' => rawurlencode( $term ),
'utm_locale' => wpforms_sanitize_key( get_locale() ),
]
),
$link
);
}
/**
* Modify the default USer-Agent generated by wp_remote_*() to include additional information.
*
* @since 1.7.5.2
*
* @return string
*/
function wpforms_get_default_user_agent() {
$license_type = wpforms()->is_pro() ? ucwords( (string) wpforms_get_license_type() ) : 'Lite';
return 'WordPress/' . get_bloginfo( 'version' ) . '; ' . get_bloginfo( 'url' ) . '; WPForms/' . $license_type . '-' . WPFORMS_VERSION;
}
/**
* Get the ISO 639-2 Language Code from user/site locale.
*
* @see http://www.loc.gov/standards/iso639-2/php/code_list.php
*
* @since 1.5.0
*
* @return string
*/
function wpforms_get_language_code() {
$default_lang = 'en';
$locale = get_user_locale();
if ( ! empty( $locale ) ) {
$lang = explode( '_', $locale );
if ( ! empty( $lang ) && is_array( $lang ) ) {
$default_lang = strtolower( $lang[0] );
}
}
return $default_lang;
}
/**
* Changes array of items into string of items, separated by comma and sql-escaped.
*
* @see https://coderwall.com/p/zepnaw
*
* @since 1.7.4
*
* @param mixed|array $items Item(s) to be joined into string.
* @param string $format Can be %s or %d.
*
* @return string Items separated by comma and sql-escaped.
*/
function wpforms_wpdb_prepare_in( $items, $format = '%s' ) {
global $wpdb;
$items = (array) $items;
$how_many = count( $items );
if ( $how_many === 0 ) {
return '';
}
$placeholders = array_fill( 0, $how_many, $format );
$prepared_format = implode( ',', $placeholders );
// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
return $wpdb->prepare( $prepared_format, $items );
}
/**
* Get the render engine slug according to the Modern Markup setting value and corresponding filter.
*
* @since 1.8.1
*
* @return string
*/
function wpforms_get_render_engine() {
$render_engine = empty( wpforms_setting( 'modern-markup', false ) ) ? 'classic' : 'modern';
/**
* Filter current render engine slug.
* Allows addons to use their own frontend rendering engine.
*
* @since 1.8.1
*
* @param string $render_engine Render engine slug.
*/
$render_engine = apply_filters( 'wpforms_get_render_engine', $render_engine );
return $render_engine;
}

View File

@@ -0,0 +1,118 @@
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Register and setup WPForms as a Visual Composer element.
*
* @since 1.3.0
*/
function wpforms_visual_composer_shortcode() {
if ( ! is_user_logged_in() ) {
return;
}
$wpf = wpforms()->form->get(
'',
[
'orderby' => 'title',
]
);
if ( ! empty( $wpf ) ) {
$forms = [
esc_html__( 'Select a form to display', 'wpforms-lite' ) => '',
];
foreach ( $wpf as $form ) {
$forms[ $form->post_title ] = $form->ID;
}
} else {
$forms = [
esc_html__( 'No forms found', 'wpforms-lite' ) => '',
];
}
vc_map(
[
'name' => esc_html__( 'WPForms', 'wpforms-lite' ),
'base' => 'wpforms',
'icon' => WPFORMS_PLUGIN_URL . 'assets/images/sullie-vc.png',
'category' => esc_html__( 'Content', 'wpforms-lite' ),
'description' => esc_html__( 'Add your form', 'wpforms-lite' ),
'params' => [
[
'type' => 'dropdown',
'heading' => esc_html__( 'Form', 'wpforms-lite' ),
'param_name' => 'id',
'value' => $forms,
'save_always' => true,
'description' => esc_html__( 'Select a form to add it to your post or page.', 'wpforms-lite' ),
'admin_label' => true,
],
[
'type' => 'dropdown',
'heading' => esc_html__( 'Display Form Name', 'wpforms-lite' ),
'param_name' => 'title',
'value' => [
esc_html__( 'No', 'wpforms-lite' ) => 'false',
esc_html__( 'Yes', 'wpforms-lite' ) => 'true',
],
'save_always' => true,
'description' => esc_html__( 'Would you like to display the forms name?', 'wpforms-lite' ),
'dependency' => [
'element' => 'id',
'not_empty' => true,
],
],
[
'type' => 'dropdown',
'heading' => esc_html__( 'Display Form Description', 'wpforms-lite' ),
'param_name' => 'description',
'value' => [
esc_html__( 'No', 'wpforms-lite' ) => 'false',
esc_html__( 'Yes', 'wpforms-lite' ) => 'true',
],
'save_always' => true,
'description' => esc_html__( 'Would you like to display the form description?', 'wpforms-lite' ),
'dependency' => [
'element' => 'id',
'not_empty' => true,
],
],
],
]
);
}
add_action( 'vc_before_init', 'wpforms_visual_composer_shortcode' );
/**
* Load our basic CSS when in Visual Composer's frontend editor.
*
* @since 1.3.0
*/
function wpforms_visual_composer_shortcode_css() {
// Load CSS per global setting.
if ( wpforms_setting( 'disable-css', '1' ) === '1' ) {
wp_enqueue_style(
'wpforms-full',
WPFORMS_PLUGIN_URL . 'assets/css/frontend/classic/wpforms-full.css',
[],
WPFORMS_VERSION
);
}
if ( wpforms_setting( 'disable-css', '1' ) === '2' ) {
wp_enqueue_style(
'wpforms-base',
WPFORMS_PLUGIN_URL . 'assets/css/wpforms-base.css',
[],
WPFORMS_VERSION
);
}
}
add_action( 'vc_load_iframe_jscss', 'wpforms_visual_composer_shortcode_css' );

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,842 @@
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Constant Contact integration.
*
* @since 1.3.6
*/
class WPForms_Constant_Contact extends WPForms_Provider {
/**
* Provider access token.
*
* @since 1.3.6
*
* @var string
*/
public $access_token;
/**
* Provider API key.
*
* @since 1.3.6
*
* @var string
*/
public $api_key = 'c58xq3r27udz59h9rrq7qnvf';
/**
* Sign up link.
*
* @since 1.3.6
*
* @var string
*/
public $sign_up = 'https://constant-contact.evyy.net/c/11535/341874/3411?sharedid=wpforms';
/**
* Initialize.
*
* @since 1.3.6
*/
public function init() {
$this->version = '1.3.6';
$this->name = 'Constant Contact';
$this->slug = 'constant-contact';
$this->priority = 14;
$this->icon = WPFORMS_PLUGIN_URL . 'assets/images/icon-provider-constant-contact.png';
if ( is_admin() ) {
// Admin notice requesting connecting.
$this->connect_request();
add_action( 'wpforms_admin_notice_dismiss_ajax', [ $this, 'connect_dismiss' ] );
add_filter( "wpforms_providers_provider_settings_formbuilder_display_content_default_screen_{$this->slug}", [ $this, 'builder_settings_default_content' ] );
// Provide option to override sign up link.
$sign_up = get_option( 'wpforms_constant_contact_signup', false );
if ( $sign_up ) {
$this->sign_up = esc_html( $sign_up );
}
}
}
/**
* Process and submit entry to provider.
*
* @since 1.3.6
*
* @param array $fields List of fields with their data and settings.
* @param array $entry Submitted entry values.
* @param array $form_data Form data and settings.
* @param int $entry_id Saved entry ID.
*
* @return void
*/
public function process_entry( $fields, $entry, $form_data, $entry_id = 0 ) {
// Only run if this form has a connections for this provider.
if ( empty( $form_data['providers'][ $this->slug ] ) ) {
return;
}
/*
* Fire for each connection.
*/
foreach ( $form_data['providers'][ $this->slug ] as $connection ) :
// Before proceeding make sure required fields are configured.
if ( empty( $connection['fields']['email'] ) ) {
continue;
}
// Setup basic data.
$list_id = $connection['list_id'];
$account_id = $connection['account_id'];
$email_data = explode( '.', $connection['fields']['email'] );
$email_id = $email_data[0];
$email = $fields[ $email_id ]['value'];
$this->api_connect( $account_id );
// Email is required and Access token are required.
if ( empty( $email ) || empty( $this->access_token ) ) {
continue;
}
// Check for conditionals.
$pass = $this->process_conditionals( $fields, $entry, $form_data, $connection );
if ( ! $pass ) {
wpforms_log(
'Constant Contact Subscription stopped by conditional logic',
$fields,
[
'type' => [ 'provider', 'conditional_logic' ],
'parent' => $entry_id,
'form_id' => $form_data['id'],
]
);
continue;
}
// Check to see if the lead already exists in Constant Contact.
$response = wp_remote_get( 'https://api.constantcontact.com/v2/contacts?api_key=' . $this->api_key . '&access_token=' . $this->access_token . '&email=' . $email );
$contact = json_decode( wp_remote_retrieve_body( $response ), true );
// Return early if there was a problem.
if ( isset( $contact['error_key'] ) ) {
wpforms_log(
'Constant Contact API Error',
$contact->get_error_message(),
[
'type' => [ 'provider', 'error' ],
'parent' => $entry_id,
'form_id' => $form_data['id'],
]
);
continue;
}
/*
* Setup Merge Vars
*/
$merge_vars = [];
foreach ( $connection['fields'] as $name => $merge_var ) {
// Don't include Email or Full name fields.
if ( 'email' === $name ) {
continue;
}
// Check if merge var is mapped.
if ( empty( $merge_var ) ) {
continue;
}
$merge_var = explode( '.', $merge_var );
$id = $merge_var[0];
$key = ! empty( $merge_var[1] ) ? $merge_var[1] : 'value';
// Check if mapped form field has a value.
if ( empty( $fields[ $id ][ $key ] ) ) {
continue;
}
$value = $fields[ $id ][ $key ];
// Constant Contact doesn't native URL field so it has to be
// stored in a custom field.
if ( $name === 'url' ) {
$merge_vars['custom_fields'] = [
[
'name' => 'custom_field_1',
'value' => $value,
],
];
continue;
}
// Constant Contact stores name in two fields, so we have to
// separate it.
if ( $name === 'full_name' ) {
$names = explode( ' ', $value );
if ( ! empty( $names[0] ) ) {
$merge_vars['first_name'] = $names[0];
}
if ( ! empty( $names[1] ) ) {
$merge_vars['last_name'] = $names[1];
}
continue;
}
// Constant Contact stores address in multiple fields, so we
// have to separate it.
if ( $name === 'address' ) {
// Only support Address fields.
if ( $fields[ $id ]['type'] !== 'address' ) {
continue;
}
// Postal code may be in extended US format.
$postal = [
'code' => '',
'subcode' => '',
];
if ( ! empty( $fields[ $id ]['postal'] ) ) {
$p = explode( '-', $fields[ $id ]['postal'] );
$postal['code'] = ! empty( $p[0] ) ? $p[0] : '';
$postal['subcode'] = ! empty( $p[1] ) ? $p[1] : '';
}
$merge_vars['addresses'] = [
[
'address_type' => 'BUSINESS',
'city' => ! empty( $fields[ $id ]['city'] ) ? $fields[ $id ]['city'] : '',
'country_code' => ! empty( $fields[ $id ]['country'] ) ? $fields[ $id ]['country'] : '',
'line1' => ! empty( $fields[ $id ]['address1'] ) ? $fields[ $id ]['address1'] : '',
'line2' => ! empty( $fields[ $id ]['address2'] ) ? $fields[ $id ]['address2'] : '',
'postal_code' => $postal['code'],
'state' => ! empty( $fields[ $id ]['state'] ) ? $fields[ $id ]['state'] : '',
'sub_postal_code' => $postal['subcode'],
],
];
continue;
}
$merge_vars[ $name ] = $value;
}
/*
* Process in API
*/
// If we have a previous contact, only update the list association.
if ( ! empty( $contact['results'] ) ) {
$data = $contact['results'][0];
// Check if they are already assigned to lists.
if ( ! empty( $data['lists'] ) ) {
foreach ( $data['lists'] as $list ) {
// If they are already assigned to this list, return early.
if ( isset( $list['id'] ) && (string) $list_id === (string) $list['id'] ) {
return;
}
}
// Otherwise, add them to the list.
$data['lists'][ count( $data['lists'] ) ] = [
'id' => $list_id,
'status' => 'ACTIVE',
];
} else {
// Add the contact to the list.
$data['lists'][0] = [
'id' => $list_id,
'status' => 'ACTIVE',
];
}
// Combine merge vars into data before sending.
$data = array_merge( $data, $merge_vars );
// Args to use.
$args = [
'body' => wp_json_encode( $data ),
'method' => 'PUT',
'headers' => [
'Content-Type' => 'application/json',
],
];
$update = wp_remote_request( 'https://api.constantcontact.com/v2/contacts/' . $data['id'] . '?api_key=' . $this->api_key . '&access_token=' . $this->access_token . '&action_by=ACTION_BY_VISITOR', $args );
$res = json_decode( wp_remote_retrieve_body( $update ), true );
} else {
// Add a new contact.
$data = [
'email_addresses' => [ [ 'email_address' => $email ] ],
'lists' => [ [ 'id' => $list_id ] ],
];
// Combine merge vars into data before sending.
$data = array_merge( $data, $merge_vars );
// Args to use.
$args = [
'body' => wp_json_encode( $data ),
'headers' => [
'Content-Type' => 'application/json',
],
];
$add = wp_remote_post( 'https://api.constantcontact.com/v2/contacts?api_key=' . $this->api_key . '&access_token=' . $this->access_token . '&action_by=ACTION_BY_VISITOR', $args );
$res = json_decode( wp_remote_retrieve_body( $add ), true );
}
// Check for errors.
if ( isset( $res['error_key'] ) ) {
wpforms_log(
'Constant Contact API Error',
$res->get_error_message(),
[
'type' => [ 'provider', 'error' ],
'parent' => $entry_id,
'form_id' => $form_data['id'],
]
);
}
endforeach;
}
/************************************************************************
* API methods - these methods interact directly with the provider API. *
************************************************************************/
/**
* Authenticate with the API.
*
* @since 1.3.6
*
* @param array $data Contact data.
* @param string $form_id Form ID.
*
* @return WP_Error|string Unique ID or error object
*/
public function api_auth( $data = [], $form_id = '' ) {
$this->access_token = isset( $data['authcode'] ) ? $data['authcode'] : '';
$user = $this->get_account_information();
if ( is_wp_error( $user ) ) {
return $user;
}
$id = uniqid();
wpforms_update_providers_options(
$this->slug,
[
'access_token' => sanitize_text_field( $data['authcode'] ),
'label' => sanitize_text_field( $data['label'] ),
'date' => time(),
],
$id
);
return $id;
}
/**
* Get account information.
*
* @since 1.7.6
*
* @return array|WP_Error
*/
public function get_account_information() {
$response = wp_remote_get( 'https://api.constantcontact.com/v2/account/info?api_key=' . $this->api_key . '&access_token=' . $this->access_token );
if ( is_wp_error( $response ) ) {
return $response;
}
$user = json_decode( wp_remote_retrieve_body( $response ), true );
if ( ! empty( $user[0]['error_key'] ) ) {
$message = ! empty( $user[0]['error_message'] ) ? $user[0]['error_message'] : '';
return new WP_Error( $this->slug . '_error', $message );
}
return $response;
}
/**
* Establish connection object to API.
*
* @since 1.3.6
*
* @param string $account_id
*
* @return mixed array or error object.
*/
public function api_connect( $account_id ) {
if ( ! empty( $this->api[ $account_id ] ) ) {
return $this->api[ $account_id ];
} else {
$providers = wpforms_get_providers_options();
if ( ! empty( $providers[ $this->slug ][ $account_id ] ) ) {
$this->api[ $account_id ] = true;
$this->access_token = $providers[ $this->slug ][ $account_id ]['access_token'];
} else {
return $this->error( 'API error' );
}
}
}
/**
* Retrieve provider account lists.
*
* @since 1.3.6
*
* @param string $connection_id
* @param string $account_id
*
* @return mixed array or error object
*/
public function api_lists( $connection_id = '', $account_id = '' ) {
$this->api_connect( $account_id );
$request = wp_remote_get( 'https://api.constantcontact.com/v2/lists?api_key=' . $this->api_key . '&access_token=' . $this->access_token );
$lists = json_decode( wp_remote_retrieve_body( $request ), true );
if ( empty( $lists ) ) {
wpforms_log(
'Constant Contact API Error',
'',
[
'type' => [ 'provider', 'error' ],
]
);
return $this->error( esc_html__( 'API list error: Constant API error', 'wpforms-lite' ) );
}
return $lists;
}
/**
* Retrieve provider account list fields.
*
* @since 1.3.6
*
* @param string $connection_id
* @param string $account_id
* @param string $list_id
*
* @return mixed array or error object
*/
public function api_fields( $connection_id = '', $account_id = '', $list_id = '' ) {
$provider_fields = [
[
'name' => 'Email',
'field_type' => 'email',
'req' => '1',
'tag' => 'email',
],
[
'name' => 'Full Name',
'field_type' => 'name',
'tag' => 'full_name',
],
[
'name' => 'First Name',
'field_type' => 'first',
'tag' => 'first_name',
],
[
'name' => 'Last Name',
'field_type' => 'last',
'tag' => 'last_name',
],
[
'name' => 'Phone',
'field_type' => 'text',
'tag' => 'work_phone',
],
[
'name' => 'Website',
'field_type' => 'text',
'tag' => 'url',
],
[
'name' => 'Address',
'field_type' => 'address',
'tag' => 'address',
],
[
'name' => 'Job Title',
'field_type' => 'text',
'tag' => 'job_title',
],
[
'name' => 'Company',
'field_type' => 'text',
'tag' => 'company_name',
],
];
return $provider_fields;
}
/*************************************************************************
* Output methods - these methods generally return HTML for the builder. *
*************************************************************************/
/**
* Provider account authorize fields HTML.
*
* @since 1.3.6
*
* @return string
*/
public function output_auth() {
$providers = wpforms_get_providers_options();
$class = ! empty( $providers[ $this->slug ] ) ? 'hidden' : '';
ob_start();
?>
<div class="wpforms-provider-account-add <?php echo sanitize_html_class( $class ); ?> wpforms-connection-block">
<h4><?php esc_html_e( 'Add New Account', 'wpforms-lite' ); ?></h4>
<p>
<?php esc_html_e( 'Please fill out all of the fields below to register your new Constant Contact account.', 'wpforms-lite' ); ?>
<br>
<a href="<?php echo esc_url( wpforms_utm_link( 'https://wpforms.com/docs/how-to-connect-constant-contact-with-wpforms/', 'Marketing Integrations', 'Constant Contact Documentation' ) ); ?>" target="_blank" rel="noopener noreferrer">
<?php esc_html_e( 'Click here for documentation on connecting WPForms with Constant Contact.', 'wpforms-lite' ); ?>
</a>
</p>
<p class="wpforms-alert wpforms-alert-warning">
<?php esc_html_e( 'Because Constant Contact requires external authentication, you will need to register WPForms with Constant Contact before you can proceed.', 'wpforms-lite' ); ?>
</p>
<p>
<strong>
<a onclick="window.open(this.href,'','resizable=yes,location=no,width=750,height=500,status'); return false" href="https://oauth2.constantcontact.com/oauth2/oauth/siteowner/authorize?response_type=code&client_id=c58xq3r27udz59h9rrq7qnvf&redirect_uri=https://wpforms.com/oauth/constant-contact/" class="btn">
<?php esc_html_e( 'Click here to register with Constant Contact', 'wpforms-lite' ); ?>
</a>
</strong>
</p>
<?php
printf(
'<input type="text" data-name="authcode" placeholder="%s %s *" class="wpforms-required">',
esc_attr( $this->name ),
esc_attr__( 'Authorization Code', 'wpforms-lite' )
);
printf(
'<input type="text" data-name="label" placeholder="%s %s *" class="wpforms-required">',
esc_attr( $this->name ),
esc_attr__( 'Account Nickname', 'wpforms-lite' )
);
printf(
'<button data-provider="%s">%s</button>',
esc_attr( $this->slug ),
esc_html__( 'Connect', 'wpforms-lite' )
);
?>
</div>
<?php
return ob_get_clean();
}
/**
* Provider account list groups HTML.
*
* @since 1.3.6
*
* @param string $connection_id Connection Id.
* @param array $connection Connection data.
*
* @return string
*/
public function output_groups( $connection_id = '', $connection = [] ) {
// No groups or segments for this provider.
return '';
}
/**
* Default content for the provider settings panel in the form builder.
*
* @since 1.6.8
*
* @param string $content Default content.
*
* @return string
*/
public function builder_settings_default_content( $content ) {
ob_start();
?>
<p>
<a href="<?php echo esc_url( $this->sign_up ); ?>" class="wpforms-btn wpforms-btn-md wpforms-btn-orange" target="_blank" rel="noopener noreferrer">
<?php esc_html_e( 'Try Constant Contact for Free', 'wpforms-lite' ); ?>
</a>
</p>
<p>
<?php
printf(
'<a href="%s" target="_blank" rel="noopener noreferrer" class="secondary-text">%s</a>',
esc_url( admin_url( 'admin.php?page=wpforms-page&view=constant-contact' ) ),
esc_html__( 'Learn more about the power of email marketing.', 'wpforms-lite' )
);
?>
</p>
<?php
return $content . ob_get_clean();
}
/*************************************************************************
* Integrations tab methods - these methods relate to the settings page. *
*************************************************************************/
/**
* AJAX to add a provider from the settings integrations tab.
*
* @since 1.7.6
*/
public function integrations_tab_add() {
// phpcs:ignore WordPress.Security.NonceVerification, WordPress.Security.ValidatedSanitizedInput
if ( $_POST['provider'] !== $this->slug ) {
return;
}
// phpcs:ignore WordPress.Security.NonceVerification, WordPress.Security.ValidatedSanitizedInput
$data = ! empty( $_POST['data'] ) ? wp_parse_args( wp_unslash( $_POST['data'] ), [] ) : [];
if ( empty( $data['authcode'] ) ) {
wp_send_json_error(
[
'error_msg' => esc_html__( 'The "Authorization Code" is required.', 'wpforms-lite' ),
]
);
}
if ( empty( $data['label'] ) ) {
wp_send_json_error(
[
'error_msg' => esc_html__( 'The "Account Nickname" is required.', 'wpforms-lite' ),
]
);
}
parent::integrations_tab_add();
}
/**
* Form fields to add a new provider account.
*
* @since 1.3.6
*/
public function integrations_tab_new_form() {
?>
<p>
<a href="<?php echo esc_url( wpforms_utm_link( 'https://wpforms.com/docs/how-to-connect-constant-contact-with-wpforms/', 'Settings - Integration', 'Constant Contact Documentation' ) ); ?>" target="_blank" rel="noopener noreferrer">
<?php esc_html_e( 'Click here for documentation on connecting WPForms with Constant Contact.', 'wpforms-lite' ); ?>
</a>
</p>
<p class="wpforms-alert wpforms-alert-warning">
<?php esc_html_e( 'Because Constant Contact requires external authentication, you will need to register WPForms with Constant Contact before you can proceed.', 'wpforms-lite' ); ?>
</p>
<p>
<strong>
<a onclick="window.open(this.href,'','resizable=yes,location=no,width=800,height=600,status'); return false" href="https://oauth2.constantcontact.com/oauth2/oauth/siteowner/authorize?response_type=code&client_id=c58xq3r27udz59h9rrq7qnvf&redirect_uri=https://wpforms.com/oauth/constant-contact/" class="btn">
<?php esc_html_e( 'Click here to register with Constant Contact', 'wpforms-lite' ); ?>
</a>
</strong>
</p>
<?php
printf(
'<input type="text" name="authcode" placeholder="%s %s *" class="wpforms-required">',
esc_attr( $this->name ),
esc_attr__( 'Authorization Code', 'wpforms-lite' )
);
printf(
'<input type="text" name="label" placeholder="%s %s *" class="wpforms-required">',
esc_attr( $this->name ),
esc_attr__( 'Account Nickname', 'wpforms-lite' )
);
}
/************************
* Other functionality. *
************************/
/**
* Add admin notices to connect to Constant Contact.
*
* @since 1.3.6
*/
public function connect_request() {
// Only consider showing the review request to admin users.
if ( ! is_super_admin() ) {
return;
}
// Don't display on WPForms admin content pages.
// phpcs:disable WordPress.Security.NonceVerification.Recommended
if ( ! empty( $_GET['wpforms-page'] ) ) {
return;
}
// Don't display if user is about to connect via Settings page.
if ( ! empty( $_GET['wpforms-integration'] ) && $this->slug === $_GET['wpforms-integration'] ) {
return;
}
// phpcs:enable WordPress.Security.NonceVerification.Recommended
// Only display the notice if the Constant Contact option is set and
// there are previous Constant Contact connections created.
// Please do not delete 'wpforms_constant_contact' option check from the code.
$cc_notice = get_option( 'wpforms_constant_contact', false );
$providers = wpforms_get_providers_options();
if ( ! $cc_notice || ! empty( $providers[ $this->slug ] ) ) {
return;
}
// Output the notice message.
$connect = admin_url( 'admin.php?page=wpforms-settings&view=integrations&wpforms-integration=constant-contact#!wpforms-tab-providers' );
$learn_more = admin_url( 'admin.php?page=wpforms-page&view=constant-contact' );
ob_start();
?>
<p>
<?php
echo wp_kses(
__( 'Get the most out of the <strong>WPForms</strong> plugin &mdash; use it with an active Constant Contact account.', 'wpforms-lite' ),
[
'strong' => [],
]
);
?>
</p>
<p>
<a href="<?php echo esc_url( $this->sign_up ); ?>" class="button-primary" target="_blank" rel="noopener noreferrer">
<?php esc_html_e( 'Try Constant Contact for Free', 'wpforms-lite' ); ?>
</a>
<a href="<?php echo esc_url( $connect ); ?>" class="button-secondary">
<?php esc_html_e( 'Connect your existing account', 'wpforms-lite' ); ?>
</a>
<?php
echo wp_kses(
sprintf( /* translators: %s - WPForms Constant Contact internal URL. */
__( 'Learn More about the <a href="%s">power of email marketing</a>', 'wpforms-lite' ),
esc_url( $learn_more )
),
[
'a' => [
'href' => [],
],
]
);
?>
</p>
<style>
.wpforms-constant-contact-notice p:first-of-type {
margin: 16px 0 8px;
}
.wpforms-constant-contact-notice p:last-of-type {
margin: 8px 0 16px;
}
.wpforms-constant-contact-notice .button-primary,
.wpforms-constant-contact-notice .button-secondary {
margin: 0 10px 0 0;
}
</style>
<?php
$notice = ob_get_clean();
\WPForms\Admin\Notice::info(
$notice,
[
'dismiss' => \WPForms\Admin\Notice::DISMISS_GLOBAL,
'slug' => 'constant_contact_connect',
'autop' => false,
'class' => 'wpforms-constant-contact-notice',
]
);
}
/**
* Dismiss the Constant Contact admin notice.
*
* @since 1.3.6
* @since 1.6.7.1 Added parameter $notice_id.
*
* @param string $notice_id Notice ID (slug).
*/
public function connect_dismiss( $notice_id = '' ) {
if ( $notice_id !== 'global-constant_contact_connect' ) {
return;
}
delete_option( 'wpforms_constant_contact' );
wp_send_json_success();
}
}
new WPForms_Constant_Contact();

View File

@@ -0,0 +1,328 @@
<?php
/**
* Base form template.
*
* @since 1.0.0
*/
abstract class WPForms_Template {
/**
* Full name of the template, eg "Contact Form".
*
* @since 1.0.0
*
* @var string
*/
public $name;
/**
* Slug of the template, eg "contact-form" - no spaces.
*
* @since 1.0.0
*
* @var string
*/
public $slug;
/**
* Source of the template.
*
* @since 1.6.8
*
* @var array
*/
public $source;
/**
* Categories array.
*
* @since 1.6.8
*
* @var array
*/
public $categories;
/**
* Short description the template.
*
* @since 1.0.0
*
* @var string
*/
public $description = '';
/**
* Short description of the fields included with the template.
*
* @since 1.0.0
*
* @var string
*/
public $includes = '';
/**
* URL of the icon to display in the admin area.
*
* @since 1.0.0
*
* @var string
*/
public $icon = '';
/**
* Form template preview URL.
*
* @since 1.7.5.3
*
* @var string
*/
public $url = '';
/**
* Form template thumbnail url.
*
* @since 1.8.2
*
* @var string
*/
public $thumbnail = '';
/**
* Array of data that is assigned to the post_content on form creation.
*
* @since 1.0.0
*
* @var array
*/
public $data;
/**
* Priority to show in the list of available templates.
*
* @since 1.0.0
*
* @var int
*/
public $priority = 20;
/**
* Core or additional template.
*
* @since 1.4.0
*
* @var bool
*/
public $core = false;
/**
* Modal message to display when the template is applied.
*
* @since 1.0.0
*
* @var array
*/
public $modal = '';
/**
* Primary class constructor.
*
* @since 1.0.0
*/
public function __construct() {
// Bootstrap.
$this->init();
$type = $this->core ? '_core' : '';
add_filter( "wpforms_form_templates{$type}", [ $this, 'template_details' ], $this->priority );
add_filter( 'wpforms_create_form_args', [ $this, 'template_data' ], 10, 2 );
add_filter( 'wpforms_save_form_args', [ $this, 'template_replace' ], 10, 3 );
add_filter( 'wpforms_builder_template_active', [ $this, 'template_active' ], 10, 2 );
}
/**
* Let's get started.
*
* @since 1.0.0
*/
public function init() {}
/**
* Add basic template details to the Add New Form admin screen.
*
* @since 1.0.0
*
* @param array $templates Templates array.
*
* @return array
*/
public function template_details( $templates ) {
$templates[] = [
'name' => $this->name,
'slug' => $this->slug,
'source' => $this->source,
'categories' => $this->categories,
'description' => $this->description,
'includes' => $this->includes,
'icon' => $this->icon,
'url' => ! empty( $this->url ) ? $this->url : '',
'plugin_dir' => $this->get_plugin_dir(),
'thumbnail' => ! empty( $this->thumbnail ) ? $this->thumbnail : '',
];
return $templates;
}
/**
* Get the directory name of the plugin in which current template resides.
*
* @since 1.6.9
*
* @return string
*/
private function get_plugin_dir() {
$reflection = new \ReflectionClass( $this );
$template_file_path = wp_normalize_path( $reflection->getFileName() );
// Cutting out the WP_PLUGIN_DIR from the beginning of the template file path.
$template_file_path = preg_replace( '{^' . wp_slash( wp_normalize_path( WP_PLUGIN_DIR ) ) . '}', '', $template_file_path );
$template_file_chunks = explode( '/', $template_file_path );
return $template_file_chunks[1];
}
/**
* Add template data when form is created.
*
* @since 1.0.0
*
* @param array $args Create form arguments.
* @param array $data Template data.
*
* @return array
*/
public function template_data( $args, $data ) {
if ( ! empty( $data ) && ! empty( $data['template'] ) ) {
if ( $data['template'] === $this->slug ) {
// Enable Notifications by default.
$this->data['settings']['notification_enable'] = isset( $this->data['settings']['notification_enable'] )
? $this->data['settings']['notification_enable']
: 1;
$args['post_content'] = wpforms_encode( $this->data );
}
}
return $args;
}
/**
* Replace template on post update if triggered.
*
* @since 1.0.0
*
* @param array $form Form post data.
* @param array $data Form data.
* @param array $args Update form arguments.
*
* @return array
*/
public function template_replace( $form, $data, $args ) {
// We should proceed only if the template slug passed via $args['template'] is equal to the current template slug.
// This will work only for offline templates: Blank Form, all the Addons Templates, and all the custom templates.
// All the online (modern) templates use the hash as the identifier,
// and they are handled by `\WPForms\Admin\Builder\Templates::apply_to_existing_form()`.
if ( empty( $args['template'] ) || $args['template'] !== $this->slug ) {
return $form;
}
$form_data = wpforms_decode( wp_unslash( $form['post_content'] ) );
// Something is wrong with the form data.
if ( empty( $form_data ) ) {
return $form;
}
// Compile the new form data preserving needed data from the existing form.
$new = $this->data;
$new['id'] = isset( $form_data['id'] ) ? $form_data['id'] : 0;
$new['settings'] = isset( $form_data['settings'] ) ? $form_data['settings'] : [];
$new['payments'] = isset( $form_data['payments'] ) ? $form_data['payments'] : [];
$new['meta'] = isset( $form_data['meta'] ) ? $form_data['meta'] : [];
$new['meta']['template'] = isset( $this->data['meta']['template'] ) ? $this->data['meta']['template'] : '';
/**
* Allow modifying form data when a template is replaced.
*
* @since 1.7.9
*
* @param array $new Updated form data.
* @param array $form_data Current form data.
* @param array $template Template data.
*/
$new = (array) apply_filters( 'wpforms_templates_class_base_template_replace_modify_data', $new, $form_data, $this );
// Update the form with new data.
$form['post_content'] = wpforms_encode( $new );
return $form;
}
/**
* Pass information about the active template back to the builder.
*
* @since 1.0.0
*
* @param array $details Details.
* @param object $form Form data.
*
* @return array|void
*/
public function template_active( $details, $form ) {
if ( empty( $form ) ) {
return;
}
$form_data = wpforms_decode( $form->post_content );
if ( empty( $this->modal ) || empty( $form_data['meta']['template'] ) || $this->slug !== $form_data['meta']['template'] ) {
return $details;
} else {
$display = $this->template_modal_conditional( $form_data );
}
return [
'name' => $this->name,
'slug' => $this->slug,
'description' => $this->description,
'includes' => $this->includes,
'icon' => $this->icon,
'modal' => $this->modal,
'modal_display' => $display,
];
}
/**
* Conditional to determine if the template informational modal screens
* should display.
*
* @since 1.0.0
*
* @param array $form_data Form data and settings.
*
* @return bool
*/
public function template_modal_conditional( $form_data ) {
return false;
}
}

View File

@@ -0,0 +1,47 @@
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Blank form template.
*
* @since 1.0.0
*/
class WPForms_Template_Blank extends WPForms_Template {
/**
* Primary class constructor.
*
* @since 1.0.0
*/
public function init() {
$this->priority = 1;
$this->name = esc_html__( 'Blank Form', 'wpforms-lite' );
$this->slug = 'blank';
$this->source = 'wpforms-core';
$this->categories = 'all';
$this->description = esc_html__( 'The blank form allows you to create any type of form using our drag & drop builder.', 'wpforms-lite' );
$this->includes = '';
$this->icon = '';
$this->modal = '';
$this->core = true;
$this->data = [
'field_id' => '1',
'fields' => [],
'settings' => [
'antispam' => '1',
'ajax_submit' => '1',
'confirmation_message_scroll' => '1',
'submit_text_processing' => esc_html__( 'Sending...', 'wpforms-lite' ),
],
'meta' => [
'template' => $this->slug,
],
];
}
}
new WPForms_Template_Blank();

View File

@@ -0,0 +1,86 @@
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Simple Contact Form Template for WPForms.
*
* @since 1.7.5.3
*/
class WPForms_Template_Simple_Contact_Form extends WPForms_Template {
/**
* Primary class constructor.
*
* @since 1.7.5.3
*/
public function init() {
$this->name = esc_html__( 'Simple Contact Form', 'wpforms-lite' );
$this->priority = 1;
$this->source = 'wpforms-core';
$this->categories = 'all';
$this->core = true;
$this->slug = 'simple-contact-form-template';
$this->url = 'https://wpforms.com/templates/simple-contact-form-template/';
$this->description = esc_html__( 'Collect the names, emails, and messages from site visitors that need to talk to you.', 'wpforms-lite' );
$this->thumbnail = esc_url( WPFORMS_PLUGIN_URL . 'assets/images/thumbnail-simple-contact-form-template.jpg' );
$this->data = [
'fields' => [
'0' => [
'id' => '0',
'type' => 'name',
'format' => 'first-last',
'label' => esc_html__( 'Name', 'wpforms-lite' ),
'required' => '1',
'size' => 'medium',
],
'1' => [
'id' => '1',
'type' => 'email',
'label' => esc_html__( 'Email', 'wpforms-lite' ),
'required' => '1',
'size' => 'medium',
],
'2' => [
'id' => '2',
'type' => 'textarea',
'label' => esc_html__( 'Comment or Message', 'wpforms-lite' ),
'size' => 'medium',
'placeholder' => '',
'css' => '',
],
],
'field_id' => 3,
'settings' => [
'form_desc' => '',
'submit_text' => esc_html__( 'Submit', 'wpforms-lite' ),
'submit_text_processing' => esc_html__( 'Sending...', 'wpforms-lite' ),
'antispam' => '1',
'notification_enable' => '1',
'notifications' => [
'1' => [
'email' => '{admin_email}',
'replyto' => '{field_id="1"}',
'message' => '{all_fields}',
],
],
'confirmations' => [
'1' => [
'type' => 'message',
'message' => esc_html__( 'Thanks for contacting us! We will be in touch with you shortly.', 'wpforms-lite' ),
'message_scroll' => '1',
],
],
'ajax_submit' => '1',
],
'meta' => [
'template' => $this->slug,
],
];
}
}
new WPForms_Template_Simple_Contact_Form();