Merged in feature/MAW-855-import-code-into-aws (pull request #2)

code import from pantheon

* code import from pantheon
This commit is contained in:
Tony Volpe
2023-12-04 23:08:14 +00:00
parent 8c9b1312bc
commit 8f4b5efda6
4766 changed files with 185592 additions and 239967 deletions

11
wp/wp-config-local.php Normal file
View File

@@ -0,0 +1,11 @@
<?php
define( 'DB_NAME', 'medicalalert' );
define( 'DB_USER', 'root' );
define( 'DB_PASSWORD', 'root' );
define( 'DB_HOST', 'localhost:/Applications/MAMP/tmp/mysql/mysql.sock' );
define( 'DB_CHARSET', 'utf8' );
define( 'DB_COLLATE', '' );
$table_prefix = 'wp_xygy_';

View File

@@ -1,22 +1,10 @@
<?php
define( 'WP_CACHE', true ); // Added by WP Rocket
/**
* This config file is yours to hack on. It will work out of the box on Pantheon
* but you may find there are a lot of neat tricks to be used here.
*
* See our documentation for more details:
*
* https://pantheon.io/docs
*/
/**
* This block will be executed if you are NOT running on Pantheon and have NO
* wp-config-local.php. Insert alternate config here if necessary.
*
* If you are only running on Pantheon, you can ignore this block.
*/
if ( file_exists( dirname( __FILE__ ) . '/wp-config-local.php' ) ) {
include( dirname( __FILE__ ) . '/wp-config-local.php' );
define( 'WP_LOCAL_DEV', true );
} else {
// ------------------------------------
define('DB_NAME', $_SERVER["SUBDOMAIN"]);
@@ -38,6 +26,8 @@ define('AUTH_SALT', 'bc345fc63882f49dbba21248add8beb61b8e68d20ce5d4623de0
define('SECURE_AUTH_SALT', '03e361ac295e49d27bd47e6229558724bdf16e3ed9705efd2822c643c20ffdc0');
define('LOGGED_IN_SALT', '90d8f33cc74663986e2bbe86b5d11539180ea434974ea2a1e17eca04f4b54abc');
define('NONCE_SALT', '6640b7f1a18031eaf01b6e7dc3f445b6f582ab9922674c286c2b527c9c579dea');
}
$table_prefix = 'wp_xygy_';
/**

View File

@@ -1,76 +0,0 @@
<?php
/**
* Plugin Name: Pantheon MU Plugin Loader
* Description: Loads the MU plugins required to run the site
* Author: Pantheon Systems
* Author URI: https://pantheon.io
* Version: 1.0
*/
if ( defined( 'WP_INSTALLING' ) && WP_INSTALLING ) {
return;
}
// Add mu-plugins here.
$pantheon_mu_plugins = [
'pantheon-mu-plugin/pantheon.php',
];
foreach ( $pantheon_mu_plugins as $file ) {
require_once WPMU_PLUGIN_DIR . '/' . $file;
}
unset( $file );
add_action( 'pre_current_active_plugins', function () use ( $pantheon_mu_plugins ) {
global $plugins, $wp_list_table;
// Add our own mu-plugins to the page.
foreach ( $pantheon_mu_plugins as $plugin_file ) {
// Do not apply markup/translate as it'll be cached.
$plugin_data = get_plugin_data( WPMU_PLUGIN_DIR . "/$plugin_file", false, false );
if ( empty( $plugin_data['Name'] ) ) {
$plugin_data['Name'] = $plugin_file;
}
$plugins['mustuse'][ $plugin_file ] = $plugin_data;
}
// Recount totals.
$GLOBALS['totals']['mustuse'] = count( $plugins['mustuse'] );
// Only apply the rest if we're actually looking at the page.
if ( $GLOBALS['status'] !== 'mustuse' ) {
return;
}
// Reset the list table's data.
$wp_list_table->items = $plugins['mustuse'];
foreach ( $wp_list_table->items as $plugin_file => $plugin_data ) {
$wp_list_table->items[ $plugin_file ] = _get_plugin_data_markup_translate( $plugin_file, $plugin_data, false, true );
}
$total_this_page = $GLOBALS['totals']['mustuse'];
if ( $GLOBALS['orderby'] ) {
uasort( $wp_list_table->items, [ $wp_list_table, '_order_callback' ] );
}
// Force showing all plugins.
// See https://core.trac.wordpress.org/ticket/27110.
$plugins_per_page = $total_this_page;
$wp_list_table->set_pagination_args( [
'total_items' => $total_this_page,
'per_page' => $plugins_per_page,
] );
});
add_filter( 'network_admin_plugin_action_links', function ( $actions, $plugin_file, $plugin_data, $context ) use ( $pantheon_mu_plugins ) {
if ( $context !== 'mustuse' || ! in_array( $plugin_file, $pantheon_mu_plugins, true ) ) {
return $actions;
}
$actions[] = sprintf( '<span style="color:#333">File: <code>%s</code></span>', $plugin_file );
return $actions;
}, 10, 4 );

View File

@@ -1,3 +0,0 @@
# Ignore composer files.
/vendor
composer.lock

View File

@@ -1 +0,0 @@
* @pantheon-systems/cms-platform

View File

@@ -1,16 +0,0 @@
# editorconfig.org
root = true
[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
indent_style = tab
[{.jshintrc,*.json,*.yml}]
indent_style = space
indent_size = 2
[{*.txt,wp-config-sample.php}]
end_of_line = crlf

View File

@@ -1,63 +0,0 @@
#login {
width: 500px;
}
#return-to-pantheon {
margin-bottom: 20px;
display: flex;
justify-content: space-between;
align-items: center;
}
#return-to-pantheon .left {
font-size: 16px;
font-weight: 400;
line-height: 1.4;
}
#return-to-pantheon a{
display: flex;
align-items: center;
justify-content: space-between;
color: #000000;
padding: 5px 10px;
background: #F8DD44;
color: #000000 !important;
text-decoration: none !important;
font-size: 12px;
text-align: center;
border-radius: 10px;
min-width: 130px
}
#return-to-pantheon a:hover {
-webkit-transition: all 0.25s;
-moz-transition: all 0.25s;
transition: all 0.25s;
background: #EFD01B;
color: #333333 !important;
}
#return-to-pantheon .fist-icon {
display: inline-block;
width: 12px;
margin-right: 5px;
}
@media (max-width: 499px) {
#login {
width: 320px;
}
#return-to-pantheon {
flex-direction: column;
}
#return-to-pantheon .left {
order: 2;
}
#return-to-pantheon .right {
margin-bottom: 25px;
order: 1;
}
}

View File

@@ -1,17 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="14px" height="32px" viewBox="0 0 14 32" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 46.2 (44496) - http://www.bohemiancoding.com/sketch -->
<title>Group 11</title>
<desc>Created with Sketch.</desc>
<defs></defs>
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="WP---button" transform="translate(-322.000000, -538.000000)" fill="#333333">
<g id="Group" transform="translate(311.000000, 534.000000)">
<g id="Group-11" transform="translate(11.000000, 4.000000)">
<path d="M1.54822908,0 L4.52272957,6.90999702 L0.736842105,6.90999702 L1.98837032,9.82347541 L9.66224514,9.82347541 L1.54822908,0 L1.54822908,0 Z M11.589856,24.7575559 L10.3368651,21.8425037 L8.57849426,21.8425037 L8.54163298,21.7553741 L6.11083637,16.1102593 L4.6957728,16.1102593 L7.16386951,21.8425037 L2.66269769,21.8425037 L10.7780302,31.6679821 L7.80265209,24.7575559 L11.589856,24.7575559 L11.589856,24.7575559 Z" id="Fill-1"></path>
<path d="M6.3944081,15.35938 L5.16291955,12.493687 L4.54724842,12.493687 L5.77976089,15.3559463 L3.74741716,15.3559463 L6.2165378,21.0911952 L1.18058487,21.0911952 C0.756241333,21.0911952 0.522640287,21.0911952 0.330873868,20.4898718 C0.102099894,19.7718033 0,18.2748614 0,15.6365067 C0,12.9982951 0.102099894,11.5016393 0.330873868,10.7831416 C0.522640287,10.1818182 0.756241333,10.1818182 1.18058487,10.1818182 L6.73391219,10.1818182 L8.96270314,15.35938 L6.3944081,15.35938 Z M12.6233793,18.7798987 C12.7164101,18.7798987 12.9331895,18.892924 12.9331895,19.9363338 C12.9331895,20.9791714 12.7164101,21.0911952 12.6233793,21.0911952 L8.86294364,21.0911952 L7.86842046,18.7798987 L12.6233793,18.7798987 Z M12.6233793,12.493687 L8.3441065,12.493687 L7.34958333,10.1818182 L12.6233793,10.1818182 C12.7164101,10.1818182 12.9331895,10.2944143 12.9331895,11.3369657 C12.9331895,12.3800894 12.7164101,12.493687 12.6233793,12.493687 Z M13.063228,15.9142057 C13.1571364,15.9142057 13.3739159,16.0268018 13.3739159,17.0699255 C13.3739159,18.1130492 13.1571364,18.225073 13.063228,18.225073 L7.6291147,18.225073 L6.63415269,15.9142057 L13.063228,15.9142057 Z M13.063228,13.0485127 C13.1571364,13.0485127 13.3739159,13.1601073 13.3739159,14.2036602 C13.3739159,15.2463547 13.1571364,15.35938 13.063228,15.35938 L9.57837427,15.35938 L8.58297345,13.0485127 L13.063228,13.0485127 Z" id="Path"></path>
</g>
</g>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 2.5 KiB

View File

@@ -1,8 +0,0 @@
jQuery(document).ready(function( $ ) {
$('#return-to-pantheon')
.detach()
.prependTo('#loginform')
.show();
});

View File

@@ -1,96 +0,0 @@
<?php
/**
* WP-CLI commands for the Pantheon mu-plugin.
*
* @package pantheon
*/
namespace Pantheon\CLI;
use Pantheon_Cache;
use WP_CLI;
// Support the old pantheon-cache command but return a deprecation notice.
WP_CLI::add_command( 'pantheon-cache set-maintenance-mode', '\\Pantheon\\CLI\\__deprecated_maintenance_mode_output' );
WP_CLI::add_command( 'pantheon cache set-maintenance-mode', '\\Pantheon\\CLI\\set_maintenance_mode_command' );
WP_CLI::add_command( 'pantheon set-maintenance-mode', '\\Pantheon\\CLI\\set_maintenance_mode_command' );
/**
* Sets maintenance mode status.
*
* Enable maintenance mode to work on your site while serving cached pages
* to visitors and bots, or everyone except administators.
*
* ## DEPRECATION NOTICE
*
* This command is deprecated and will be removed in a future release.
* Use `pantheon set-maintenance-mode` instead.
*
* ## USAGE
*
* wp pantheon-cache set-maintenance-mode <status> (deprecated) or
* wp pantheon cache set-maintenance-mode <status>
*
* ## OPTIONS
*
* <status>
* : Maintenance mode status.
* ---
* options:
* - disabled
* - anonymous
* - everyone
* ---
*
* @subcommand set-maintenance-mode
*
* @deprecated 1.0.0
*/
function __deprecated_maintenance_mode_output( $args ) {
$allowed_args = [ 'disabled', 'anonymous', 'everyone' ];
$replacement_command = ( ! empty( $args && count( $args ) === 1 ) && in_array( $args[0], $allowed_args, true ) ) ? 'set-maintenance-mode ' . $args[0] : false;
WP_CLI::warning( WP_CLI::colorize( '%y' . sprintf( __( 'This command is deprecated and will be removed in a future release. Use `wp pantheon %s` instead.', 'pantheon-systems' ), $replacement_command ) . '%n' ) );
WP_CLI::line( __( 'Run `wp pantheon set-maintenance-mode --help` for more information.', 'pantheon-systems' ) );
// The command should fail before we get here, but in case it doesn't, display an error.
if ( false === $replacement_command ) {
WP_CLI::error( __( 'Invalid arguments. Run `wp pantheon set-maintenance-mode --help` for more infomation.', 'pantheon-systems' ) );
}
set_maintenance_mode_command( $args );
}
/**
* Sets maintenance mode status.
*
* Enable maintenance mode to work on your site while serving cached pages
* to visitors and bots, or everyone except administators.
*
* ## OPTIONS
*
* <status>
* : Maintenance mode status.
* ---
* options:
* - disabled
* - anonymous
* - everyone
* ---
*
* @subcommand set-maintenance-mode
*/
function set_maintenance_mode_command( $args ) {
list( $status ) = $args;
$out = Pantheon_Cache()->default_options;
if ( ! empty( $status )
&& in_array( $status, [ 'anonymous', 'everyone' ], true ) ) {
$out['maintenance_mode'] = $status;
} else {
$out['maintenance_mode'] = 'disabled';
}
update_option( Pantheon_Cache::SLUG, $out );
WP_CLI::success( sprintf( 'Maintenance mode set to: %s', $out['maintenance_mode'] ) );
}

View File

@@ -1,741 +0,0 @@
<?php
/**
* File mostly copied from wp-admin/includes/network.php.
*
* Changes:
* - Multisite instructions on step2
* - Text area rows increased
* - Allow altering config filename
* - Allow altering config contents
* - Remove if file_exists .htaccess
*/
/**
* Check for an existing network.
*
* @since 3.0.0
*
* @global wpdb $wpdb WordPress database abstraction object.
*
* @return string|false Base domain if network exists, otherwise false.
*/
function network_domain_check() {
global $wpdb;
$sql = $wpdb->prepare( 'SHOW TABLES LIKE %s', $wpdb->esc_like( $wpdb->site ) );
if ( $wpdb->get_var( $sql ) ) {
return $wpdb->get_var( "SELECT domain FROM $wpdb->site ORDER BY id ASC LIMIT 1" );
}
return false;
}
/**
* Allow subdomain installation
*
* @since 3.0.0
* @return bool Whether subdomain installation is allowed
*/
function allow_subdomain_install() {
$domain = preg_replace( '|https?://([^/]+)|', '$1', get_option( 'home' ) );
if ( parse_url( get_option( 'home' ), PHP_URL_PATH ) || 'localhost' === $domain || preg_match( '|^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$|', $domain ) ) {
return false;
}
return true;
}
/**
* Allow subdirectory installation.
*
* @since 3.0.0
*
* @global wpdb $wpdb WordPress database abstraction object.
*
* @return bool Whether subdirectory installation is allowed
*/
function allow_subdirectory_install() {
global $wpdb;
/**
* Filters whether to enable the subdirectory installation feature in Multisite.
*
* @since 3.0.0
*
* @param bool $allow Whether to enable the subdirectory installation feature in Multisite.
* Default false.
*/
if ( apply_filters( 'allow_subdirectory_install', false ) ) {
return true;
}
if ( defined( 'ALLOW_SUBDIRECTORY_INSTALL' ) && ALLOW_SUBDIRECTORY_INSTALL ) {
return true;
}
$post = $wpdb->get_row( "SELECT ID FROM $wpdb->posts WHERE post_date < DATE_SUB(NOW(), INTERVAL 1 MONTH) AND post_status = 'publish'" );
if ( empty( $post ) ) {
return true;
}
return false;
}
/**
* Get base domain of network.
*
* @since 3.0.0
* @return string Base domain.
*/
function get_clean_basedomain() {
$existing_domain = network_domain_check();
if ( $existing_domain ) {
return $existing_domain;
}
$domain = preg_replace( '|https?://|', '', get_option( 'siteurl' ) );
$slash = strpos( $domain, '/' );
if ( $slash ) {
$domain = substr( $domain, 0, $slash );
}
return $domain;
}
/**
* Prints step 1 for Network installation process.
*
* @todo Realistically, step 1 should be a welcome screen explaining what a Network is and such.
* Navigating to Tools > Network should not be a sudden "Welcome to a new install process!
* Fill this out and click here." See also contextual help todo.
*
* @since 3.0.0
*
* @global bool $is_apache
*
* @param false|WP_Error $errors Optional. Error object. Default false.
*/
function network_step1( $errors = false ) {
global $is_apache;
if ( defined( 'DO_NOT_UPGRADE_GLOBAL_TABLES' ) ) {
echo '<div class="error"><p><strong>' . __( 'Error:' ) . '</strong> ' . sprintf(
/* translators: %s: DO_NOT_UPGRADE_GLOBAL_TABLES */
__( 'The constant %s cannot be defined when creating a network.' ),
'<code>DO_NOT_UPGRADE_GLOBAL_TABLES</code>'
) . '</p></div>';
echo '</div>';
require_once ABSPATH . 'wp-admin/admin-footer.php';
die();
}
$active_plugins = get_option( 'active_plugins' );
if ( ! empty( $active_plugins ) ) {
echo '<div class="notice notice-warning"><p><strong>' . __( 'Warning:' ) . '</strong> ' . sprintf(
/* translators: %s: URL to Plugins screen. */
__( 'Please <a href="%s">deactivate your plugins</a> before enabling the Network feature.' ),
admin_url( 'plugins.php?plugin_status=active' )
) . '</p></div>';
echo '<p>' . __( 'Once the network is created, you may reactivate your plugins.' ) . '</p>';
echo '</div>';
require_once ABSPATH . 'wp-admin/admin-footer.php';
die();
}
$hostname = get_clean_basedomain();
$has_ports = strstr( $hostname, ':' );
if ( ( false !== $has_ports && ! in_array( $has_ports, array( ':80', ':443' ), true ) ) ) {
echo '<div class="error"><p><strong>' . __( 'Error:' ) . '</strong> ' . __( 'You cannot install a network of sites with your server address.' ) . '</p></div>';
echo '<p>' . sprintf(
/* translators: %s: Port number. */
__( 'You cannot use port numbers such as %s.' ),
'<code>' . $has_ports . '</code>'
) . '</p>';
echo '<a href="' . esc_url( admin_url() ) . '">' . __( 'Go to Dashboard' ) . '</a>';
echo '</div>';
require_once ABSPATH . 'wp-admin/admin-footer.php';
die();
}
echo '<form method="post">';
wp_nonce_field( 'install-network-1' );
$error_codes = array();
if ( is_wp_error( $errors ) ) {
echo '<div class="error"><p><strong>' . __( 'Error: The network could not be created.' ) . '</strong></p>';
foreach ( $errors->get_error_messages() as $error ) {
echo "<p>$error</p>";
}
echo '</div>';
$error_codes = $errors->get_error_codes();
}
if ( ! empty( $_POST['sitename'] ) && ! in_array( 'empty_sitename', $error_codes, true ) ) {
$site_name = $_POST['sitename'];
} else {
/* translators: %s: Default network title. */
$site_name = sprintf( __( '%s Sites' ), get_option( 'blogname' ) );
}
if ( ! empty( $_POST['email'] ) && ! in_array( 'invalid_email', $error_codes, true ) ) {
$admin_email = $_POST['email'];
} else {
$admin_email = get_option( 'admin_email' );
}
?>
<p><?php _e( 'Welcome to the Network installation process!' ); ?></p>
<p><?php _e( 'Fill in the information below and you&#8217;ll be on your way to creating a network of WordPress sites. Configuration files will be created in the next step.' ); ?></p>
<?php
if ( isset( $_POST['subdomain_install'] ) ) {
$subdomain_install = (bool) $_POST['subdomain_install'];
} elseif ( apache_mod_loaded( 'mod_rewrite' ) ) { // Assume nothing.
$subdomain_install = true;
} elseif ( ! allow_subdirectory_install() ) {
$subdomain_install = true;
} else {
$subdomain_install = false;
$got_mod_rewrite = got_mod_rewrite();
if ( $got_mod_rewrite ) { // Dangerous assumptions.
echo '<div class="updated inline"><p><strong>' . __( 'Note:' ) . '</strong> ';
printf(
/* translators: %s: mod_rewrite */
__( 'Please make sure the Apache %s module is installed as it will be used at the end of this installation.' ),
'<code>mod_rewrite</code>'
);
echo '</p>';
} elseif ( $is_apache ) {
echo '<div class="error inline"><p><strong>' . __( 'Warning:' ) . '</strong> ';
printf(
/* translators: %s: mod_rewrite */
__( 'It looks like the Apache %s module is not installed.' ),
'<code>mod_rewrite</code>'
);
echo '</p>';
}
if ( $got_mod_rewrite || $is_apache ) { // Protect against mod_rewrite mimicry (but ! Apache).
echo '<p>';
printf(
/* translators: 1: mod_rewrite, 2: mod_rewrite documentation URL, 3: Google search for mod_rewrite. */
__( 'If %1$s is disabled, ask your administrator to enable that module, or look at the <a href="%2$s">Apache documentation</a> or <a href="%3$s">elsewhere</a> for help setting it up.' ),
'<code>mod_rewrite</code>',
'https://httpd.apache.org/docs/mod/mod_rewrite.html',
'https://www.google.com/search?q=apache+mod_rewrite'
);
echo '</p></div>';
}
}
if ( allow_subdomain_install() && allow_subdirectory_install() ) :
?>
<h3><?php esc_html_e( 'Addresses of Sites in your Network' ); ?></h3>
<p><?php _e( 'Please choose whether you would like sites in your WordPress network to use sub-domains or sub-directories.' ); ?>
<strong><?php _e( 'You cannot change this later.' ); ?></strong></p>
<p><?php _e( 'You will need a wildcard DNS record if you are going to use the virtual host (sub-domain) functionality.' ); ?></p>
<?php // @todo Link to an MS readme? ?>
<table class="form-table" role="presentation">
<tr>
<th><label><input type="radio" name="subdomain_install" value="1"<?php checked( $subdomain_install ); ?> /> <?php _e( 'Sub-domains' ); ?></label></th>
<td>
<?php
printf(
/* translators: 1: Host name. */
_x( 'like <code>site1.%1$s</code> and <code>site2.%1$s</code>', 'subdomain examples' ),
$hostname
);
?>
</td>
</tr>
<tr>
<th><label><input type="radio" name="subdomain_install" value="0"<?php checked( ! $subdomain_install ); ?> /> <?php _e( 'Sub-directories' ); ?></label></th>
<td>
<?php
printf(
/* translators: 1: Host name. */
_x( 'like <code>%1$s/site1</code> and <code>%1$s/site2</code>', 'subdirectory examples' ),
$hostname
);
?>
</td>
</tr>
</table>
<?php
endif;
if ( WP_CONTENT_DIR !== ABSPATH . 'wp-content' && ( allow_subdirectory_install() || ! allow_subdomain_install() ) ) {
echo '<div class="error inline"><p><strong>' . __( 'Warning:' ) . '</strong> ' . __( 'Subdirectory networks may not be fully compatible with custom wp-content directories.' ) . '</p></div>';
}
$is_www = ( 0 === strpos( $hostname, 'www.' ) );
if ( $is_www ) :
?>
<h3><?php esc_html_e( 'Server Address' ); ?></h3>
<p>
<?php
printf(
/* translators: 1: Site URL, 2: Host name, 3: www. */
__( 'You should consider changing your site domain to %1$s before enabling the network feature. It will still be possible to visit your site using the %3$s prefix with an address like %2$s but any links will not have the %3$s prefix.' ),
'<code>' . substr( $hostname, 4 ) . '</code>',
'<code>' . $hostname . '</code>',
'<code>www</code>'
);
?>
</p>
<table class="form-table" role="presentation">
<tr>
<th scope='row'><?php esc_html_e( 'Server Address' ); ?></th>
<td>
<?php
printf(
/* translators: %s: Host name. */
__( 'The internet address of your network will be %s.' ),
'<code>' . $hostname . '</code>'
);
?>
</td>
</tr>
</table>
<?php endif; ?>
<h3><?php esc_html_e( 'Network Details' ); ?></h3>
<table class="form-table" role="presentation">
<?php if ( 'localhost' === $hostname ) : ?>
<tr>
<th scope="row"><?php esc_html_e( 'Sub-directory Installation' ); ?></th>
<td>
<?php
printf(
/* translators: 1: localhost, 2: localhost.localdomain */
__( 'Because you are using %1$s, the sites in your WordPress network must use sub-directories. Consider using %2$s if you wish to use sub-domains.' ),
'<code>localhost</code>',
'<code>localhost.localdomain</code>'
);
// Uh oh:
if ( ! allow_subdirectory_install() ) {
echo ' <strong>' . __( 'Warning:' ) . ' ' . __( 'The main site in a sub-directory installation will need to use a modified permalink structure, potentially breaking existing links.' ) . '</strong>';
}
?>
</td>
</tr>
<?php elseif ( ! allow_subdomain_install() ) : ?>
<tr>
<th scope="row"><?php esc_html_e( 'Sub-directory Installation' ); ?></th>
<td>
<?php
_e( 'Because your installation is in a directory, the sites in your WordPress network must use sub-directories.' );
// Uh oh:
if ( ! allow_subdirectory_install() ) {
echo ' <strong>' . __( 'Warning:' ) . ' ' . __( 'The main site in a sub-directory installation will need to use a modified permalink structure, potentially breaking existing links.' ) . '</strong>';
}
?>
</td>
</tr>
<?php elseif ( ! allow_subdirectory_install() ) : ?>
<tr>
<th scope="row"><?php esc_html_e( 'Sub-domain Installation' ); ?></th>
<td>
<?php
_e( 'Because your installation is not new, the sites in your WordPress network must use sub-domains.' );
echo ' <strong>' . __( 'The main site in a sub-directory installation will need to use a modified permalink structure, potentially breaking existing links.' ) . '</strong>';
?>
</td>
</tr>
<?php endif; ?>
<?php if ( ! $is_www ) : ?>
<tr>
<th scope='row'><?php esc_html_e( 'Server Address' ); ?></th>
<td>
<?php
printf(
/* translators: %s: Host name. */
__( 'The internet address of your network will be %s.' ),
'<code>' . $hostname . '</code>'
);
?>
</td>
</tr>
<?php endif; ?>
<tr>
<th scope='row'><label for="sitename"><?php esc_html_e( 'Network Title' ); ?></label></th>
<td>
<input name='sitename' id='sitename' type='text' size='45' value='<?php echo esc_attr( $site_name ); ?>' />
<p class="description">
<?php _e( 'What would you like to call your network?' ); ?>
</p>
</td>
</tr>
<tr>
<th scope='row'><label for="email"><?php esc_html_e( 'Network Admin Email' ); ?></label></th>
<td>
<input name='email' id='email' type='text' size='45' value='<?php echo esc_attr( $admin_email ); ?>' />
<p class="description">
<?php _e( 'Your email address.' ); ?>
</p>
</td>
</tr>
</table>
<?php submit_button( __( 'Install' ), 'primary', 'submit' ); ?>
</form>
<?php
}
/**
* Prints step 2 for Network installation process.
*
* @since 3.0.0
*
* @global wpdb $wpdb WordPress database abstraction object.
* @global bool $is_nginx Whether the server software is Nginx or something else.
*
* @param false|WP_Error $errors Optional. Error object. Default false.
*/
function network_step2( $errors = false ) {
global $wpdb, $is_nginx;
$hostname = get_clean_basedomain();
$slashed_home = trailingslashit( get_option( 'home' ) );
$base = parse_url( $slashed_home, PHP_URL_PATH );
$document_root_fix = str_replace( '\\', '/', realpath( $_SERVER['DOCUMENT_ROOT'] ) );
$abspath_fix = str_replace( '\\', '/', ABSPATH );
$home_path = 0 === strpos( $abspath_fix, $document_root_fix ) ? $document_root_fix . $base : get_home_path();
$wp_siteurl_subdir = preg_replace( '#^' . preg_quote( $home_path, '#' ) . '#', '', $abspath_fix );
$rewrite_base = ! empty( $wp_siteurl_subdir ) ? ltrim( trailingslashit( $wp_siteurl_subdir ), '/' ) : '';
$config_filename = 'wp-config.php';
$config_filename = apply_filters( 'pantheon.multisite.config_filename', 'wp-config.php' );
$location_of_wp_config = $abspath_fix;
if ( ! file_exists( ABSPATH . $config_filename ) && file_exists( dirname( ABSPATH ) . '/' . $config_filename ) ) {
$location_of_wp_config = dirname( $abspath_fix );
}
$location_of_wp_config = trailingslashit( $location_of_wp_config );
// Wildcard DNS message.
if ( is_wp_error( $errors ) ) {
echo '<div class="error">' . $errors->get_error_message() . '</div>';
}
if ( $_POST ) {
if ( allow_subdomain_install() ) {
$subdomain_install = allow_subdirectory_install() ? ! empty( $_POST['subdomain_install'] ) : true;
} else {
$subdomain_install = false;
}
} else {
if ( is_multisite() ) {
$subdomain_install = is_subdomain_install();
?>
<p><?php _e( 'The original configuration steps are shown here for reference.' ); ?></p>
<?php
} else {
$subdomain_install = (bool) $wpdb->get_var( "SELECT meta_value FROM $wpdb->sitemeta WHERE site_id = 1 AND meta_key = 'subdomain_install'" );
?>
<div class="error"><p><strong><?php _e( 'Warning:' ); ?></strong> <?php _e( 'An existing WordPress network was detected.' ); ?></p></div>
<p><?php _e( 'Please complete the configuration steps. To create a new network, you will need to empty or remove the network database tables.' ); ?></p>
<?php
}
}
$subdir_match = $subdomain_install ? '' : '([_0-9a-zA-Z-]+/)?';
$subdir_replacement_01 = $subdomain_install ? '' : '$1';
$subdir_replacement_12 = $subdomain_install ? '$1' : '$2';
if ( $_POST || ! is_multisite() ) {
?>
<h3><?php esc_html_e( 'Enabling the Network' ); ?></h3>
<p><?php _e( 'Complete the following steps to enable the features for creating a network of sites.' ); ?></p>
<div class="notice notice-warning inline"><p>
<?php
if ( file_exists( $home_path . 'web.config' ) ) {
echo '<strong>' . __( 'Caution:' ) . '</strong> ';
printf(
/* translators: 1: wp-config.php, 2: web.config */
__( 'You should back up your existing %1$s and %2$s files.' ),
'<code>' . $config_filename . '</code>',
'<code>web.config</code>'
);
} else {
echo '<strong>' . __( 'Caution:' ) . '</strong> ';
printf(
/* translators: %s: wp-config.php */
__( 'You should back up your existing %s file.' ),
'<code>' . $config_filename . '</code>',
);
}
?>
</p></div>
<?php
}
?>
<ol>
<li><p id="network-wpconfig-rules-description">
<?php
printf(
/* translators: 1: wp-config.php, 2: Location of wp-config file, 3: Translated version of "That's all, stop editing! Happy publishing." */
__( 'Add the following to your %1$s file in %2$s <strong>above</strong> the line reading %3$s:' ),
'<code>' . $config_filename . '</code>',
'<code>' . $location_of_wp_config . '</code>',
/*
* translators: This string should only be translated if wp-config-sample.php is localized.
* You can check the localized release package or
* https://i18n.svn.wordpress.org/<locale code>/branches/<wp version>/dist/wp-config-sample.php
*/
'<code>/* ' . __( 'That&#8217;s all, stop editing! Happy publishing.' ) . ' */</code>'
);
?>
</p>
<p class="configuration-rules-label"><label for="network-wpconfig-rules">
<?php
printf(
/* translators: %s: File name (wp-config.php, .htaccess or web.config). */
__( 'Network configuration rules for %s' ),
'<code>' . $config_filename . '</code>'
);
?>
</label></p>
<textarea id="network-wpconfig-rules" class="code" readonly="readonly" cols="100" rows="31" aria-describedby="network-wpconfig-rules-description">
<?php ob_start(); ?>
if ( !empty( $_ENV['PANTHEON_ENVIRONMENT'] )) {
$site_name = $_ENV['PANTHEON_SITE_NAME'];
// Override $hostname value as needed.
switch ( $_ENV['PANTHEON_ENVIRONMENT'] ) {
case 'live':
$hostname = $_SERVER['HTTP_HOST'];
break;
case 'test':
$hostname = 'test-' . $site_name . '.pantheonsite.io';
break;
case 'dev':
$hostname = 'dev-' . $site_name . '.pantheonsite.io';
break;
case 'lando':
$hostname = $site_name . '.lndo.site';
break;
default:
$hostname = $_ENV['PANTHEON_ENVIRONMENT'] . '-' . $site_name . '.pantheonsite.io';
break;
}
} else {
// Override with a default hostname.
$hostname = '<?php echo $hostname ?>';
}
define( 'MULTISITE', true );
define( 'SUBDOMAIN_INSTALL', <?php echo $subdomain_install ? 'true' : 'false'; ?> );
define( 'DOMAIN_CURRENT_SITE', $hostname );
define( 'PATH_CURRENT_SITE', '<?php echo $base; ?>' );
define( 'SITE_ID_CURRENT_SITE', 1 );
define( 'BLOG_ID_CURRENT_SITE', 1 );
<?php
$config_file_contents = ob_get_contents();
ob_end_clean();
$config_file_contents = apply_filters( 'pantheon.multisite.config_contents', $config_file_contents );
echo $config_file_contents;
?>
</textarea>
<?php
$keys_salts = array(
'AUTH_KEY' => '',
'SECURE_AUTH_KEY' => '',
'LOGGED_IN_KEY' => '',
'NONCE_KEY' => '',
'AUTH_SALT' => '',
'SECURE_AUTH_SALT' => '',
'LOGGED_IN_SALT' => '',
'NONCE_SALT' => '',
);
foreach ( $keys_salts as $c => $v ) {
if ( defined( $c ) ) {
unset( $keys_salts[ $c ] );
}
}
if ( ! empty( $keys_salts ) ) {
$keys_salts_str = '';
$from_api = wp_remote_get( 'https://api.wordpress.org/secret-key/1.1/salt/' );
if ( is_wp_error( $from_api ) ) {
foreach ( $keys_salts as $c => $v ) {
$keys_salts_str .= "\ndefine( '$c', '" . wp_generate_password( 64, true, true ) . "' );";
}
} else {
$from_api = explode( "\n", wp_remote_retrieve_body( $from_api ) );
foreach ( $keys_salts as $c => $v ) {
$keys_salts_str .= "\ndefine( '$c', '" . substr( array_shift( $from_api ), 28, 64 ) . "' );";
}
}
$num_keys_salts = count( $keys_salts );
?>
<p id="network-wpconfig-authentication-description">
<?php
if ( 1 === $num_keys_salts ) {
printf(
/* translators: %s: wp-config.php */
__( 'This unique authentication key is also missing from your %s file.' ),
'<code>' . $config_filename . '</code>',
);
} else {
printf(
/* translators: %s: wp-config.php */
__( 'These unique authentication keys are also missing from your %s file.' ),
'<code>' . $config_filename . '</code>',
);
}
?>
<?php _e( 'To make your installation more secure, you should also add:' ); ?>
</p>
<p class="configuration-rules-label"><label for="network-wpconfig-authentication"><?php _e( 'Network configuration authentication keys' ); ?></label></p>
<textarea id="network-wpconfig-authentication" class="code" readonly="readonly" cols="100" rows="<?php echo $num_keys_salts; ?>" aria-describedby="network-wpconfig-authentication-description"><?php echo esc_textarea( $keys_salts_str ); ?></textarea>
<?php
}
?>
</li>
<?php
if ( iis7_supports_permalinks() ) :
// IIS doesn't support RewriteBase, all your RewriteBase are belong to us.
$iis_subdir_match = ltrim( $base, '/' ) . $subdir_match;
$iis_rewrite_base = ltrim( $base, '/' ) . $rewrite_base;
$iis_subdir_replacement = $subdomain_install ? '' : '{R:1}';
$web_config_file = '<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.webServer>
<rewrite>
<rules>
<rule name="WordPress Rule 1" stopProcessing="true">
<match url="^index\.php$" ignoreCase="false" />
<action type="None" />
</rule>';
if ( is_multisite() && get_site_option( 'ms_files_rewriting' ) ) {
$web_config_file .= '
<rule name="WordPress Rule for Files" stopProcessing="true">
<match url="^' . $iis_subdir_match . 'files/(.+)" ignoreCase="false" />
<action type="Rewrite" url="' . $iis_rewrite_base . WPINC . '/ms-files.php?file={R:1}" appendQueryString="false" />
</rule>';
}
$web_config_file .= '
<rule name="WordPress Rule 2" stopProcessing="true">
<match url="^' . $iis_subdir_match . 'wp-admin$" ignoreCase="false" />
<action type="Redirect" url="' . $iis_subdir_replacement . 'wp-admin/" redirectType="Permanent" />
</rule>
<rule name="WordPress Rule 3" stopProcessing="true">
<match url="^" ignoreCase="false" />
<conditions logicalGrouping="MatchAny">
<add input="{REQUEST_FILENAME}" matchType="IsFile" ignoreCase="false" />
<add input="{REQUEST_FILENAME}" matchType="IsDirectory" ignoreCase="false" />
</conditions>
<action type="None" />
</rule>
<rule name="WordPress Rule 4" stopProcessing="true">
<match url="^' . $iis_subdir_match . '(wp-(content|admin|includes).*)" ignoreCase="false" />
<action type="Rewrite" url="' . $iis_rewrite_base . '{R:1}" />
</rule>
<rule name="WordPress Rule 5" stopProcessing="true">
<match url="^' . $iis_subdir_match . '([_0-9a-zA-Z-]+/)?(.*\.php)$" ignoreCase="false" />
<action type="Rewrite" url="' . $iis_rewrite_base . '{R:2}" />
</rule>
<rule name="WordPress Rule 6" stopProcessing="true">
<match url="." ignoreCase="false" />
<action type="Rewrite" url="index.php" />
</rule>
</rules>
</rewrite>
</system.webServer>
</configuration>
';
echo '<li><p id="network-webconfig-rules-description">';
printf(
/* translators: 1: File name (.htaccess or web.config), 2: File path. */
__( 'Add the following to your %1$s file in %2$s, <strong>replacing</strong> other WordPress rules:' ),
'<code>web.config</code>',
'<code>' . $home_path . '</code>'
);
echo '</p>';
if ( ! $subdomain_install && WP_CONTENT_DIR !== ABSPATH . 'wp-content' ) {
echo '<p><strong>' . __( 'Warning:' ) . ' ' . __( 'Subdirectory networks may not be fully compatible with custom wp-content directories.' ) . '</strong></p>';
}
?>
<p class="configuration-rules-label"><label for="network-webconfig-rules">
<?php
printf(
/* translators: %s: File name (wp-config.php, .htaccess or web.config). */
__( 'Network configuration rules for %s' ),
'<code>web.config</code>'
);
?>
</label></p>
<textarea id="network-webconfig-rules" class="code" readonly="readonly" cols="100" rows="20" aria-describedby="network-webconfig-rules-description"><?php echo esc_textarea( $web_config_file ); ?></textarea>
</li>
</ol>
<?php
elseif ( $is_nginx ) : // End iis7_supports_permalinks(). Link to Nginx documentation instead:
echo '<li><p>';
printf(
/* translators: %s: Documentation URL. */
__( 'It seems your network is running with Nginx web server. <a href="%s">Learn more about further configuration</a>.' ),
__( 'https://wordpress.org/support/article/nginx/' )
);
echo '</p></li>';
else : // End $is_nginx. Construct an .htaccess file instead:
$ms_files_rewriting = '';
if ( is_multisite() && get_site_option( 'ms_files_rewriting' ) ) {
$ms_files_rewriting = "\n# uploaded files\nRewriteRule ^";
$ms_files_rewriting .= $subdir_match . "files/(.+) {$rewrite_base}" . WPINC . "/ms-files.php?file={$subdir_replacement_12} [L]" . "\n";
}
$htaccess_file = <<<EOF
RewriteEngine On
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
RewriteBase {$base}
RewriteRule ^index\.php$ - [L]
{$ms_files_rewriting}
# add a trailing slash to /wp-admin
RewriteRule ^{$subdir_match}wp-admin$ {$subdir_replacement_01}wp-admin/ [R=301,L]
RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^ - [L]
RewriteRule ^{$subdir_match}(wp-(content|admin|includes).*) {$rewrite_base}{$subdir_replacement_12} [L]
RewriteRule ^{$subdir_match}(.*\.php)$ {$rewrite_base}$subdir_replacement_12 [L]
RewriteRule . index.php [L]
EOF;
echo '<li><p id="network-htaccess-rules-description">';
printf(
/* translators: 1: File name (.htaccess or web.config), 2: File path. */
__( 'Add the following to your %1$s file in %2$s, <strong>replacing</strong> other WordPress rules:' ),
'<code>.htaccess</code>',
'<code>' . $home_path . '</code>'
);
echo '</p>';
if ( ! $subdomain_install && WP_CONTENT_DIR !== ABSPATH . 'wp-content' ) {
echo '<p><strong>' . __( 'Warning:' ) . ' ' . __( 'Subdirectory networks may not be fully compatible with custom wp-content directories.' ) . '</strong></p>';
}
?>
<p class="configuration-rules-label"><label for="network-htaccess-rules">
<?php
printf(
/* translators: %s: File name (wp-config.php, .htaccess or web.config). */
__( 'Network configuration rules for %s' ),
'<code>.htaccess</code>'
);
?>
</label></p>
<textarea id="network-htaccess-rules" class="code" readonly="readonly" cols="100" rows="<?php echo substr_count( $htaccess_file, "\n" ) + 1; ?>" aria-describedby="network-htaccess-rules-description"><?php echo esc_textarea( $htaccess_file ); ?></textarea>
</li>
</ol>
<?php
endif; // End IIS/Nginx/Apache code branches.
if ( ! is_multisite() ) {
?>
<p><?php _e( 'Once you complete these steps, your network is enabled and configured. You will have to log in again.' ); ?> <a href="<?php echo esc_url( wp_login_url() ); ?>"><?php _e( 'Log In' ); ?></a></p>
<?php
}
}

View File

@@ -1,120 +0,0 @@
<?php
/**
* File mostly copied from wp-admin/network.php.
*
* Changes:
* - s/__DIR__.'\//ABSPATH . 'wp-admin\//g
* - s/ABSPATH . 'wp-admin\/includes\/network.php/__DIR__ . '\/includes-network.php/g'
*/
define( 'WP_INSTALLING_NETWORK', true );
/** WordPress Administration Bootstrap */
require_once ABSPATH . 'wp-admin/admin.php';
if ( ! current_user_can( 'setup_network' ) ) {
wp_die( __( 'Sorry, you are not allowed to manage options for this site.' ) );
}
if ( is_multisite() ) {
if ( ! is_network_admin() ) {
wp_redirect( network_admin_url( 'setup.php' ) );
exit;
}
if ( ! defined( 'MULTISITE' ) ) {
wp_die( __( 'The Network creation panel is not for WordPress MU networks.' ) );
}
}
require_once __DIR__ . '/includes-network.php';
// We need to create references to ms global tables to enable Network.
foreach ( $wpdb->tables( 'ms_global' ) as $table => $prefixed_table ) {
$wpdb->$table = $prefixed_table;
}
if ( ! network_domain_check() && ( ! defined( 'WP_ALLOW_MULTISITE' ) || ! WP_ALLOW_MULTISITE ) ) {
wp_die(
printf(
/* translators: 1: WP_ALLOW_MULTISITE, 2: wp-config.php */
__( 'You must define the %1$s constant as true in your %2$s file to allow creation of a Network.' ),
'<code>WP_ALLOW_MULTISITE</code>',
'<code>wp-config.php</code>'
)
);
}
if ( is_network_admin() ) {
// Used in the HTML title tag.
$title = __( 'Network Setup' );
$parent_file = 'settings.php';
} else {
// Used in the HTML title tag.
$title = __( 'Create a Network of WordPress Sites' );
$parent_file = 'tools.php';
}
$network_help = '<p>' . __( 'This screen allows you to configure a network as having subdomains (<code>site1.example.com</code>) or subdirectories (<code>example.com/site1</code>). Subdomains require wildcard subdomains to be enabled in Apache and DNS records, if your host allows it.' ) . '</p>' .
'<p>' . __( 'Choose subdomains or subdirectories; this can only be switched afterwards by reconfiguring your installation. Fill out the network details, and click Install. If this does not work, you may have to add a wildcard DNS record (for subdomains) or change to another setting in Permalinks (for subdirectories).' ) . '</p>' .
'<p>' . __( 'The next screen for Network Setup will give you individually-generated lines of code to add to your wp-config.php and .htaccess files. Make sure the settings of your FTP client make files starting with a dot visible, so that you can find .htaccess; you may have to create this file if it really is not there. Make backup copies of those two files.' ) . '</p>' .
'<p>' . __( 'Add the designated lines of code to wp-config.php (just before <code>/*...stop editing...*/</code>) and <code>.htaccess</code> (replacing the existing WordPress rules).' ) . '</p>' .
'<p>' . __( 'Once you add this code and refresh your browser, multisite should be enabled. This screen, now in the Network Admin navigation menu, will keep an archive of the added code. You can toggle between Network Admin and Site Admin by clicking on the Network Admin or an individual site name under the My Sites dropdown in the Toolbar.' ) . '</p>' .
'<p>' . __( 'The choice of subdirectory sites is disabled if this setup is more than a month old because of permalink problems with &#8220;/blog/&#8221; from the main site. This disabling will be addressed in a future version.' ) . '</p>' .
'<p><strong>' . __( 'For more information:' ) . '</strong></p>' .
'<p>' . __( '<a href="https://wordpress.org/support/article/create-a-network/">Documentation on Creating a Network</a>' ) . '</p>' .
'<p>' . __( '<a href="https://wordpress.org/support/article/tools-network-screen/">Documentation on the Network Screen</a>' ) . '</p>';
get_current_screen()->add_help_tab(
array(
'id' => 'network',
'title' => __( 'Network' ),
'content' => $network_help,
)
);
get_current_screen()->set_help_sidebar(
'<p><strong>' . __( 'For more information:' ) . '</strong></p>' .
'<p>' . __( '<a href="https://wordpress.org/support/article/create-a-network/">Documentation on Creating a Network</a>' ) . '</p>' .
'<p>' . __( '<a href="https://wordpress.org/support/article/tools-network-screen/">Documentation on the Network Screen</a>' ) . '</p>' .
'<p>' . __( '<a href="https://wordpress.org/support/">Support</a>' ) . '</p>'
);
require_once ABSPATH . 'wp-admin/admin-header.php';
?>
<div class="wrap">
<h1><?php echo esc_html( $title ); ?></h1>
<?php
if ( $_POST ) {
check_admin_referer( 'install-network-1' );
require_once ABSPATH . 'wp-admin/includes/upgrade.php';
// Create network tables.
install_network();
$base = parse_url( trailingslashit( get_option( 'home' ) ), PHP_URL_PATH );
$subdomain_install = allow_subdomain_install() ? ! empty( $_POST['subdomain_install'] ) : false;
if ( ! network_domain_check() ) {
$result = populate_network( 1, get_clean_basedomain(), sanitize_email( $_POST['email'] ), wp_unslash( $_POST['sitename'] ), $base, $subdomain_install );
if ( is_wp_error( $result ) ) {
if ( 1 === count( $result->get_error_codes() ) && 'no_wildcard_dns' === $result->get_error_code() ) {
network_step2( $result );
} else {
network_step1( $result );
}
} else {
network_step2();
}
} else {
network_step2();
}
} elseif ( is_multisite() || network_domain_check() ) {
network_step2();
} else {
network_step1();
}
?>
</div>
<?php require_once ABSPATH . 'wp-admin/admin-footer.php'; ?>

View File

@@ -1,72 +0,0 @@
<?php
/**
* Modify the WordPress login form for Pantheon
*/
/**
* Should we proceed with adding the return to Pantheon button?
*
* Only if we are on a Pantheon subdomain
*/
$show_return_to_pantheon_button = apply_filters( 'show_return_to_pantheon_button', (
(
false !== stripos( get_site_url(), 'pantheonsite.io') ||
( isset( $_SERVER['HTTP_HOST'] ) && false !== stripos( $_SERVER['HTTP_HOST'], 'pantheonsite.io') )
)
) );
if( $show_return_to_pantheon_button ){
/**
* Enqueue Pantheon login styles
*
* @return void
*/
function Pantheon_Enqueue_Login_style()
{
wp_enqueue_style('pantheon-login-mods', plugin_dir_url(__FILE__) . 'assets/css/return-to-pantheon-button.css', false);
}
add_action('login_enqueue_scripts', 'Pantheon_Enqueue_Login_style', 10);
/**
* Enqueue Pantheon login scripts
*
* @return void
*/
function Pantheon_Enqueue_Login_script()
{
wp_enqueue_script('pantheon-login-mods', plugin_dir_url(__FILE__) . 'assets/js/return-to-pantheon-button.js', array('jquery'), false, true);
}
add_action('login_enqueue_scripts', 'Pantheon_Enqueue_Login_script', 1);
/**
* Print return to Pantheon link HTML
*
* @return void
*/
function Return_To_Pantheon_Button_HTML()
{
$pantheon_dashboard_url = 'https://dashboard.pantheon.io/sites/' . $_ENV['PANTHEON_SITE'] . '#' . $_ENV['PANTHEON_ENVIRONMENT'];
$pantheon_fist_icon_url = plugin_dir_url(__FILE__) . 'assets/images/pantheon-fist-icon-black.svg';
$login_message = apply_filters( 'pantheon_wp_login_text', __('Login to your WordPress Site', 'pantheon') );
?>
<div id="return-to-pantheon" style="display: none;">
<div class="left">
<?php echo $login_message; ?>
</div>
<div class="right">
<a href="<?php echo esc_url( $pantheon_dashboard_url ); ?>">
<img class="fist-icon" src="<?php echo esc_url( $pantheon_fist_icon_url ); ?>">
<?php _e('Return to Pantheon', 'pantheon'); ?>
</a>
</div>
</div>
<?php
}
add_action('login_header', 'Return_To_Pantheon_Button_HTML', 10);
}

View File

@@ -1,37 +0,0 @@
<?php
/**
* If a site has multisite enabled, but has not had the final installation
* steps completed, alert the user and provide links.
*
* @package pantheon
*/
/**
* Detects if a user is using the correct upstream and framework and give them appropriate next steps to finalize WPMS setup.
*
* @return void
*/
function pantheon_multisite_install_finalize_message() { ?>
<div class="notice notice-info is-dismissible">
<?php
if ( isset( $_ENV['PANTHEON_ENVIRONMENT'] ) ) {
if ( getenv( 'FRAMEWORK' ) === 'wordpress_network' ) {
?>
<p><?php esc_html_e( 'Your WordPress Multisite is almost ready!', 'pantheon' ); ?></p>
<p><?php echo sprintf( __( 'Visit <a href="%s">Pantheon Multisite Configuration</a> for documentation on how to finalize configuration of your site network.', 'pantheon' ), 'https://pantheon.io/docs/guides/multisite/config/#install-the-wordpress-site-network' ); ?></p>
<?php
} else {
?>
<p><?php esc_html_e( 'You are trying to configure a WordPress Multisite with a wrong upstream!', 'pantheon' ); ?></p>
<p><?php echo sprintf( __( 'Make sure that you have the correct upstream configuration for WPMS. If you do not have that capability or check if you are eligible, please <a href="%s">Contact Support</a>.', 'pantheon' ), 'https://pantheon.io/support' ); ?></p>
<?php
}
}
?>
</div>
<?php
}
add_action( 'admin_notices', 'pantheon_multisite_install_finalize_message' );

View File

@@ -1,41 +0,0 @@
<?php
namespace Pantheon\NetworkSetup;
/**
* Alter network setup pages to include Pantheon-specific instructions.
*/
/**
* Replace the WordPress core Network Setup page from the Settings menu.
*/
function pantheon_remove_network_setup() {
global $submenu;
if ( isset( $submenu['tools.php'][50] ) ) {
unset( $submenu['tools.php'][50] );
}
}
/**
* Register the Pantheon network setup submenu page.
*/
function pantheon_add_network_setup() {
add_management_page(
__( 'Create a Network of WordPress Sites', 'network-setup' ),
__( 'Network Setup', 'network-setup' ),
'setup_network',
'setup_network',
__NAMESPACE__ . '\\pantheon_render_network_setup_page'
);
}
/**
* Render the Pantheon network setup page.
*/
function pantheon_render_network_setup_page() {
global $wpdb;
require_once __DIR__ . '/network/network.php';
}
add_action( 'admin_menu', __NAMESPACE__ . '\\pantheon_remove_network_setup' );
add_action( 'admin_menu', __NAMESPACE__ . '\\pantheon_add_network_setup' );

View File

@@ -1,542 +0,0 @@
<?php
/* This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
class Pantheon_Cache {
/**
* Define the capability required to see and modify the settings page.
*
* @var string
*/
public $options_capability = 'manage_options';
/**
* Define the default options, which are overridden by what's in wp_options.
*
* @var array
*/
public $default_options = array();
/**
* Stores the options for this plugin (from wp_options).
*
* @var array
*/
public $options = array();
/**
* Store the Paths to be flushed at shutdown.
*
* @var array
*/
public $paths = array();
/**
* The slug for the plugin, used in various places like the options page.
*/
const SLUG = 'pantheon-cache';
/**
* Holds the singleton instance.
*
* @static
* @var object
*/
protected static $instance;
/**
* Get a reference to the singleton.
*
* @return object The singleton instance.
*/
public static function instance() {
if ( ! isset( self::$instance ) ) {
self::$instance = new Pantheon_Cache;
self::$instance->setup();
}
return self::$instance;
}
protected function __construct() {
/** Don't do anything **/
}
/**
* Setup the actions and filters we need to hook into, and initialize any properties we need.
*
* @return void
*/
protected function setup() {
$this->options = get_option( self::SLUG, array() );
$this->default_options = array(
'default_ttl' => 600,
'maintenance_mode' => 'disabled',
);
$this->options = wp_parse_args( $this->options, $this->default_options );
add_action( 'init', array( $this, 'action_init_do_maintenance_mode' ) );
add_action( 'admin_init', array( $this, 'action_admin_init' ) );
add_action( 'admin_menu', array( $this, 'action_admin_menu' ) );
add_action( 'load-plugin-install.php', array( $this, 'action_load_plugin_install' ) );
add_action( 'admin_post_pantheon_cache_flush_site', array( $this, 'flush_site' ) );
add_action( 'send_headers', array( $this, 'cache_add_headers' ) );
add_filter( 'rest_post_dispatch', array( $this, 'filter_rest_post_dispatch_send_cache_control' ), 10, 2 );
add_action( 'admin_notices', function(){
global $wp_object_cache;
if ( empty( $wp_object_cache->missing_redis_message ) ) {
return;
}
$wp_object_cache->missing_redis_message = 'Alert! The Pantheon Redis service needs to be enabled before the WP Redis object cache will function properly.';
}, 9 ); // Before the message is displayed in the plugin notice.
add_action( 'shutdown', array( $this, 'cache_clean_urls' ), 999 );
}
/**
* Displays maintenance mode when enabled.
*/
public function action_init_do_maintenance_mode() {
$do_maintenance_mode = false;
if ( in_array( $this->options['maintenance_mode'], [ 'anonymous', 'everyone' ], true )
&& ! is_user_logged_in() ) {
$do_maintenance_mode = true;
}
if ( 'everyone' === $this->options['maintenance_mode']
&& is_user_logged_in()
&& ! current_user_can( 'manage_options' ) ) {
$do_maintenance_mode = true;
}
if ( defined( 'WP_CLI' ) && WP_CLI ) {
$do_maintenance_mode = false;
}
if ( 'wp-login.php' === $GLOBALS['pagenow'] ) {
$do_maintenance_mode = false;
}
/**
* Modify maintenance mode behavior with more advanced conditionals.
*
* @var boolean $do_maintenance_mode Whether or not to do maintenance mode.
*/
$do_maintenance_mode = apply_filters( 'pantheon_cache_do_maintenance_mode', $do_maintenance_mode );
if ( ! $do_maintenance_mode ) {
return;
}
wp_die(
__( 'Briefly unavailable for scheduled maintenance. Check back in a minute.' ),
__( 'Maintenance' ),
503
);
}
/**
* Prep the Settings API.
*
* @return void
*/
public function action_admin_init() {
register_setting( self::SLUG, self::SLUG, array( self::$instance, 'sanitize_options' ) );
add_settings_section( 'general', false, '__return_false', self::SLUG );
add_settings_field( 'default_ttl', null, array( self::$instance, 'default_ttl_field' ), self::SLUG, 'general' );
add_settings_field( 'maintenance_mode', null, array( self::$instance, 'maintenance_mode_field' ), self::SLUG, 'general' );
}
/**
* Add the settings page to the menu.
*
* @return void
*/
public function action_admin_menu() {
add_options_page( __( 'Pantheon Page Cache', 'pantheon-cache' ), __( 'Pantheon Page Cache', 'pantheon-cache' ), $this->options_capability, self::SLUG, array( self::$instance, 'view_settings_page' ) );
}
/**
* Check to see if JavaScript should trigger the opening of the plugin install box
*/
public function action_load_plugin_install() {
if ( empty( $_GET['action'] ) || 'pantheon-load-infobox' !== $_GET['action'] ) {
return;
}
add_action( 'admin_footer', array( $this, 'action_admin_footer_trigger_plugin_open' ) );
}
/**
* Trigger the opening of the Pantheon Advanced Page Cache infobox
*/
public function action_admin_footer_trigger_plugin_open() {
?>
<script>
jQuery(document).ready(function(){
// Wait until the click event handler is bound by core JavaScript
setTimeout(function(){
jQuery('.plugin-card-pantheon-advanced-page-cache a.open-plugin-details-modal').trigger('click');
}, 1 )
});
</script>
<?php
}
/**
* Add the HTML for the default TTL field.
*
* @return void
*/
public function default_ttl_field() {
echo '<h3>' . __( 'Default Time to Live (TTL)', 'pantheon-cache' ) . '</h3>';
echo '<p>' . __( 'Maximum time a cached page will be served. A higher TTL typically improves site performance.', 'pantheon-cache' ) . '</p>';
echo '<input type="text" name="' . self::SLUG . '[default_ttl]" value="' . $this->options['default_ttl'] . '" size="5" /> ' . __( 'seconds', 'pantheon-cache' );
}
/**
* Add the HTML for the maintenance mode field.
*
* @return void
*/
public function maintenance_mode_field() {
echo '<h3>' . __( 'Maintenance Mode', 'pantheon-cache' ) . '</h3>';
echo '<p>' . __( 'Enable maintenance mode to work on your site while serving cached pages to:', 'pantheon-cache' ) . '</p>';
echo '<label style="display: block; margin-bottom: 5px;"><input type="radio" name="' . self::SLUG . '[maintenance_mode]" value="" ' . checked( 'disabled', $this->options['maintenance_mode'], false ) . ' /> ' . __( 'Disabled', 'pantheon-cache' ) . '</label>';
echo '<label style="display: block; margin-bottom: 5px;"><input type="radio" name="' . self::SLUG . '[maintenance_mode]" value="anonymous" ' . checked( 'anonymous', $this->options['maintenance_mode'], false ) . ' /> ' . __( 'Logged-Out Visitors', 'pantheon-cache' ) . '</label>';
echo '<label style="display: block; margin-bottom: 5px;"><input type="radio" name="' . self::SLUG . '[maintenance_mode]" value="everyone" ' . checked( 'everyone', $this->options['maintenance_mode'], false ) . ' /> ' . __( 'Everyone except Administrators', 'pantheon-cache' ) . '</label>';
}
/**
* Sanitize our options.
*
* @param array $in The POST values.
* @return array The sanitized POST values.
*/
public function sanitize_options( $in ) {
$out = $this->default_options;
// Validate default_ttl
$out['default_ttl'] = absint( $in['default_ttl'] );
if ( $out['default_ttl'] < 60 && isset( $_ENV['PANTHEON_ENVIRONMENT'] ) && 'live' === $_ENV['PANTHEON_ENVIRONMENT'] ) {
$out['default_ttl'] = 60;
}
if ( ! empty( $in['maintenance_mode'] )
&& in_array( $in['maintenance_mode'], [ 'anonymous', 'everyone' ], true ) ) {
$out['maintenance_mode'] = $in['maintenance_mode'];
} else {
$out['maintenance_mode'] = 'disabled';
}
return $out;
}
/**
* Output the settings page.
*
* @return void
*/
public function view_settings_page() {
?>
<div class="wrap">
<h2><?php esc_html_e( 'Pantheon Page Cache', 'pantheon-cache' ); ?></h2>
<?php if ( ! empty( $_GET['cache-cleared'] ) && 'true' == $_GET['cache-cleared'] ) : ?>
<div class="updated below-h2">
<p><?php esc_html_e( 'Site cache flushed.', 'pantheon-cache' ); ?></p>
</div>
<?php endif ?>
<?php if ( class_exists( 'Pantheon_Advanced_Page_Cache\Purger' ) ) : // translators: %s is a link. ?>
<div class="notice notice-success"><p><?php echo wp_kses_post( sprintf( __( 'Pantheon Advanced Page Cache activated. <a target="_blank" href="%s">Learn more</a>', 'pantheon-cache' ), 'https://docs.pantheon.io/guides/wordpress-configurations/wordpress-cache-plugin' ) ); ?></p></div>
<?php else : // translators: %s is a link. ?>
<div class="notice notice-warning"><p><?php echo wp_kses_post( sprintf( __( 'Want to automatically clear related pages when you update content? Learn more about the <a href="%s">Pantheon Advanced Page Cache</a>.', 'pantheon-cache' ), 'https://docs.pantheon.io/guides/wordpress-configurations/wordpress-cache-plugin' ) ); ?></p></div>
<?php endif; ?>
<?php
/**
* Permits the Pantheon Advanced Page Cache plugin to add
* supplemental text.
*/
do_action( 'pantheon_cache_settings_page_top' );
?>
<?php if ( apply_filters( 'pantheon_cache_allow_clear_all', true ) ) : ?>
<form action="admin-post.php" method="POST">
<input type="hidden" name="action" value="pantheon_cache_flush_site" />
<?php wp_nonce_field( 'pantheon-cache-clear-all', 'pantheon-cache-nonce' ); ?>
<h3><?php _e( 'Clear Site Cache', 'pantheon-cache' ); ?></h3>
<p><?php _e( 'Use with care. Clearing the entire site cache will negatively impact performance for a short period of time.', 'pantheon-cache' ); ?></p>
<?php submit_button( __( 'Clear Cache', 'pantheon-cache' ), 'secondary' ); ?>
</form>
<hr />
<?php endif ?>
<style>
.ttl-form th[scope="row"] {
display: none;
}
.ttl-form td {
padding-left: 0;
}
.ttl-form td p {
margin-bottom: 1em;
font-size: 13px;
}
</style>
<form action="options.php" method="POST" class="ttl-form">
<?php settings_fields( self::SLUG ); ?>
<?php do_settings_sections( self::SLUG ); ?>
<?php submit_button( __( 'Save Changes', 'pantheon-cache' ) ); ?>
</form>
<hr />
<?php
/**
* Permits the Pantheon Advanced Page Cache plugin to add
* supplemental text.
*/
do_action( 'pantheon_cache_settings_page_bottom' ); ?>
</div>
<?php
}
/**
* Get the cache-control header value.
*
* This removes "max-age=0" which could hypothetically be used by
* Varnish on an immediate subsequent request.
*
* @return void
*/
private function get_cache_control_header_value() {
if ( ! is_admin() && ! is_user_logged_in() ) {
$ttl = absint( $this->options['default_ttl'] );
if ( $ttl < 60 && isset( $_ENV['PANTHEON_ENVIRONMENT'] ) && 'live' === $_ENV['PANTHEON_ENVIRONMENT'] ) {
$ttl = 60;
}
return sprintf( 'public, max-age=%d', $ttl );
} else {
return 'no-cache, no-store, must-revalidate';
}
}
/**
* Add the cache-control header.
*
* @return void
*/
public function cache_add_headers() {
header( sprintf( 'cache-control: %s', $this->get_cache_control_header_value() ) );
}
/**
* Send the cache control header for REST API requests
*
* @param WP_REST_Response $response Response.
* @return WP_REST_Response Response.
*/
public function filter_rest_post_dispatch_send_cache_control( $response ) {
$response->header( 'Cache-Control', $this->get_cache_control_header_value() );
return $response;
}
/**
* Clear the cache for the entire site.
*
* @return void
*/
public function flush_site() {
if ( ! function_exists( 'current_user_can' ) || false == current_user_can( 'manage_options' ) )
return false;
if ( ! empty( $_POST['pantheon-cache-nonce'] ) && wp_verify_nonce( $_POST['pantheon-cache-nonce'], 'pantheon-cache-clear-all' ) ) {
if ( function_exists( 'pantheon_clear_edge_all' ) ) {
pantheon_clear_edge_all();
}
wp_cache_flush();
wp_redirect( admin_url( 'options-general.php?page=pantheon-cache&cache-cleared=true' ) );
exit();
}
}
/**
* Clear the cache for a post.
*
* @deprecated
*
* @param int $post_id A post ID to clean.
* @return void
*/
public function clean_post_cache( $post_id, $include_homepage = true ) {
if ( method_exists( 'Pantheon_Advanced_Page_Cache\Purger', 'action_clean_post_cache' ) ) {
Pantheon_Advanced_Page_Cache\Purger::action_clean_post_cache( $post_id );
}
}
/**
* Clear the cache for a given term or terms and taxonomy.
*
* @deprecated
*
* @param int|array $ids Single or list of Term IDs.
* @param string $taxonomy Can be empty and will assume tt_ids, else will use for context.
* @return void
*/
public function clean_term_cache( $term_ids, $taxonomy ) {
if ( method_exists( 'Pantheon_Advanced_Page_Cache\Purger', 'action_clean_term_cache' ) ) {
Pantheon_Advanced_Page_Cache\Purger::action_clean_term_cache( $term_ids );
}
}
/**
* Clear the cache for a given term or terms and taxonomy.
*
* @deprecated
*
* @param int|array $object_ids Single or list of term object ID(s).
* @param array|string $object_type The taxonomy object type.
* @return void
*/
public function clean_object_term_cache( $object_ids, $object_type ) {
// Handled by Pantheon Integrated CDN
}
/**
* Enqueue Fully-qualified urls to be cleared on shutdown.
*
* @param array|string $urls List of full urls to clear.
* @return void
*/
public function enqueue_urls( $urls ) {
$paths = array();
$urls = array_filter( (array) $urls, 'is_string' );
foreach ( $urls as $full_url ) {
# Parse down to the path+query, escape regex.
$parsed = parse_url( $full_url );
# Sometimes parse_url can return false, on malformed urls
if (FALSE == $parsed) {
continue;
}
# Build up the path, checking if the array key exists first
if (array_key_exists('path', $parsed)) {
$path = $parsed['path'];
if (array_key_exists('query', $parsed)) {
$path = $path . $parsed['query'];
}
}
# If the path doesn't exist, set it to the null string
else {
$path = '';
}
if ( '' == $path ) {
continue;
}
$path = '^' . preg_quote( $path ) . '$';
$paths[] = $path;
}
$this->paths = array_merge( $this->paths, $paths );
}
/**
* Enqueue a regex to be cleared.
*
* You must understand regular expressions to use this, and be careful.
*
* @param string $regex path regex to clear.
* @return void
*/
public function enqueue_regex( $regex ) {
$this->paths[] = $regex;
}
public function cache_clean_urls() {
if ( empty( $this->paths ) )
return;
$this->paths = apply_filters( 'pantheon_clean_urls', array_unique( $this->paths ) );
# Call the big daddy here
$url = home_url();
$host = parse_url( $url, PHP_URL_HOST );
$this->paths = apply_filters( 'pantheon_final_clean_urls', $this->paths );
if ( function_exists( 'pantheon_clear_edge_paths' ) ) {
pantheon_clear_edge_paths( $this->paths );
}
}
}
/**
* Get a reference to the singleton.
*
* This can be used to reference public methods, e.g. `Pantheon_Cache()->clean_post_cache( 123 )`
*
* @return void
*/
function Pantheon_Cache() {
return Pantheon_Cache::instance();
}
add_action( 'plugins_loaded', 'Pantheon_Cache' );
/**
* @see Pantheon_Cache::clean_post_cache
*
* @deprecated Please call Pantheon Integrated CDN instead.
*/
function pantheon_clean_post_cache( $post_id, $include_homepage = true ) {
Pantheon_Cache()->clean_post_cache( $post_id, $include_homepage );
}
/**
* @see Pantheon_Cache::clean_term_cache
*
* @deprecated Please call Pantheon Integrated CDN instead.
*/
function pantheon_clean_term_cache( $term_ids, $taxonomy ) {
Pantheon_Cache()->clean_term_cache( $term_ids, $taxonomy );
}
/**
* @see Pantheon_Cache::enqueue_urls
*
* @deprecated Please call Pantheon Integrated CDN instead.
*/
function pantheon_enqueue_urls( $urls ) {
Pantheon_Cache()->enqueue_urls( $urls );
}

View File

@@ -1,29 +0,0 @@
<?php
/**
* If a Pantheon site is in Git mode, hide the Plugin installation functionality and show a notice.
*/
if ( ! wp_is_writable( WP_PLUGIN_DIR ) ) {
if ( ! defined( 'DISALLOW_FILE_MODS' ) ) {
define( 'DISALLOW_FILE_MODS', true );
}
add_action( 'admin_notices', '_pantheon_plugin_install_notice' );
add_action( 'network_admin_notices', '_pantheon_plugin_install_notice' );
}
function _pantheon_plugin_install_notice() {
$screen = get_current_screen();
// Only show this notice on the plugins page.
if ( 'plugins' === $screen->id || 'plugins-network' === $screen->id ) { ?>
<div class="update-nag notice notice-warning is-dismissible" style="margin: 5px 6em 15px 0;">
<p style="font-size: 14px; margin: 0;">
<?php
// Translators: %s is a URL to the user's Pantheon Dashboard.
echo wp_kses_post( sprintf( __( 'If you wish to update or add plugins using the WordPress UI, switch your site to SFTP mode from <a href="%s">your Pantheon dashboard</a>.', 'pantheon-systems' ), 'https://dashboard.pantheon.io/sites/' . $_ENV['PANTHEON_SITE'] ) );
?>
</p>
</div>
<?php
}
}

View File

@@ -1,182 +0,0 @@
<?php
/**
* Pantheon MU Plugin Updates
*
* Handles modifying the default WordPress update behavior on Pantheon.
*/
// If on Pantheon...
if ( isset( $_ENV['PANTHEON_ENVIRONMENT'] ) ) {
// Disable WordPress auto updates.
if ( ! defined( 'WP_AUTO_UPDATE_CORE' ) ) {
define( 'WP_AUTO_UPDATE_CORE', false );
}
remove_action( 'wp_maybe_auto_update', 'wp_maybe_auto_update' );
// Remove the default WordPress core update nag.
add_action( 'admin_menu', '_pantheon_hide_update_nag' );
}
/**
* Remove the default WordPress core update nag message.
*
* @return void
*/
function _pantheon_hide_update_nag() {
remove_action( 'admin_notices', 'update_nag', 3 );
remove_action( 'network_admin_notices', 'update_nag', 3 );
}
/**
* Helper function that returns the current WordPress version.
*
* @return string
*/
function _pantheon_get_current_wordpress_version() : string {
include ABSPATH . WPINC . '/version.php';
return $wp_version; // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UndefinedVariable
}
/**
* Get the latest WordPress version.
*
* @return string|null
*/
function _pantheon_get_latest_wordpress_version() : ?string {
$core_updates = get_core_updates();
if ( ! is_array( $core_updates ) || empty( $core_updates ) || ! property_exists( $core_updates[0], 'current' ) ) {
return null;
}
return $core_updates[0]->current;
}
/**
* Check if WordPress core is at the latest version.
*
* @return bool
*/
function _pantheon_is_wordpress_core_latest() : bool {
$latest_wp_version = _pantheon_get_latest_wordpress_version();
$wp_version = _pantheon_get_current_wordpress_version();
if ( null === $latest_wp_version ) {
return true;
}
// Return true if our version is the latest.
return version_compare( str_replace( '-src', '', $latest_wp_version ), str_replace( '-src', '', $wp_version ), '<=' ); // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UndefinedVariable
}
/**
* Check if WordPress core is a pre-release version.
*
* @return bool
*/
function _pantheon_is_wordpress_core_prerelease() : bool {
$wp_version = _pantheon_get_current_wordpress_version();
// Return true if our version is a prerelease. Pre-releases are identified by a dash in the version number.
return false !== strpos( $wp_version, '-' ); // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UndefinedVariable
}
/**
* Replace WordPress core update nag EVERYWHERE with our own notice.
* Use git upstream instead
*
* @return void
*/
function _pantheon_upstream_update_notice() {
$wp_version = _pantheon_get_current_wordpress_version();
$screen = get_current_screen();
// Translators: %s is a URL to the user's Pantheon Dashboard.
$notice_message = sprintf( __( 'Check for updates on <a href="%s">your Pantheon dashboard</a>.', 'pantheon-systems' ), 'https://dashboard.pantheon.io/sites/' . $_ENV['PANTHEON_SITE'] );
// Translators: %s is a URL to Pantheon's upstream updates documentation.
$upstream_help_message = sprintf( __( 'For details on applying updates, see the <a href="%s">Applying Upstream Updates</a> documentation.', 'pantheon-systems' ), 'https://docs.pantheon.io/core-updates' );
$update_help = __( 'If you need help, contact an administrator for your Pantheon organization.', 'pantheon-systems' );
$div_class = esc_attr( 'update-nag notice notice-warning' );
$div_style = esc_attr( 'display: table;' );
$paragraph_style = esc_attr( 'font-size: 14px; font-weight: bold; margin: 0 0 0.5em 0;' );
if ( ! _pantheon_is_wordpress_core_latest() ) {
// If WP core is out of date, alter the message and show the nag everywhere.
// Translators: %s is a URL to the user's Pantheon Dashboard.
$notice_message = sprintf( __( 'A new WordPress update is available! Please update from <a href="%s">your Pantheon dashboard</a>.', 'pantheon-systems' ), 'https://dashboard.pantheon.io/sites/' . $_ENV['PANTHEON_SITE'] );
}
// If WP core is a pre-release, alter the message.
if ( _pantheon_is_wordpress_core_prerelease() ) {
$version = '<span style="font-weight: normal;">(' . $wp_version . ')</span>';
$paragraph_style = esc_attr( 'font-size: 14px;' );
// Translators: %s is the current WordPress version.
$notice_message = sprintf( __( '<strong>You are using a development version of WordPress.</strong> %s', 'pantheon-systems' ), $version );
// If we're on the Updates page, add a note about the Beta Tester plugin.
if ( 'update-core' === $screen->id || 'update-core-network' === $screen->id ) {
$notice_message .= '<br /><span style="font-weight: normal;">';
$notice_message .= __( 'You are responsible for keeping WordPress up-to-date. Pantheon updates to WordPress will not appear in the dashboard as long as you\'re using a pre-release version. If you are using the Beta Tester plugin, you must have your site in SFTP mode to get the latest updates to your Pantheon Dev environment.', 'pantheon-systems' );
}
}
ob_start();
?>
<div class="<?php echo esc_attr( $div_class ); ?>" style="<?php echo esc_attr( $div_style ); ?>">
<p style="<?php echo esc_attr( $paragraph_style ); ?>">
<?php echo wp_kses_post( $notice_message ); ?>
</p>
<?php if ( ! _pantheon_is_wordpress_core_prerelease() ) : ?>
<?php echo wp_kses_post( $upstream_help_message ); ?>
<br />
<?php echo wp_kses_post( $update_help ); ?>
<?php endif; ?>
</div>
<?php
$notice_html = ob_get_clean();
// If a WP core update is not detected, only show the nag on the updates page.
if ( ! _pantheon_is_wordpress_core_latest() || 'update-core' === $screen->id || 'update-core-network' === $screen->id ) {
// Escaping is handled above when we're buffering the output, so we can ignore it here.
echo $notice_html; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
}
/**
* Register Pantheon specific WordPress update admin notice.
*
* @return void
*/
function _pantheon_register_upstream_update_notice() {
// Only register notice if we are on Pantheon and this is not a WordPress Ajax request.
if ( isset( $_ENV['PANTHEON_ENVIRONMENT'] ) && ! wp_doing_ajax() ) {
add_action( 'admin_notices', '_pantheon_upstream_update_notice' );
add_action( 'network_admin_notices', '_pantheon_upstream_update_notice' );
}
}
add_action( 'admin_init', '_pantheon_register_upstream_update_notice' );
/**
* Return zero updates and current time as last checked time.
*
* @return object
*/
function _pantheon_disable_wp_updates() : object {
$wp_version = _pantheon_get_current_wordpress_version();
return (object) [
'updates' => [],
'version_checked' => $wp_version, // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UndefinedVariable
'last_checked' => time(),
];
}
// In the Test and Live environments, clear plugin/theme update notifications.
// Users must check a dev or multidev environment for updates.
if ( isset( $_ENV['PANTHEON_ENVIRONMENT'] ) && in_array( $_ENV['PANTHEON_ENVIRONMENT'], [ 'test', 'live' ], true ) && ( php_sapi_name() !== 'cli' ) ) {
// Disable Plugin Updates.
remove_action( 'load-update-core.php', 'wp_update_plugins' );
add_filter( 'pre_site_transient_update_plugins', '_pantheon_disable_wp_updates' );
// Disable Theme Updates.
remove_action( 'load-update-core.php', 'wp_update_themes' );
add_filter( 'pre_site_transient_update_themes', '_pantheon_disable_wp_updates' );
}

View File

@@ -1,51 +0,0 @@
<?php
/**
* Plugin Name: Pantheon
* Plugin URI: https://pantheon.io/
* Description: Building on Pantheon's and WordPress's strengths, together.
* Version: 1.0.0
* Author: Pantheon
* Author URI: https://pantheon.io/
*
* @package pantheon
*/
if ( isset( $_ENV['PANTHEON_ENVIRONMENT'] ) ) {
require_once 'inc/pantheon-page-cache.php';
if ( ! defined( 'DISABLE_PANTHEON_UPDATE_NOTICES' ) || ! DISABLE_PANTHEON_UPDATE_NOTICES ) {
require_once 'inc/pantheon-updates.php';
}
if ( ! defined('RETURN_TO_PANTHEON_BUTTON') || RETURN_TO_PANTHEON_BUTTON ) {
require_once 'inc/pantheon-login-form-mods.php';
}
if ( 'dev' === $_ENV['PANTHEON_ENVIRONMENT'] && function_exists( 'wp_is_writable' ) ) {
require_once 'inc/pantheon-plugin-install-notice.php';
}
if ( defined( 'WP_CLI' ) && WP_CLI ) {
require_once 'inc/cli.php';
}
if ( ! defined( 'FS_METHOD' ) ) {
/**
* When this constant is not set, WordPress writes and then deletes a
* temporary file to determine if it has direct access to the filesystem,
* which we already know to be the case. This multiplies filesystem
* operations and can degrade performance of the filesystem as a whole in
* the case of large sites that do a lot of filesystem operations.
* Setting this constant to 'direct' tells WordPress to assume it has
* direct access and skip creating the extra temporary file.
*/
define( 'FS_METHOD', 'direct' );
}
// When developing a WordPress Multisite locally, ensure that this constant is set.
// This will set the Multisite variable in all Pantheon environments.
if ( getenv( 'FRAMEWORK' ) === 'wordpress_network' && ! defined( 'WP_ALLOW_MULTISITE' ) ) {
define( 'WP_ALLOW_MULTISITE', true );
}
if ( defined( 'MULTISITE' ) && defined( 'WP_ALLOW_MULTISITE' ) && WP_ALLOW_MULTISITE ) {
require_once 'inc/pantheon-network-setup.php';
}
if ( defined( 'WP_ALLOW_MULTISITE' ) && ( ! defined( 'MULTISITE' ) || empty( MULTISITE ) ) ) {
require_once 'inc/pantheon-multisite-finalize.php';
}
} // Ensuring that this is on Pantheon.

View File

@@ -55,7 +55,7 @@ var $ = jQuery.noConflict(),
default:
if (tag) {
// Queue the requests for an AJAX call at the end of init
dtx.queue.push(raw_value);
dtx.queue.push({ 'value': raw_value, 'multiline': $input.is('textarea') });
}
return; // Don't continue after queuing it for AJAX
}

View File

@@ -1,2 +1,2 @@
/*! Do not edit, this file is generated automatically - 2023-08-07 16:08:32 EDT */
var $=jQuery.noConflict(),dtx={queue:[],init:function(){var $inputs=$("input.dtx-pageload[data-dtx-value]");$inputs.length&&($inputs.each(function(i,el){var el=$(el),raw_value=el.attr("data-dtx-value"),v=decodeURIComponent(raw_value).split(" ");if(v.length){var tag=v[0],atts={};if(1<v.length)for(var x=1;x<v.length;x++){var att=v[x].split("=");2===att.length&&(atts[att[0]]=att[1].split("'").join(""))}var value="";switch(tag){case"CF7_GET":value=dtx.get(atts);break;case"CF7_referrer":value=dtx.referrer(atts);break;case"CF7_URL":value=dtx.current_url(atts);break;case"CF7_get_cookie":value=dtx.get_cookie(atts);break;case"CF7_guid":value=dtx.guid();break;case"CF7_get_current_var":if(!dtx.validKey(atts,"key")||"url"!=atts.key)return;value=dtx.current_url(atts);break;case"CF7_get_post_var":case"CF7_get_custom_field":case"CF7_get_taxonomy":case"CF7_get_attachment":case"CF7_bloginfo":case"CF7_get_theme_option":return;default:return void(tag&&dtx.queue.push(raw_value))}dtx.set(el,value)}}),dtx.queue.length)&&setTimeout(function(){$.ajax({type:"POST",url:dtx_obj.ajax_url,dataType:"json",data:{action:"wpcf7dtx",shortcodes:dtx.queue},cache:!1,error:function(xhr,status,error){console.error("[CF7 DTX AJAX ERROR]",error,status,xhr)},success:function(data,status,xhr){"object"==typeof data&&data.length&&$.each(data,function(i,obj){var $inputs=$('.wpcf7 form input.dtx-pageload[data-dtx-value="'+obj.raw_value+'"]');$inputs.length&&(dtx.set($inputs,obj.value),$inputs.addClass("dtx-ajax-loaded"))})}})},10)},validKey:function(obj,key){return obj.hasOwnProperty(key)&&"string"==typeof obj[key]&&obj[key].trim()},obfuscate:function(value,atts){if(value=value.trim(),dtx.validKey(atts,"obfuscate")&&atts.obfuscate){for(var o="",i=0;i<value.length;i++)o+="&#"+value.codePointAt(i)+";";return o}return value},set:function($input,value){$input.attr("value",value).addClass("dtx-loaded")},get:function(atts){if(dtx.validKey(atts,"key")){var query=window.location.search;if(query)return query=new URLSearchParams(query),dtx.obfuscate(query.get(atts.key).trim(),atts)}return""},referrer:function(atts){return dtx.obfuscate(document.referrer,atts)},current_url:function(atts){if(!atts.hasOwnProperty("part"))return dtx.obfuscate(window.location.href,atts);if(["scheme","host","port","path","query","fragment"].includes(atts.part))switch(atts.part){case"scheme":return dtx.obfuscate(window.location.protocol.replace(":",""),atts);case"host":return dtx.obfuscate(window.location.host,atts);case"port":return dtx.obfuscate(window.location.port,atts);case"path":return dtx.obfuscate(window.location.pathname,atts);case"query":return dtx.obfuscate(window.location.search.replace("?",""),atts);case"fragment":return dtx.obfuscate(window.location.hash.replace("#",""),atts)}return""},get_cookie:function(atts){var keyValue;return atts.hasOwnProperty("key")&&"string"==typeof atts.key&&""!=atts.key.trim()&&(keyValue=document.cookie.match("(^|;) ?"+atts.key.trim()+"=([^;]*)(;|$)"))?dtx.obfuscate(keyValue[2],atts):""},guid:function(){if(void 0!==window.crypto&&void 0!==window.crypto.getRandomValues)return([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g,c=>(c^crypto.getRandomValues(new Uint8Array(1))[0]&15>>c/4).toString(16)).toUpperCase();console.warn("[CF7 DTX] Cryptographically secure PRNG is not available for generating GUID value");var d=(new Date).getTime(),d2="undefined"!=typeof performance&&performance.now&&1e3*performance.now()||0;return"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,function(c){var r=16*Math.random();return 0<d?(r=(d+r)%16|0,d=Math.floor(d/16)):(r=(d2+r)%16|0,d2=Math.floor(d2/16)),("x"===c?r:3&r|8).toString(16).toUpperCase()}).toUpperCase()}};$(document).ready(dtx.init);
/*! Do not edit, this file is generated automatically - 2023-09-18 14:09:50 EDT */
var $=jQuery.noConflict(),dtx={queue:[],init:function(){var e=$("input.dtx-pageload[data-dtx-value]");e.length&&(e.each(function(e,t){var r=$(t),a=r.attr("data-dtx-value"),o=decodeURIComponent(a).split(" ");if(o.length){var n=o[0],c={};if(1<o.length)for(var u=1;u<o.length;u++){var i=o[u].split("="),d;2===i.length&&(c[i[0]]=i[1].split("'").join(""))}var s="";switch(n){case"CF7_GET":s=dtx.get(c);break;case"CF7_referrer":s=dtx.referrer(c);break;case"CF7_URL":s=dtx.current_url(c);break;case"CF7_get_cookie":s=dtx.get_cookie(c);break;case"CF7_guid":s=dtx.guid();break;case"CF7_get_current_var":if(!dtx.validKey(c,"key")||"url"!=c.key)return;s=dtx.current_url(c);break;case"CF7_get_post_var":case"CF7_get_custom_field":case"CF7_get_taxonomy":case"CF7_get_attachment":case"CF7_bloginfo":case"CF7_get_theme_option":return;default:return void(n&&dtx.queue.push({value:a,multiline:r.is("textarea")}))}dtx.set(r,s)}}),dtx.queue.length)&&setTimeout(function(){$.ajax({type:"POST",url:dtx_obj.ajax_url,dataType:"json",data:{action:"wpcf7dtx",shortcodes:dtx.queue},cache:!1,error:function(e,t,r){},success:function(e,t,r){"object"==typeof e&&e.length&&$.each(e,function(e,t){var r=$('.wpcf7 form input.dtx-pageload[data-dtx-value="'+t.raw_value+'"]');r.length&&(dtx.set(r,t.value),r.addClass("dtx-ajax-loaded"))})}})},10)},validKey:function(e,t){return e.hasOwnProperty(t)&&"string"==typeof e[t]&&e[t].trim()},obfuscate:function(e,t){if(e=e.trim(),dtx.validKey(t,"obfuscate")&&t.obfuscate){for(var r="",a=0;a<e.length;a++)r+="&#"+e.codePointAt(a)+";";return r}return e},set:function(e,t){e.attr("value",t).addClass("dtx-loaded")},get:function(e){if(dtx.validKey(e,"key")){var t=window.location.search;if(t)return t=new URLSearchParams(t),dtx.obfuscate(t.get(e.key).trim(),e)}return""},referrer:function(e){return dtx.obfuscate(document.referrer,e)},current_url:function(e){if(!e.hasOwnProperty("part"))return dtx.obfuscate(window.location.href,e);var t;if(["scheme","host","port","path","query","fragment"].includes(e.part))switch(e.part){case"scheme":return dtx.obfuscate(window.location.protocol.replace(":",""),e);case"host":return dtx.obfuscate(window.location.host,e);case"port":return dtx.obfuscate(window.location.port,e);case"path":return dtx.obfuscate(window.location.pathname,e);case"query":return dtx.obfuscate(window.location.search.replace("?",""),e);case"fragment":return dtx.obfuscate(window.location.hash.replace("#",""),e)}return""},get_cookie:function(e){var t;return e.hasOwnProperty("key")&&"string"==typeof e.key&&""!=e.key.trim()&&(t=document.cookie.match("(^|;) ?"+e.key.trim()+"=([^;]*)(;|$)"))?dtx.obfuscate(t[2],e):""},guid:function(){var r,a;return(void 0!==window.crypto&&void 0!==window.crypto.getRandomValues?([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g,e=>(e^crypto.getRandomValues(new Uint8Array(1))[0]&15>>e/4).toString(16)):(r=(new Date).getTime(),a="undefined"!=typeof performance&&performance.now&&1e3*performance.now()||0,"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,function(e){var t=16*Math.random();return 0<r?(t=(r+t)%16|0,r=Math.floor(r/16)):(t=(a+t)%16|0,a=Math.floor(a/16)),("x"===e?t:3&t|8).toString(16).toUpperCase()}))).toUpperCase()}};$(document).ready(dtx.init);

View File

@@ -26,6 +26,14 @@
};
$(function() {
$('form.tag-generator-panel input.dtx-option').on('change keyup', wpcf7dtx.taggen.updateOption);
$('form.tag-generator-panel .dtx-option').on('change keyup click', wpcf7dtx.taggen.updateOption);
$('.contact-form-editor-panel #tag-generator-list a.thickbox.button[href*="inlineId=tag-generator-panel-dynamic_"]').each(function() {
var $btn = $(this),
name = $btn.text();
$btn.addClass('dtx-form-tag');
if (name == 'dynamic drop-down menu' || name == 'dynamic checkboxes' || name == 'dynamic radio buttons') {
$btn.attr('href', $btn.attr('href').replace('height=500', 'height=750'));
}
});
});
})(jQuery);

View File

@@ -1,2 +1,2 @@
/*! Do not edit, this file is generated automatically - 2023-08-07 16:08:32 EDT */
!function($){"use strict";"undefined"!=typeof wpcf7&&null!==wpcf7&&(window.wpcf7dtx=window.wpcf7dtx||{},wpcf7dtx.taggen={},wpcf7dtx.taggen.escapeRegExp=function(str){return str.replace(/([.*+?^=!:${}()|\[\]\/\\])/g,"\\$1")},wpcf7dtx.taggen.replaceAll=function(input,f,r,no_escape){var rexp;return null!=input&&"string"==typeof input&&""!==input.trim()&&-1<input.indexOf(f)?(rexp=new RegExp(wpcf7dtx.taggen.escapeRegExp(f),"g"),no_escape&&(rexp=new RegExp(f,"g")),input.replace(rexp,r)):input},wpcf7dtx.taggen.updateOption=function(e){var e=$(e.currentTarget),value=encodeURIComponent(wpcf7dtx.taggen.replaceAll(e.val(),"'","&#39;"));e.siblings('input[type="hidden"].option').val(value)},$(function(){$("form.tag-generator-panel input.dtx-option").on("change keyup",wpcf7dtx.taggen.updateOption)}))}(jQuery);
/*! Do not edit, this file is generated automatically - 2023-09-18 14:09:50 EDT */
!function(n){"use strict";"undefined"!=typeof wpcf7&&null!==wpcf7&&(window.wpcf7dtx=window.wpcf7dtx||{},wpcf7dtx.taggen={},wpcf7dtx.taggen.escapeRegExp=function(e){return e.replace(/([.*+?^=!:${}()|\[\]\/\\])/g,"\\$1")},wpcf7dtx.taggen.replaceAll=function(e,t,n,a){var c;return null!=e&&"string"==typeof e&&""!==e.trim()&&-1<e.indexOf(t)?(c=new RegExp(wpcf7dtx.taggen.escapeRegExp(t),"g"),a&&(c=new RegExp(t,"g")),e.replace(c,n)):e},wpcf7dtx.taggen.updateOption=function(e){var e=n(e.currentTarget),t=encodeURIComponent(wpcf7dtx.taggen.replaceAll(e.val(),"'","&#39;"));e.siblings('input[type="hidden"].option').val(t)},n(function(){n("form.tag-generator-panel .dtx-option").on("change keyup click",wpcf7dtx.taggen.updateOption),n('.contact-form-editor-panel #tag-generator-list a.thickbox.button[href*="inlineId=tag-generator-panel-dynamic_"]').each(function(){var e=n(this),t=e.text();e.addClass("dtx-form-tag"),"dynamic drop-down menu"!=t&&"dynamic checkboxes"!=t&&"dynamic radio buttons"!=t||e.attr("href",e.attr("href").replace("height=500","height=750"))})}))}(jQuery);

View File

@@ -1,7 +1,46 @@
.tag-generator-panel[data-id^="dynamic_select"],
.tag-generator-panel[data-id^="dynamic_checkbox"],
.tag-generator-panel[data-id^="dynamic_radio"] {
height: 740px;
}
#tag-generator-list a.button.dtx-form-tag {
border-color: #765cb9;
color: #765cb9;
}
.tag-generator-panel table.form-table th {
width: 130px;
}
.tag-generator-panel .control-box input.oneline {
.tag-generator-panel table.form-table td small {
display: block;
margin-top: 0.25em;
}
.tag-generator-panel .control-box input.oneline,
.tag-generator-panel .control-box input.multiline,
.tag-generator-panel .control-box textarea {
width: 100%;
}
.tag-generator-panel .control-box input[list],
.tag-generator-panel .control-box input.multiline,
.tag-generator-panel .control-box textarea {
height: 30px;
min-height: 30px;
}
.tag-generator-panel .control-box input.multiline {
display: inline-block;
/* -webkit-appearance: none;
-moz-appearance: none;
appearance: none; */
resize: vertical;
overflow-x: hidden;
overflow-y: scroll;
}
.tag-generator-panel .control-box textarea {
height: 3.5em;
}

View File

@@ -0,0 +1 @@
.tag-generator-panel[data-id^=dynamic_checkbox],.tag-generator-panel[data-id^=dynamic_radio],.tag-generator-panel[data-id^=dynamic_select]{height:740px}#tag-generator-list a.button.dtx-form-tag{border-color:#765cb9;color:#765cb9}.tag-generator-panel table.form-table th{width:130px}.tag-generator-panel table.form-table td small{display:block;margin-top:.25em}.tag-generator-panel .control-box input.multiline,.tag-generator-panel .control-box input.oneline,.tag-generator-panel .control-box textarea{width:100%}.tag-generator-panel .control-box input.multiline,.tag-generator-panel .control-box input[list],.tag-generator-panel .control-box textarea{height:30px;min-height:30px}.tag-generator-panel .control-box input.multiline{display:inline-block;resize:vertical;overflow-x:hidden;overflow-y:scroll}.tag-generator-panel .control-box textarea{height:3.5em}

View File

@@ -1,5 +1,48 @@
== Changelog ==
= 4.1.0 =
* Feature: Looks for a `dtx.php` file in the `wp_content` directory to maybe load custom shortcodes, [see support thread](https://wordpress.org/support/topic/how-to-avoid-custom-shortcodes-being-overwritten-on-updates/)
* Feature: Looks for a `dtx.php` file in the current active theme's directory to maybe load custom shortcodes, [see support thread](https://wordpress.org/support/topic/how-to-avoid-custom-shortcodes-being-overwritten-on-updates/)
* Feature: Looks for a `dtx.php` file in the current active theme's parent directory to maybe load custom shortcodes, [see support thread](https://wordpress.org/support/topic/how-to-avoid-custom-shortcodes-being-overwritten-on-updates/)
* Fix: addressed user reported bug, [see support thread](https://wordpress.org/support/topic/fatal-error-v4-0-3/)
= 4.0.3 =
* Feature: Added `exclusive` option to checkbox tag generator
* Fix: addressed bug that put all dynamic checkbox/radio options into one
* Fix: addressed bug in frontend validator for multiple selected values
= 4.0.2 =
* Fix: addressed bug that put all dynamic select options into one, [see support thread](https://wordpress.org/support/topic/dynamic-select-get-option-values-from-shortcode/)
* Update: sanitizing and escaping filters now accept `none` as value for `$type` to bypass. Use with caution.
= 4.0.1 =
* Fix: addressed bug that prevented translation for cache compatibility description
= 4.0.0 =
* Major: modified function names
* Major: deprecated `dynamictext` and `dynamichidden` form tags in favor of `dynamic_text` and `dynamic_hidden`. For more information, see the [knowledge base](https://aurisecreative.com/docs/contact-form-7-dynamic-text-extension/form-tags/?utm_source=wordpress.org&utm_medium=link&utm_campaign=contact-form-7-dynamic-text-extension&utm_content=readme)
* Feature: introduced `dynamic_email` form tag. For usage details, see the [knowledge base](https://aurisecreative.com/docs/contact-form-7-dynamic-text-extension/form-tags/dynamic-email/?utm_source=wordpress.org&utm_medium=link&utm_campaign=contact-form-7-dynamic-text-extension&utm_content=readme)
* Feature: introduced `dynamic_url` form tag. For usage details, see the [knowledge base](https://aurisecreative.com/docs/contact-form-7-dynamic-text-extension/form-tags/dynamic-url/?utm_source=wordpress.org&utm_medium=link&utm_campaign=contact-form-7-dynamic-text-extension&utm_content=readme)
* Feature: introduced `dynamic_tel` form tag. For usage details, see the [knowledge base](https://aurisecreative.com/docs/contact-form-7-dynamic-text-extension/form-tags/dynamic-tel/?utm_source=wordpress.org&utm_medium=link&utm_campaign=contact-form-7-dynamic-text-extension&utm_content=readme)
* Feature: introduced `dynamic_number` form tag. For usage details, see the [knowledge base](https://aurisecreative.com/docs/contact-form-7-dynamic-text-extension/form-tags/dynamic-number/?utm_source=wordpress.org&utm_medium=link&utm_campaign=contact-form-7-dynamic-text-extension&utm_content=readme)
* Feature: introduced `dynamic_range` form tag. For usage details, see the [knowledge base](https://aurisecreative.com/docs/contact-form-7-dynamic-text-extension/form-tags/dynamic-range/?utm_source=wordpress.org&utm_medium=link&utm_campaign=contact-form-7-dynamic-text-extension&utm_content=readme)
* Feature: introduced `dynamic_textarea` form tag. For usage details, see the [knowledge base](https://aurisecreative.com/docs/contact-form-7-dynamic-text-extension/form-tags/dynamic-textarea/?utm_source=wordpress.org&utm_medium=link&utm_campaign=contact-form-7-dynamic-text-extension&utm_content=readme)
* Feature: introduced `dynamic_select` form tag. For usage details, see the [knowledge base](https://aurisecreative.com/docs/contact-form-7-dynamic-text-extension/form-tags/dynamic-select/?utm_source=wordpress.org&utm_medium=link&utm_campaign=contact-form-7-dynamic-text-extension&utm_content=readme)
* Feature: introduced `dynamic_radio` form tag. For usage details, see the [knowledge base](https://aurisecreative.com/docs/contact-form-7-dynamic-text-extension/form-tags/dynamic-radio/?utm_source=wordpress.org&utm_medium=link&utm_campaign=contact-form-7-dynamic-text-extension&utm_content=readme)
* Feature: introduced `dynamic_date` form tag. For usage details, see the [knowledge base](https://aurisecreative.com/docs/contact-form-7-dynamic-text-extension/form-tags/dynamic-date/?utm_source=wordpress.org&utm_medium=link&utm_campaign=contact-form-7-dynamic-text-extension&utm_content=readme)
* Feature: introduced `dynamic_submit` form tag. For usage details, see the [knowledge base](https://aurisecreative.com/docs/contact-form-7-dynamic-text-extension/form-tags/dynamic-submit/?utm_source=wordpress.org&utm_medium=link&utm_campaign=contact-form-7-dynamic-text-extension&utm_content=readme)
* Feature: introduced `dtx_hide_blank` form tag attribute for `dynamic_select`. For usage details, see the [knowledge base](https://aurisecreative.com/docs/contact-form-7-dynamic-text-extension/form-tags/dynamic-select/?utm_source=wordpress.org&utm_medium=link&utm_campaign=contact-form-7-dynamic-text-extension&utm_content=readme)
* Feature: introduced `dtx_disable_blank` form tag attribute for `dynamic_select`. For usage details, see the [knowledge base](https://aurisecreative.com/docs/contact-form-7-dynamic-text-extension/form-tags/dynamic-select/?utm_source=wordpress.org&utm_medium=link&utm_campaign=contact-form-7-dynamic-text-extension&utm_content=readme)
* Feature: added mail validation for `dynamic_email` and `dynamic_hidden` for backend configuration. For more information, see the [FAQ](https://aurisecreative.com/docs/contact-form-7-dynamic-text-extension/frequently-asked-questions/?utm_source=wordpress.org&utm_medium=link&utm_campaign=contact-form-7-dynamic-text-extension&utm_content=readme)
* Feature: added the Akismet feature to DTX text, email, and URL form tags.
* Update: adjusted how queued values were sent for cache compatibility mode to allow for multiline values in textareas
* Removed unused utility functions
= 3.5.4 =
* Fix: Updated JavaScript to prevent cacheable fields from making unnecessary AJAX requests

View File

@@ -4,7 +4,7 @@
* Plugin Name: Contact Form 7 - Dynamic Text Extension
* Plugin URI: https://sevenspark.com/goods/contact-form-7-dynamic-text-extension
* Description: This plugin extends Contact Form 7 by adding dynamic form fields that accept any shortcode to generate default values and placeholder text. Requires Contact Form 7.
* Version: 3.5.4
* Version: 4.1.0
* Author: SevenSpark, AuRise Creative
* Author URI: https://sevenspark.com
* License: GPL2
@@ -32,7 +32,7 @@
*/
// Define current version
define('WPCF7DTX_VERSION', '3.5.4');
define('WPCF7DTX_VERSION', '4.1.0');
// Define root directory
defined('WPCF7DTX_DIR') || define('WPCF7DTX_DIR', __DIR__);
@@ -47,31 +47,139 @@ defined('WPCF7DTX_FILE') || define('WPCF7DTX_FILE', __FILE__);
*/
function wpcf7dtx_init()
{
add_action('wpcf7_init', 'wpcf7dtx_add_shortcode_dynamictext'); // Add custom form tags to CF7
add_filter('wpcf7_validate_dynamictext*', 'wpcf7dtx_dynamictext_validation_filter', 20, 2); // Validate custom form tags
add_action('wpcf7_init', 'wpcf7dtx_add_shortcodes'); // Add custom form tags to CF7
}
add_action('plugins_loaded', 'wpcf7dtx_init', 20);
/**
* DTX Formg Tag Configuration
*
* @since 4.0.0
*
* @return array
*/
function wpcf7dtx_config()
{
global $wpcf7_dynamic_fields_config;
if (!isset($wpcf7_dynamic_fields_config)) {
$wpcf7_dynamic_fields_config = array(
'dynamic_text' => array(
'title' => __('dynamic text', 'contact-form-7-dynamic-text-extension'), //title
'options' => array('placeholder', 'readonly', 'dtx_pageload'),
'description' => __('a single-line plain text', 'contact-form-7-dynamic-text-extension')
),
'dynamic_hidden' => array(
'title' => __('dynamic hidden', 'contact-form-7-dynamic-text-extension'), //title
'options' => array('dtx_pageload'),
'description' => __('a single-line plain text hidden input field', 'contact-form-7-dynamic-text-extension'),
'features' => array(
'display-hidden' => true // Generates an HTML element that is not visible
)
),
'dynamic_email' => array(
'title' => __('dynamic email', 'contact-form-7-dynamic-text-extension'), //title
'options' => array('placeholder', 'readonly', 'dtx_pageload'),
'description' => __('a single-line email address input field', 'contact-form-7-dynamic-text-extension')
),
'dynamic_url' => array(
'title' => __('dynamic URL', 'contact-form-7-dynamic-text-extension'), //title
'options' => array('placeholder', 'readonly', 'dtx_pageload'),
'description' => __('a single-line URL input field', 'contact-form-7-dynamic-text-extension')
),
'dynamic_tel' => array(
'title' => __('dynamic tel', 'contact-form-7-dynamic-text-extension'), //title
'options' => array('placeholder', 'readonly', 'pattern'),
'description' => __('a single-line telephone number input field', 'contact-form-7-dynamic-text-extension')
),
'dynamic_number' => array(
'title' => __('dynamic number', 'contact-form-7-dynamic-text-extension'), //title
'options' => array('placeholder', 'readonly', 'min', 'max', 'step', 'pattern'),
'description' => __('a numeric input field displayed as a number spinbox', 'contact-form-7-dynamic-text-extension')
),
'dynamic_range' => array(
'title' => __('dynamic range', 'contact-form-7-dynamic-text-extension'), //title
'options' => array('placeholder', 'readonly', 'min', 'max', 'step', 'pattern'),
'description' => __('a numeric input field displayed as a slider between a minimum and maximum range', 'contact-form-7-dynamic-text-extension')
),
'dynamic_textarea' => array(
'title' => __('dynamic textarea', 'contact-form-7-dynamic-text-extension'), //title
'options' => array('placeholder', 'readonly', 'dtx_pageload'),
'description' => __('a multi-line plain text input field', 'contact-form-7-dynamic-text-extension')
),
'dynamic_select' => array(
'title' => __('dynamic drop-down menu', 'contact-form-7-dynamic-text-extension'), //title
'options' => array('placeholder', 'readonly', 'multiple', 'include_blank'),
'description' => __('a drop-down menu (i.e select input field)', 'contact-form-7-dynamic-text-extension'),
'features' => array(
'selectable-values' => true // Generates an option (or group of options) from which you can select one or more options
)
),
'dynamic_checkbox' => array(
'title' => __('dynamic checkboxes', 'contact-form-7-dynamic-text-extension'), //title
'options' => array('readonly', 'label_first', 'use_label_element', 'exclusive'),
'description' => __('a group of checkboxes', 'contact-form-7-dynamic-text-extension'),
'features' => array(
'multiple-controls-container' => true, // Generates an HTML element that can contain multiple form controls
'selectable-values' => true // Generates an option (or group of options) from which you can select one or more options
)
),
'dynamic_radio' => array(
'title' => __('dynamic radio buttons', 'contact-form-7-dynamic-text-extension'), //title
'options' => array('readonly', 'label_first', 'use_label_element'),
'description' => __('a group of radio buttons', 'contact-form-7-dynamic-text-extension'),
'features' => array(
'multiple-controls-container' => true, // Generates an HTML element that can contain multiple form controls
'selectable-values' => true // Generates an option (or group of options) from which you can select one or more options
)
),
'dynamic_date' => array(
'title' => __('dynamic date', 'contact-form-7-dynamic-text-extension'), //title
'options' => array('placeholder', 'readonly', 'min', 'max'),
'description' => __('a date input field', 'contact-form-7-dynamic-text-extension')
),
'dynamic_submit' => array(
'title' => __('dynamic submit', 'contact-form-7-dynamic-text-extension'), //title
'options' => array('dtx_pageload'),
'description' => __('a submit button', 'contact-form-7-dynamic-text-extension')
)
);
}
return $wpcf7_dynamic_fields_config;
}
/**
* Add Custom Shortcodes to Contact Form 7
*
* @return void
*/
function wpcf7dtx_add_shortcode_dynamictext()
function wpcf7dtx_add_shortcodes()
{
//Add the dynamic text and hidden form fields
wpcf7_add_form_tag(
array(
'dynamictext', 'dynamictext*',
'dynamichidden', 'dynamichidden*' //Required hidden fields do nothing
),
'wpcf7dtx_dynamictext_shortcode_handler', //Callback
array( //Features
'name-attr' => true,
'dtx_pageload' => true
)
);
//Add the dynamic form fields
foreach (wpcf7dtx_config() as $form_tag => $field) {
$input_type = str_replace('dynamic_', '', $form_tag);
$tag_types = array($form_tag, "$form_tag*");
$callback = 'wpcf7dtx_shortcode_handler';
$features = array_merge(array('name-attr' => true), wpcf7dtx_array_has_key('features', $field, array()));
switch ($input_type) {
case 'text':
case 'hidden':
// Add deprecated tags
$dep_tag = str_replace('_', '', $form_tag);
$tag_types[] = $dep_tag;
$tag_types[] = "$dep_tag*";
add_filter("wpcf7_validate_$dep_tag*", 'wpcf7dtx_validation_filter', 20, 2); // Validate required deprecated form tags
break;
case 'submit':
case 'reset':
$callback = 'wpcf7dtx_button_shortcode_handler';
$features['name-attr'] = false;
break;
default:
break;
}
add_filter("wpcf7_validate_$form_tag*", 'wpcf7dtx_validation_filter', 20, 2); // Validate required custom form tags
wpcf7_add_form_tag($tag_types, $callback, $features);
}
}
/**
@@ -112,35 +220,9 @@ add_action('wp_enqueue_scripts', 'wpcf7dtx_enqueue_frontend_assets');
include_once(WPCF7DTX_DIR . '/includes/utilities.php');
/**
* Get Dynamic Value
*
* @since 3.2.2
*
* @param string $value The form tag value.
* @param WPCF7_FormTag|false $tag Optional. Use to look up default value.
*
* @return string The dynamic output or the original value, not escaped or sanitized.
* Include Validation Functions
*/
function wpcf7dtx_get_dynamic($value, $tag = false)
{
if ($tag !== false) {
$default = $tag->get_option('defaultvalue', '', true);
if (!$default) {
$default = $tag->get_default_option(strval(reset($tag->values)));
}
$value = wpcf7_get_hangover($tag->name, $default);
}
$value = apply_filters('wpcf7dtx_sanitize', $value);
if (is_string($value) && !empty($value)) {
// If a shortcode was passed as the options, evaluate it and use the result
$shortcode_tag = '[' . $value . ']';
$shortcode_output = do_shortcode($shortcode_tag); //Shortcode value
if (is_string($shortcode_output) && $shortcode_output != $shortcode_tag) {
return apply_filters('wpcf7dtx_sanitize', $shortcode_output);
}
}
return $value;
}
include_once(WPCF7DTX_DIR . '/includes/validation.php');
/**
* Form Tag Handler
@@ -149,59 +231,54 @@ function wpcf7dtx_get_dynamic($value, $tag = false)
*
* @return string HTML output of the shortcode
*/
function wpcf7dtx_dynamictext_shortcode_handler($tag)
function wpcf7dtx_shortcode_handler($tag)
{
// Name attribute is required for these form tags
if (empty($tag->name)) {
return '';
}
//Validate
// Validate
$validation_error = wpcf7_get_validation_error($tag->name);
//Configure classes
$class = wpcf7_form_controls_class($tag->type, 'wpcf7dtx-dynamictext');
if ($validation_error) {
$class .= ' wpcf7-not-valid';
}
//Configure input attributes
$atts = array();
$atts['type'] = sanitize_key(str_replace(array('dynamic_', 'dynamic'), '', $tag->basetype));
$atts['name'] = $tag->name;
$atts['id'] = $tag->get_id_option();
$atts['id'] = strval($tag->get_id_option());
$atts['tabindex'] = $tag->get_option('tabindex', 'signed_int', true);
$atts['size'] = $tag->get_size_option('40');
$atts['maxlength'] = $tag->get_maxlength_option();
$atts['minlength'] = $tag->get_minlength_option();
$atts['aria-invalid'] = $validation_error ? 'true' : 'false';
switch ($tag->basetype) {
case 'dynamichidden':
$atts['type'] = 'hidden'; //Override type as hidden
break;
default: // Includes `dynamictext`
$atts['type'] = 'text'; //Override type as text
break;
}
if ($atts['maxlength'] && $atts['minlength'] && $atts['maxlength'] < $atts['minlength']) {
unset($atts['maxlength'], $atts['minlength']);
$atts['class'] = explode(' ', wpcf7_form_controls_class($atts['type']));
$atts['class'][] = 'wpcf7dtx';
$atts['class'][] = sanitize_html_class('wpcf7dtx-' . $atts['type']);
if ($validation_error) {
$atts['class'][] = 'wpcf7-not-valid';
$atts['aria-invalid'] = 'true';
$atts['aria-describedby'] = wpcf7_get_validation_error_reference($tag->name);
} else {
$atts['aria-invalid'] = 'false';
}
if ($tag->has_option('readonly')) {
$atts['readonly'] = 'readonly';
}
if ($tag->is_required() && $atts['type'] !== 'hidden') {
// Add required attribute to applicable input types
if ($tag->is_required() && !in_array($atts['type'], array('hidden', 'quiz'))) {
$atts['aria-required'] = 'true';
$atts['required'] = 'required';
}
// Evaluate the dynamic value
$value = wpcf7dtx_get_dynamic(false, $tag);
$sanitize_type = $atts['type'] == 'textarea' ? $atts['type'] : 'auto';
$value = wpcf7dtx_get_dynamic(false, $tag, $sanitize_type);
// Identify placeholder
if ($tag->has_option('placeholder') || $tag->has_option('watermark')) {
//Reverse engineer what JS did (converted quotes to HTML entities --> URL encode) then sanitize
$placeholder = html_entity_decode(urldecode(implode('', (array)$tag->get_option('placeholder'))), ENT_QUOTES);
$placeholder = html_entity_decode(urldecode($tag->get_option('placeholder', '', true)), ENT_QUOTES);
if ($placeholder) {
//If a different placeholder text has been specified, set both attributes
$placeholder = wpcf7dtx_get_dynamic($placeholder);
$placeholder = wpcf7dtx_get_dynamic($placeholder, false, $sanitize_type);
$atts['placeholder'] = $placeholder;
$atts['value'] = $value;
} else {
@@ -212,18 +289,186 @@ function wpcf7dtx_dynamictext_shortcode_handler($tag)
$atts['value'] = $value;
}
if ($atts['type'] == 'hidden') {
// Always disable for hidden fields
$atts['autocomplete'] = 'off';
// Page load attribute
if ($tag->has_option('dtx_pageload') && is_array($tag->raw_values) && count($tag->raw_values)) {
$atts['data-dtx-value'] = rawurlencode(sanitize_text_field($tag->raw_values[0]));
$atts['class'][] = 'dtx-pageload';
if (wp_style_is('wpcf7dtx', 'registered') && !wp_script_is('wpcf7dtx', 'queue')) {
// If already registered, just enqueue it
wp_enqueue_script('wpcf7dtx');
} elseif (!wp_style_is('wpcf7dtx', 'registered')) {
// If not registered, do that first, then enqueue it
wpcf7dtx_enqueue_frontend_assets();
wp_enqueue_script('wpcf7dtx');
}
}
// Additional configuration based on form field type
if (in_array($atts['type'], array('select', 'checkbox', 'radio'))) {
/**
* Configuration for selection-based fields
*/
if ($tag->has_option('default')) {
$atts['dtx-default'] = wpcf7dtx_get_dynamic(html_entity_decode(urldecode($tag->get_option('default', '', true)), ENT_QUOTES));
}
// Get options for selection-based fields
$options = array();
$pipes = $tag->pipes->to_array();
if (count($pipes)) {
foreach ($pipes as $pipe) {
$key = trim(strval($pipe[0]));
$value = trim(strval($pipe[1]));
if ($key && $value) {
$options[$key] = $value;
}
}
}
if ($atts['type'] == 'select' && $tag->has_option('include_blank')) {
$atts['placeholder'] = wpcf7dtx_array_has_key('placeholder', $atts, __('&#8212;Please choose an option&#8212;', 'contact-form-7-dynamic-text-extension'));
}
} else {
// Disable autocomplete for this field if a value has been specified
/**
* Configuration for text-based fields
*/
// Attributes
$atts['maxlength'] = $tag->get_maxlength_option();
$atts['minlength'] = $tag->get_minlength_option();
if ($atts['maxlength'] && $atts['minlength'] && $atts['maxlength'] < $atts['minlength']) {
unset($atts['maxlength'], $atts['minlength']);
}
// Autocomplete attribute
if ($atts['type'] == 'hidden') {
$atts['autocomplete'] = 'off'; // Always disable for hidden fields
} else {
// Disable autocomplete for this field if a dynamic value has been specified
$atts['autocomplete'] = $atts['value'] ? 'off' : $tag->get_option('autocomplete', '[-0-9a-zA-Z]+', true);
}
switch ($atts['type']) {
case 'email':
case 'url':
case 'tel':
case 'number':
case 'date':
// Client-side validation by type
$atts['class'][] = sanitize_html_class('wpcf7-validates-as-' . $atts['type']);
break;
case 'range':
// Client-side validation by type
$atts['class'][] = 'wpcf7-validates-as-number';
break;
case 'textarea':
// Attributes unique to textareas
$atts['cols'] = $tag->get_cols_option('40');
$atts['rows'] = $tag->get_rows_option('10');
break;
}
}
// Wrap up class attribute
$atts['class'] = $tag->get_class_option($atts['class']);
// Output the form field HTML
$wrapper = '<span class="wpcf7-form-control-wrap %1$s" data-name="%1$s">%2$s%3$s</span>';
$allowed_html = array('br' => array(), 'span' => array('id' => array(), 'class' => array(), 'data-name' => array(), 'aria-hidden' => array()));
switch ($atts['type']) {
case 'checkbox':
case 'radio':
return wp_kses(sprintf(
str_replace('<span class=', '<span%4$s class=', $wrapper), // Insert a 4th parameter for wrapper
esc_attr($tag->name),
wpcf7dtx_checkbox_group_html(
$atts,
$options,
in_array('use_label_element', $tag->options),
in_array('label_first', $tag->options),
in_array('exclusive', $tag->options)
),
$validation_error,
$atts['id'] ? sprintf(' id="%s"', esc_attr($atts['id'])) : ''
), array_merge($allowed_html, array(
'label' => array('for' => array()),
'input' => wpcf7dtx_get_allowed_field_properties($atts['type'])
)));
case 'select':
$allowed_html = array_merge($allowed_html, wpcf7dtx_get_allowed_field_properties('option'), array(
'select' => wpcf7dtx_get_allowed_field_properties($atts['type'])
));
return wp_kses(sprintf(
$wrapper,
esc_attr($tag->name),
wpcf7dtx_select_html(
$atts,
$options,
$tag->has_option('dtx_hide_blank'),
$tag->has_option('dtx_disable_blank')
),
$validation_error
), array_merge($allowed_html, wpcf7dtx_get_allowed_field_properties('option'), array(
'select' => wpcf7dtx_get_allowed_field_properties($atts['type']),
)));
case 'textarea':
return wp_kses(sprintf(
$wrapper,
esc_attr($tag->name),
wpcf7dtx_textarea_html($atts),
$validation_error
), array_merge($allowed_html, array(
'textarea' => wpcf7dtx_get_allowed_field_properties($atts['type'])
)));
default:
return wp_kses(sprintf(
$wrapper,
esc_attr($tag->name),
wpcf7dtx_input_html($atts),
$validation_error
), array_merge($allowed_html, array(
'input' => wpcf7dtx_get_allowed_field_properties($atts['type'])
)));
}
}
/**
* Form Tag Handler for Dynamic Submit
*
* @param WPCF7_FormTag $tag Current Contact Form 7 tag object
*
* @return string HTML output of the shortcode
*/
function wpcf7dtx_button_shortcode_handler($tag)
{
//Configure input attributes
$atts = array();
$atts['type'] = sanitize_key(str_replace('dynamic_', '', $tag->basetype));
$atts['id'] = strval($tag->get_id_option());
$atts['tabindex'] = $tag->get_option('tabindex', 'signed_int', true);
$atts['value'] = wpcf7dtx_get_dynamic(false, $tag); // Evaluate the dynamic value
$atts['class'] = explode(' ', wpcf7_form_controls_class($atts['type']));
$atts['class'][] = 'wpcf7dtx';
$atts['class'][] = sanitize_html_class('wpcf7dtx-' . $atts['type']);
if ($atts['type'] == 'submit') {
$atts['class'][] = 'has-spinner';
}
// Default value if empty
if (empty($atts['value'])) {
switch ($atts['type']) {
case 'reset':
$atts['value'] = __('Clear', 'contact-form-7-dynamic-text-extension');
break;
default:
$atts['value'] = __('Send', 'contact-form-7-dynamic-text-extension');
break;
}
}
// Page load attribute
if ($tag->has_option('dtx_pageload')) {
$atts['data-dtx-value'] = rawurlencode(sanitize_text_field(implode('', (array)$tag->raw_values)));
$class .= ' dtx-pageload';
if ($tag->has_option('dtx_pageload') && is_array($tag->raw_values) && count($tag->raw_values)) {
$atts['data-dtx-value'] = rawurlencode(sanitize_text_field($tag->raw_values[0]));
$atts['class'][] = 'dtx-pageload';
if (wp_style_is('wpcf7dtx', 'registered') && !wp_script_is('wpcf7dtx', 'queue')) {
// If already registered, just enqueue it
wp_enqueue_script('wpcf7dtx');
@@ -235,55 +480,15 @@ function wpcf7dtx_dynamictext_shortcode_handler($tag)
}
// Wrap up class attribute
$atts['class'] = $tag->get_class_option($class);
$atts['class'] = $tag->get_class_option($atts['class']);
//Output the HTML
return sprintf(
'<span class="wpcf7-form-control-wrap %s" data-name="%s"><input %s />%s</span>',
sanitize_html_class($tag->name),
esc_attr($tag->name),
wpcf7_format_atts($atts), //This function already escapes attribute values
$validation_error
// Output the form field HTML
return wp_kses(
wpcf7dtx_input_html($atts),
array('input' => wpcf7dtx_get_allowed_field_properties($atts['type']))
);
}
/**
* Validate Required Dynamic Text Field
*
* @param WPCF7_Validation $result the current validation result object
* @param WPCF7_FormTag $tag the current form tag being filtered for validation
*
* @return WPCF7_Validation a possibly modified validation result object
*/
function wpcf7dtx_dynamictext_validation_filter($result, $tag)
{
//Sanitize value
$value = empty($_POST[$tag->name]) ? '' : sanitize_text_field(strval($_POST[$tag->name]));
//Validate
if ('dynamictext' == $tag->basetype) {
if ($tag->is_required() && '' == $value) {
$result->invalidate($tag, wpcf7_get_message('invalid_required'));
}
}
if (!empty($value)) {
$maxlength = $tag->get_maxlength_option();
$minlength = $tag->get_minlength_option();
if ($maxlength && $minlength && $maxlength < $minlength) {
$maxlength = $minlength = null;
}
$code_units = wpcf7_count_code_units($value);
if (false !== $code_units) {
if ($maxlength && $maxlength < $code_units) {
$result->invalidate($tag, wpcf7_get_message('invalid_too_long'));
} elseif ($minlength && $code_units < $minlength) {
$result->invalidate($tag, wpcf7_get_message('invalid_too_short'));
}
}
}
return $result;
}
/**
* AJAX Request Handler for After Page Loading
*
@@ -296,16 +501,14 @@ function wpcf7dtx_dynamictext_validation_filter($result, $tag)
function wpcf7dtx_js_handler()
{
$return = array();
$shortcodes = wpcf7dtx_array_has_key('shortcodes', $_POST);
if (is_array($shortcodes) && count($shortcodes)) {
foreach ($shortcodes as $raw_value) {
$value = sanitize_text_field(rawurldecode($raw_value));
if (!empty($value)) {
$value = wpcf7dtx_get_dynamic($value);
}
$queue = wpcf7dtx_array_has_key('shortcodes', $_POST);
if (is_array($queue) && count($queue)) {
foreach ($queue as $field) {
$multiline = wpcf7dtx_array_has_key('multiline', $field, false);
$raw_value = sanitize_text_field(rawurldecode(wpcf7dtx_array_has_key('value', $field)));
$return[] = array(
'raw_value' => esc_attr($raw_value),
'value' => esc_attr($value)
'value' => esc_attr(wpcf7dtx_get_dynamic($raw_value, false, $multiline ? 'textarea' : 'auto'))
);
}
}
@@ -322,6 +525,20 @@ if (is_admin()) {
}
/**
* Included Shortcodes
* Built-in Shortcodes
*/
include_once(WPCF7DTX_DIR . '/includes/shortcodes.php');
/**
* Website's custom shortcodes, if they exist
*/
$user_files = array(
constant('WP_CONTENT_DIR') . '/dtx.php', // e.g. C:\path\to\website\wp-content\dtx.php
get_template_directory() . '/dtx.php', // e.g. C:\path\to\website\wp-content/themes/parent-theme/dtx.php
get_stylesheet_directory() . '/dtx.php' // e.g. C:\path\to\website\wp-content/themes/child-theme/dtx.php
);
foreach ($user_files as $user_file) {
if (file_exists($user_file)) {
include_once($user_file);
}
}

View File

@@ -16,13 +16,13 @@ function wpcf7dtx_enqueue_admin_assets($hook)
// Only load on CF7 Form pages (both editing forms and creating new forms)
if ($hook === 'toplevel_page_wpcf7' || $hook === 'contact_page_wpcf7-new') {
$prefix = 'wpcf7dtx-';
$debug = defined('WP_DEBUG') && constant('WP_DEBUG');
$debug = defined('WP_DEBUG_SCRIPTS') && constant('WP_DEBUG_SCRIPTS');
$url = plugin_dir_url(WPCF7DTX_FILE);
$path = plugin_dir_path(WPCF7DTX_FILE);
wp_enqueue_style(
$prefix . 'admin', //Handle
$url . 'assets/styles/tag-generator.css', //Source
$url . 'assets/styles/tag-generator' . ($debug ? '' : '.min') . '.css', //Source
array('contact-form-7-admin'), //Dependencies
$debug ? @filemtime($path . 'assets/styles/tag-generator.css') : WPCF7DTX_VERSION //Version
);
@@ -44,30 +44,22 @@ add_action('admin_enqueue_scripts', 'wpcf7dtx_enqueue_admin_assets'); //Enqueue
*
* @return void
*/
function wpcf7dtx_add_tag_generator_dynamictext()
function wpcf7dtx_add_tag_generators()
{
if (!class_exists('WPCF7_TagGenerator')) {
return;
}
// Custom dynamic fields to add
global $wpcf7_dynamic_fields_config;
// Loop fields to add them
$tag_generator = WPCF7_TagGenerator::get_instance();
//Dynamic Text Field
$tag_generator->add(
'dynamictext', //id
__('dynamic text', 'contact-form-7-dynamic-text-extension'), //title
'wpcf7dtx_tag_generator_dynamictext', //callback
array('placeholder', 'readonly', 'dtx_pageload') //options
);
//Dynamic Hidden Field
$tag_generator->add(
'dynamichidden', //id
__('dynamic hidden', 'contact-form-7-dynamic-text-extension'), //title
'wpcf7dtx_tag_generator_dynamictext', //callback
array('dtx_pageload') // options
);
foreach ($wpcf7_dynamic_fields_config as $id => $field) {
$tag_generator->add($id, $field['title'], 'wpcf7dtx_tag_generator', array_merge(array('name-attr', 'dtx_pageload'), $field['options']));
}
}
add_action('wpcf7_admin_init', 'wpcf7dtx_add_tag_generator_dynamictext', 100);
add_action('wpcf7_admin_init', 'wpcf7dtx_add_tag_generators', 100);
/**
* Echo HTML for Dynamic Tag Generator
@@ -77,36 +69,43 @@ add_action('wpcf7_admin_init', 'wpcf7dtx_add_tag_generator_dynamictext', 100);
*
* @return void
*/
function wpcf7dtx_tag_generator_dynamictext($contact_form, $options = '')
function wpcf7dtx_tag_generator($contact_form, $options = '')
{
$options = wp_parse_args($options);
global $wpcf7_dynamic_fields_config;
$type = $options['id'];
switch ($type) {
case 'dynamichidden': //hiden
$description = __('Generate a form-tag for a hidden input field, with a dynamically generated default value.', 'contact-form-7-dynamic-text-extension');
break;
default:
$description = __('Generate a form-tag for a single-line plain text input field, with a dynamically generated default value.', 'contact-form-7-dynamic-text-extension');
break;
}
$input_type = str_replace('dynamic_', '', $type);
$utm_source = urlencode(home_url());
$description .= sprintf(
' %s <a href="https://aurisecreative.com/docs/contact-form-7-dynamic-text-extension/?utm_source=%s&utm_medium=link&utm_campaign=contact-form-7-dynamic-text-extension&utm_content=form-tag-generator-%s" title="%s" target="_blank" rel="noopener">%s</a>.',
esc_html__('For more details, see', 'contact-form-7-dynamic-text-extension'),
$description = sprintf(
__('Generate a form-tag for %s with a dynamic default value. For more details, see %s fields in the %s.', 'contact-form-7-dynamic-text-extension'),
esc_html($wpcf7_dynamic_fields_config[$type]['description']), // dynamic description
// Link to specific form-tag documentation
sprintf(
'<a href="https://aurisecreative.com/docs/contact-form-7-dynamic-text-extension/form-tags/%s?utm_source=%s&utm_medium=link&utm_campaign=contact-form-7-dynamic-text-extension&utm_content=form-tag-generator-%s" title="%s" target="_blank" rel="noopener">%s</a>',
esc_attr(str_replace('_', '-', $type)), // URL component
esc_attr($utm_source), //UTM source
esc_attr($type), //UTM content
esc_attr__('View this form-tag on the DTX Documentation website', 'contact-form-7-dynamic-text-extension'), // Link title
esc_html(ucwords(str_replace('_', ' ', $type))) // Link label
),
// Link to general DTX documentation
sprintf(
'<a href="https://aurisecreative.com/docs/contact-form-7-dynamic-text-extension/?utm_source=%s&utm_medium=link&utm_campaign=contact-form-7-dynamic-text-extension&utm_content=form-tag-generator-%s" title="%s" target="_blank" rel="noopener">%s</a>',
esc_attr($utm_source), //UTM source
esc_attr($type), //UTM content
esc_attr__('Go to DTX Documentation website', 'contact-form-7-dynamic-text-extension'),
esc_html__('DTX knowledge base', 'contact-form-7-dynamic-text-extension')
)
);
//Open Form-Tag Generator
// Open Form-Tag Generator
printf(
'<div class="control-box"><fieldset><legend>%s</legend><table class="form-table"><tbody>',
'<div class="control-box dtx-taggen"><fieldset><legend>%s</legend><table class="form-table"><tbody>',
wp_kses($description, array('a' => array('href' => array(), 'target' => array(), 'rel' => array(), 'title' => array()))) //Tag generator description
);
//Input field - Required checkbox (not available for hidden fields)
if ($type != 'dynamichidden') {
// Input field - Required checkbox (not available for some fields)
if (!in_array($input_type, array('hidden', 'quiz', 'submit', 'reset'))) {
printf(
'<tr><th scope="row"><label for="%s">%s</label></th><td><label><input %s />%s</label></td></tr>',
esc_attr($options['content'] . '-required'), // field id
@@ -120,7 +119,8 @@ function wpcf7dtx_tag_generator_dynamictext($contact_form, $options = '')
);
}
//Input field - Field Name
// Input field - Field Name (not available for some fields)
if (!in_array($input_type, array('submit', 'reset'))) {
printf(
'<tr><th scope="row"><label for="%s">%s</label></th><td><input %s /></td></tr>',
esc_attr($options['content'] . '-name'), // field id
@@ -129,53 +129,243 @@ function wpcf7dtx_tag_generator_dynamictext($contact_form, $options = '')
'type' => 'text',
'name' => 'name',
'id' => $options['content'] . '-name',
'class' => 'tg-name oneline'
'class' => 'tg-name oneline',
'autocomplete' => 'off'
))
);
}
//Input field - Dynamic value
// Input field - Dynamic value/options
$value_name = __('Dynamic value', 'contact-form-7-dynamic-text-extension');
$value_description = __('Can be static text or a shortcode.', 'contact-form-7-dynamic-text-extension');
$value_placeholder = "CF7_GET key='foo'";
$value_input_type = '<input %s />';
switch ($input_type) {
case 'textarea':
$value_placeholder = "CF7_get_post_var key='post_excerpt'";
$value_input_type = '<textarea %s></textarea>';
break;
case 'select':
$value_name = __('Dynamic options', 'contact-form-7-dynamic-text-extension');
$value_description .= ' ' . __('If static text, use one option per line. Can define static key/value pairs using pipes.', 'contact-form-7-dynamic-text-extension');
$value_description .= ' ' . __('If shortcode, it must return only option or optgroup HTML and can override the first option and select default settings here.', 'contact-form-7-dynamic-text-extension');
$value_placeholder = "hello-world | Hello World" . PHP_EOL . "Foo";
$value_input_type = '<textarea %s></textarea>';
break;
case 'checkbox':
case 'radio':
$value_name = __('Dynamic options', 'contact-form-7-dynamic-text-extension');
$value_description .= ' ' . __('If static text, use one option per line. Can define static key/value pairs using pipes.', 'contact-form-7-dynamic-text-extension');
$value_description .= ' ' . __('If shortcode, it must return only option or optgroup HTML.', 'contact-form-7-dynamic-text-extension');
$value_placeholder = "hello-world | Hello World" . PHP_EOL . "Foo";
$value_input_type = '<textarea %s></textarea>';
break;
default: // All other text fields
break;
}
printf(
'<tr><th scope="row"><label for="%s">%s</label></th><td><input %s /><br /><small>%s <a href="https://aurisecreative.com/docs/contact-form-7-dynamic-text-extension/shortcodes/?utm_source=%s&utm_medium=link&utm_campaign=contact-form-7-dynamic-text-extension&utm_content=form-tag-generator-%s" target="_blank" rel="noopener">%s</a></small></td></tr>',
'<tr><th scope="row"><label for="%s">%s</label></th><td>' . $value_input_type . '<br /><small>%s <a href="https://aurisecreative.com/docs/contact-form-7-dynamic-text-extension/shortcodes/?utm_source=%s&utm_medium=link&utm_campaign=contact-form-7-dynamic-text-extension&utm_content=form-tag-generator-%s" target="_blank" rel="noopener">%s</a></small></td></tr>',
esc_attr($options['content'] . '-values'), // field id
esc_html__('Dynamic value', 'contact-form-7-dynamic-text-extension'), // field label
esc_html($value_name), // field label
wpcf7_format_atts(array(
'type' => 'text',
'name' => 'values',
'id' => $options['content'] . '-values',
'class' => 'oneline',
'placeholder' => "CF7_GET key='foo'"
'class' => 'multiline',
'placeholder' => $value_placeholder,
'list' => 'dtx-shortcodes'
)),
esc_html__('Can be static text or a shortcode.', 'contact-form-7-dynamic-text-extension'),
esc_attr($utm_source), //UTM source
esc_attr($type), //UTM content
esc_html__('View DTX shortcode syntax documentation', 'contact-form-7-dynamic-text-extension') //Link label
esc_html($value_description),
esc_attr($utm_source), // UTM source
esc_attr($type), // UTM content
esc_html__('View DTX shortcode syntax documentation', 'contact-form-7-dynamic-text-extension') // Link label
);
//Input field - Dynamic placeholder (not available for hidden fields)
if ($type != 'dynamichidden') {
if ($input_type == 'select') {
// Input field - Multiple selections checkbox
printf(
'<tr><th scope="row"><label for="%s">%s</label></th><td><input %s /><input %s /><br /><small>%s <a href="https://aurisecreative.com/docs/contact-form-7-dynamic-text-extension/shortcodes/dtx-attribute-placeholder/?utm_source=%s&utm_medium=link&utm_campaign=contact-form-7-dynamic-text-extension&utm_content=form-tag-generator-%s" target="_blank" rel="noopener">%s</a></small></td></tr>',
'<tr><th scope="row"><label for="%s">%s</label></th><td><label><input %s />%s</label></td></tr>',
esc_attr($options['content'] . '-multiple'), // field id
esc_html__('Multiple Options', 'contact-form-7-dynamic-text-extension'), // field Label
wpcf7_format_atts(array(
'type' => 'checkbox',
'name' => 'multiple',
'id' => $options['content'] . '-multiple',
'class' => 'option'
)),
esc_html__('Allow user to select multiple options', 'contact-form-7-dynamic-text-extension') // checkbox label
);
// Input field - Include blank checkbox
printf(
'<tr><th scope="row"><label for="%s">%s</label></th><td><label><input %s />%s</label></td></tr>',
esc_attr($options['content'] . '-include_blank'), // field id
esc_html__('First Option', 'contact-form-7-dynamic-text-extension'), // field Label
wpcf7_format_atts(array(
'type' => 'checkbox',
'name' => 'include_blank',
'id' => $options['content'] . '-include_blank',
'class' => 'include_blankvalue option'
)),
esc_html__('Insert a blank item as the first option', 'contact-form-7-dynamic-text-extension') // checkbox label
);
}
// Input field - Dynamic placeholder (not available for some fields)
if (!in_array($input_type, array('hidden', 'radio', 'checkbox', 'quiz', 'submit', 'reset'))) {
$placeholder_description = '';
if (in_array($input_type, array('select', 'checkbox', 'radio'))) {
$placeholder_label = __('First Option Label', 'contact-form-7-dynamic-text-extension');
$placeholder_description .= __('Optionally define a label for the first option.', 'contact-form-7-dynamic-text-extension') . ' ';
} else {
$placeholder_label = __('Dynamic placeholder', 'contact-form-7-dynamic-text-extension');
}
$placeholder_description .= __('Can be static text or a shortcode.', 'contact-form-7-dynamic-text-extension');
$placeholder_input_type = $input_type == 'textarea' ? $value_input_type : '<input %s />';
printf(
'<tr><th scope="row"><label for="%s">%s</label></th><td><input %s />' . $placeholder_input_type . '<br /><small>%s <a href="https://aurisecreative.com/docs/contact-form-7-dynamic-text-extension/shortcodes/dtx-attribute-placeholder/?utm_source=%s&utm_medium=link&utm_campaign=contact-form-7-dynamic-text-extension&utm_content=form-tag-generator-%s" target="_blank" rel="noopener">%s</a></small></td></tr>',
esc_attr($options['content'] . '-placeholder'), // field id
esc_html__('Dynamic placeholder', 'contact-form-7-dynamic-text-extension'), // field label
esc_html($placeholder_label), // field label
wpcf7_format_atts(array(
'type' => 'hidden',
'name' => 'placeholder',
'class' => 'option'
)),
wpcf7_format_atts(array(
'type' => 'text',
'name' => 'dtx-placeholder',
'id' => $options['content'] . '-placeholder', // field id
'class' => 'oneline dtx-option',
'placeholder' => 'CF7_get_post_var key=\'post_title\''
'class' => 'multiline dtx-option',
'placeholder' => "CF7_get_post_var key='post_title'",
'list' => 'dtx-shortcodes'
)),
esc_html__('Can be static text or a shortcode.', 'contact-form-7-dynamic-text-extension'), // Small note below input
esc_html($placeholder_description), // Small note below input
esc_attr($utm_source), //UTM source
esc_attr($type), //UTM content
esc_html__('View DTX placeholder documentation', 'contact-form-7-dynamic-text-extension') //Link label
);
}
// Additional fields for select regarding placeholder options
if ($input_type == 'select') {
// Input field - Hide Blank Option
printf(
'<tr><th scope="row"><label for="%s">%s</label></th><td><label><input %s />%s</label><br /><small>%s <a href="https://aurisecreative.com/docs/contact-form-7-dynamic-text-extension/form-tags/dynamic-select/?utm_source=%s&utm_medium=link&utm_campaign=contact-form-7-dynamic-text-extension&utm_content=form-tag-generator-%s" target="_blank" rel="noopener">%s</a></small></td></tr>',
esc_attr($options['content'] . '-dtx_hide_blank'), // field id
esc_html__('Hide First Option', 'contact-form-7-dynamic-text-extension'), // field Label
wpcf7_format_atts(array(
'type' => 'checkbox',
'name' => 'dtx_hide_blank',
'id' => $options['content'] . '-dtx_hide_blank',
'class' => 'option'
)),
esc_html__('Hide the first blank option from being visible in the drop-down', 'contact-form-7-dynamic-text-extension'), // checkbox label
esc_html__('Optional. Only works if "First Option" is checked.', 'contact-form-7-dynamic-text-extension'), // Small note below input
esc_attr($utm_source), //UTM source
esc_attr($type), //UTM content
esc_html__('View Dynamic Select documentation', 'contact-form-7-dynamic-text-extension') //Link label
);
// Input field - Disable Blank Option
printf(
'<tr><th scope="row"><label for="%s">%s</label></th><td><label><input %s />%s</label><br /><small>%s <a href="https://aurisecreative.com/docs/contact-form-7-dynamic-text-extension/form-tags/dynamic-select/?utm_source=%s&utm_medium=link&utm_campaign=contact-form-7-dynamic-text-extension&utm_content=form-tag-generator-%s" target="_blank" rel="noopener">%s</a></small></td></tr>',
esc_attr($options['content'] . '-dtx_disable_blank'), // field id
esc_html__('Disable First Option', 'contact-form-7-dynamic-text-extension'), // field Label
wpcf7_format_atts(array(
'type' => 'checkbox',
'name' => 'dtx_disable_blank',
'id' => $options['content'] . '-dtx_disable_blank',
'class' => 'option'
)),
esc_html__('Disable the first blank option from being selectable in the drop-down', 'contact-form-7-dynamic-text-extension'), // checkbox label
esc_html__('Optional. Only works if "First Option" is checked.', 'contact-form-7-dynamic-text-extension'), // Small note below input
esc_attr($utm_source), //UTM source
esc_attr($type), //UTM content
esc_html__('View Dynamic Select documentation', 'contact-form-7-dynamic-text-extension') //Link label
);
} elseif (in_array($input_type, array('checkbox', 'radio'))) {
// Additional fields for checkboxes and radio buttons
// Input field - Checkbox Layout Reverse Option
printf(
'<tr><th scope="row"><label for="%s">%s</label></th><td><label><input %s />%s</label></td></tr>',
esc_attr($options['content'] . '-label_first'), // field id
esc_html__('Reverse', 'contact-form-7-dynamic-text-extension'), // field Label
wpcf7_format_atts(array(
'type' => 'checkbox',
'name' => 'label_first',
'id' => $options['content'] . '-label_first',
'class' => 'option'
)),
esc_html__('Put a label first, an input last', 'contact-form-7-dynamic-text-extension') // checkbox label
);
// Input field - Label UI
printf(
'<tr><th scope="row"><label for="%s">%s</label></th><td><label><input %s />%s</label></td></tr>',
esc_attr($options['content'] . '-use_label_element'), // field id
esc_html__('Label', 'contact-form-7-dynamic-text-extension'), // field Label
wpcf7_format_atts(array(
'type' => 'checkbox',
'name' => 'use_label_element',
'id' => $options['content'] . '-use_label_element',
'class' => 'option'
)),
esc_html__('Wrap each item with label element', 'contact-form-7-dynamic-text-extension') // checkbox label
);
// Input field - Exclusive Checkbox
if ($input_type == 'checkbox') {
printf(
'<tr><th scope="row"><label for="%s">%s</label></th><td><label><input %s />%s</label></td></tr>',
esc_attr($options['content'] . '-exclusive'), // field id
esc_html__('Exclusive', 'contact-form-7-dynamic-text-extension'), // field Label
wpcf7_format_atts(array(
'type' => 'checkbox',
'name' => 'exclusive',
'id' => $options['content'] . '-exclusive',
'class' => 'option'
)),
esc_html__('Make checkboxes exclusive', 'contact-form-7-dynamic-text-extension') // checkbox label
);
}
}
// Input field - Dynamic default value (not available for some fields)
if (in_array($input_type, array('select'))) {
$default_input_type = '<input %s />';
$default_placeholder = '';
if ($input_type == 'checkbox') {
$default_input_type = '<textarea %s></textarea>';
$default_description = __('Optionally define the default on/off status of the checkboxes by putting a 1 (checked) or 0 (not checked) on each line that corresponds with the options.', 'contact-form-7-dynamic-text-extension') . ' ';
$default_placeholder = '0' . PHP_EOL . '1';
} else {
$default_description = __('Optionally define the option that is selected by default. This can be different than the first [blank] option. If options use key/value pairs, only define the key here.', 'contact-form-7-dynamic-text-extension') . ' ';
}
$default_description .= __('Can be static text or a shortcode.', 'contact-form-7-dynamic-text-extension');
printf(
'<tr><th scope="row"><label for="%s">%s</label></th><td><input %s />' . $default_input_type . '<br /><small>%s <a href="https://aurisecreative.com/docs/contact-form-7-dynamic-text-extension/form-tags/dynamic-select/?utm_source=%s&utm_medium=link&utm_campaign=contact-form-7-dynamic-text-extension&utm_content=form-tag-generator-%s" target="_blank" rel="noopener">%s</a></small></td></tr>',
esc_attr($options['content'] . '-default'), // field id
esc_html__('Selected Default'), // field label
wpcf7_format_atts(array(
'type' => 'hidden',
'name' => 'default',
'class' => 'option'
)),
wpcf7_format_atts(array(
'name' => 'dtx-default',
'id' => $options['content'] . '-default', // field id
'class' => 'oneline dtx-option',
'placeholder' => $default_placeholder,
'list' => 'dtx-shortcodes'
)),
esc_html($default_description), // Small note below input
esc_attr($utm_source), //UTM source
esc_attr($type), //UTM content
esc_html__('View Dynamic Select documentation', 'contact-form-7-dynamic-text-extension') //Link label
);
}
//Input field - ID attribute
printf(
'<tr><th scope="row"><label for="%s">%s</label></th><td><input %s /></td></tr>',
@@ -202,8 +392,8 @@ function wpcf7dtx_tag_generator_dynamictext($contact_form, $options = '')
))
);
//Input field - Readonly attribute (not available for hidden fields)
if ($type != 'dynamichidden') {
//Input field - Readonly attribute (not available for hidden, submit, or quiz fields)
if (!in_array($input_type, array('hidden', 'submit', 'quiz'))) {
printf(
'<tr><th scope="row"><label for="%s">%s</label></th><td><label><input %s />%s</label></td></tr>',
esc_attr($options['content'] . '-readonly'), // field id
@@ -218,25 +408,55 @@ function wpcf7dtx_tag_generator_dynamictext($contact_form, $options = '')
);
}
// Page load data attribute (triggers the loading of a frontend script)
// Input field - Page load data attribute (triggers the loading of a frontend script)
printf(
'<tr><th scope="row"><label for="%s">%s</label></th><td><label><input %s />%s</label><br /><small>%s <a href="https://aurisecreative.com/docs/contact-form-7-dynamic-text-extension/form-tag-attribute-after-page-load/?utm_source=%s&utm_medium=link&utm_campaign=contact-form-7-dynamic-text-extension&utm_content=form-tag-generator-%s" target="_blank" rel="noopener">%s</a></small></td></tr>',
esc_attr($options['content'] . '-frontend'), // field id
esc_attr($options['content'] . '-dtx_pageload'), // field id
esc_html__('Cache Compatible', 'contact-form-7-dynamic-text-extension'), // field Label
wpcf7_format_atts(array(
'type' => 'checkbox',
'name' => 'dtx_pageload',
'id' => $options['content'] . '-dtx_pageload',
'class' => 'dtx_pageloadvalue option'
'class' => 'option'
)),
esc_html__('Get the dynamic value after the page has loaded', 'contact-form-7-dynamic-text-extension'), // checkbox label
esc_html('May impact page performance.', 'contact-form-7-dynamic-text-extension'), // Small note below input
esc_html__('May impact page performance.', 'contact-form-7-dynamic-text-extension'), // Small note below input
esc_attr($utm_source), //UTM source
esc_attr($type), //UTM content
esc_html__('View DTX page load documentation', 'contact-form-7-dynamic-text-extension') //Link label
);
// Input field - Akismet module (only available for text, email, and url fields)
if (in_array($input_type, array('text', 'email', 'url'))) {
switch ($input_type) {
case 'email':
$akismet_name = 'author_email';
$akismet_desc = __("This field requires author's email address", 'contact-form-7-dynamic-text-extension');
break;
case 'url':
$akismet_name = 'author_url';
$akismet_desc = __("This field requires author's URL", 'contact-form-7-dynamic-text-extension');
break;
default:
$akismet_name = 'author';
$akismet_desc = __("This field requires author's name", 'contact-form-7-dynamic-text-extension');
break;
}
printf(
'<tr><th scope="row"><label for="%s">%s</label></th><td><label><input %s />%s</label></td></tr>',
esc_attr($options['content'] . '-readonly'), // field id
esc_html__('Akismet', 'contact-form-7-dynamic-text-extension'), // field Label
wpcf7_format_atts(array(
'type' => 'checkbox',
'name' => 'akismet:' . $akismet_name,
'id' => $options['content'] . '-akismet-' . $akismet_name,
'class' => 'akismetvalue option'
)),
esc_html($akismet_desc) // checkbox label
);
}
//Close Form-Tag Generator
printf(
'</tbody></table></fieldset></div><div class="insert-box"><input type="text" name="%s" class="tag code" readonly="readonly" onfocus="this.select()" /><div class="submitbox"><input type="button" class="button button-primary insert-tag" value="%s" /></div><br class="clear" /></div>',

View File

@@ -50,6 +50,9 @@ add_filter('wpcf7dtx_allow_protocols', 'wpcf7dtx_allow_protocols', 10, 2);
*/
function wpcf7dtx_sanitize($value = '', $type = 'auto', $protocols = false)
{
if ($type == 'none') {
return $value;
}
$value = is_string($value) ? $value : strval($value); // Force string value
if (!empty($value)) {
$type = $type == 'auto' ? wpcf7dtx_detect_value_type($value) : sanitize_text_field($type);
@@ -62,6 +65,8 @@ function wpcf7dtx_sanitize($value = '', $type = 'auto', $protocols = false)
return sanitize_key($value);
case 'slug':
return sanitize_title($value);
case 'textarea':
return sanitize_textarea_field($value);
}
}
return sanitize_text_field($value);
@@ -82,6 +87,9 @@ add_filter('wpcf7dtx_sanitize', 'wpcf7dtx_sanitize', 10, 3);
*/
function wpcf7dtx_escape($value = '', $obfuscate = false, $type = 'auto', $protocols = false)
{
if ($type == 'none') {
return $value;
}
$value = apply_filters('wpcf7dtx_sanitize', $value, $type); // Sanitize value
if (!empty($value)) {
if ($obfuscate) {
@@ -91,6 +99,8 @@ function wpcf7dtx_escape($value = '', $obfuscate = false, $type = 'auto', $proto
switch ($type) {
case 'url':
return esc_url($value, apply_filters('wpcf7dtx_allow_protocols', $protocols));
case 'textarea':
return esc_textarea($value);
}
}
return esc_attr($value); // Return attribute value
@@ -182,51 +192,449 @@ function wpcf7dtx_get_post_id($post_id, $context = 'dtx')
}
/**
* Parse Content for Specified Shortcodes
* Get Dynamic Value
*
* Parse a string of content for a specific shortcode to retrieve its attributes and content
* @since 3.2.2
*
* @since 3.1.0
* @param string $value The form tag value.
* @param WPCF7_FormTag|false $tag Optional. Use to look up default value.
* @param string $sanitize Optional. Specify type of sanitization. Default is `auto`.
*
* @param string $content The content to parse
* @param string $tag The shortcode tag
*
* @return array An associative array with `tag` (string) and `shortcodes` (sequential array). If shortcodes were discovered, each one has keys for `atts` (associative array) and `content` (string)
* @return string The dynamic output or the original value, not escaped or sanitized.
*/
function wpcf7dtx_get_shortcode_atts($content)
function wpcf7dtx_get_dynamic($value, $tag = false, $sanitize = 'auto')
{
$return = array(
'tag' => '',
'atts' => array()
if ($tag !== false) {
$default = $tag->get_option('defaultvalue', '', true);
if (!$default) {
$default = $tag->get_default_option(strval(reset($tag->values)));
}
$value = wpcf7_get_hangover($tag->name, $default);
}
$value = apply_filters('wpcf7dtx_sanitize', $value, $sanitize);
if (is_string($value) && !empty($value)) {
// If a shortcode was passed as the value, evaluate it and use the result
$shortcode_tag = '[' . $value . ']';
$shortcode_output = do_shortcode($shortcode_tag); //Shortcode value
if (is_string($shortcode_output) && $shortcode_output != $shortcode_tag) {
return apply_filters('wpcf7dtx_sanitize', $shortcode_output, $sanitize);
}
}
return $value;
}
/**
* Get Allowed HTML for Form Field Properties
*
* @since 4.0.0
*
* @param string $type Optional. The type of input for unique properties. Default is `text`.
* @param array $extra Optional. A sequential array of properties to additionally include.
*
* @return array An associative array of allowed properties appropriate for use in `wp_kses()`
*/
function wpcf7dtx_get_allowed_field_properties($type = 'text', $extra = array())
{
if (in_array($type, array('option', 'optgroup'))) {
return array(
'optgroup' => array(
'label' => array(),
'disabled' => array(),
'hidden' => array()
),
'option' => array(
'value' => array(),
'selected' => array(),
'disabled' => array(),
'hidden' => array()
)
);
//Search for shortcodes with attributes
if (false !== ($start = strpos($content, ' '))) {
$return['tag'] = substr($content, 0, $start); //Opens the start tag, assumes there are attributes because of the space
}
$allowed_properties = array(
// Global properties
'type' => array(),
'id' => array(),
'name' => array(),
'value' => array(),
'required' => array(),
'class' => array(),
'disabled' => array(),
'readonly' => array(),
'tabindex' => array(),
'size' => array(),
'title' => array(),
'autofocus' => array(),
// ARIA properties
'aria-invalid' => array(),
'aria-describedby' => array(),
// DTX properties
'data-dtx-value' => array(),
);
if (in_array($type, array('checkbox', 'radio', 'acceptance'))) {
// Properties exclusive to checkboxes and radio buttons
$allowed_properties['checked'] = array();
$allowed_properties['dtx-default'] = array();
} elseif (in_array($type, array('number', 'range'))) {
// Properties exclusive to number inputs
$allowed_properties['step'] = array();
} elseif ($type == 'select') {
// Properties exclusive to select fields
$allowed_properties['multiple'] = array();
$allowed_properties['dtx-default'] = array();
unset($allowed_properties['type'], $allowed_properties['value'], $allowed_properties['placeholder'], $allowed_properties['size']); // Remove invalid select attributes
}
if (!in_array($type, array('checkbox', 'radio', 'select', 'acceptance'))) {
// Allowed properties for all text-based inputs
$allowed_properties['placeholder'] = array();
$allowed_properties['autocomplete'] = array();
$allowed_properties['minlength'] = array();
$allowed_properties['maxlength'] = array();
if (in_array($type, array('number', 'range', 'date', 'datetime-local', 'time'))) {
// Additional properties for number and date inputs
$allowed_properties['min'] = array();
$allowed_properties['max'] = array();
}
if ($type == 'textarea') {
// Additional properties exclusive to textarea fields
$allowed_properties['cols'] = array();
$allowed_properties['rows'] = array();
unset($allowed_properties['type'], $allowed_properties['value']); // Remove invalid textarea attributes
} elseif (in_array($type, array('text', 'date', 'url', 'tel', 'email', 'password'))) {
// Additional properties exclusive to specific text fields
$allowed_properties['pattern'] = array();
}
}
if (is_array($extra) && count($extra)) {
foreach ($extra as $property) {
$allowed_properties[sanitize_title($property)] = array();
}
}
return $allowed_properties;
}
//Parse for shortcode attributes: `shortcode att1='foo' att2='bar'`
//Chop only the attributes e.g. `att1="foo" att2="bar"`
$atts_str = trim(str_replace($return['tag'], '', $content));
if (strpos($atts_str, "'") !== false) {
$atts = explode("' ", substr(
$atts_str,
0,
-1 //Clip off the last character, which is a single quote
));
/**
* Returns a formatted string of HTML attributes
*
* @since 4.0.0
*
* @param array $atts Associative array of attribute name and value pairs
*
* @return string Formatted HTML attributes with keys and values both escaped
*/
function wpcf7dtx_format_atts($atts)
{
if (is_array($atts) && count($atts)) {
foreach ($atts as $att_str) {
$pair = explode("='", $att_str);
if (is_array($pair) && count($pair) > 1) {
$key = sanitize_key(trim($pair[0])); //Validate & normalize the key
if (!empty($key)) {
$return['atts'][$key] = sanitize_text_field(html_entity_decode($pair[1]));
$sanitized_atts = array();
static $boolean_attributes = array(
'checked', 'disabled', 'multiple', 'readonly', 'required', 'selected'
);
foreach ($atts as $key => $value) {
$key = sanitize_key(strval($key));
if ($key) {
if (in_array($key, $boolean_attributes) || is_bool($value)) {
if ($value) {
$sanitized_atts[$key] = $key;
}
} elseif ($value && (is_string($value) || is_numeric($value))) {
$sanitized_atts[$key] = $value;
}
}
}
if (count($sanitized_atts)) {
$output = array();
foreach ($sanitized_atts as $key => $value) {
$output[] = sprintf('%s="%s"', esc_attr($key), esc_attr($value));
}
return implode(' ', $output);
}
}
return '';
}
/**
* Create Input Field HTML
*
* @since 4.0.0
*
* @param array $atts An associative array of input attributes.
*
* @return string HTML output of input field
*/
function wpcf7dtx_input_html($atts)
{
return sprintf('<input %s />', wpcf7dtx_format_atts($atts));
}
/**
* Create Checkbox Field HTML
*
* @since 4.0.0
*
* @param array $atts An associative array of select input attributes.
* @param string $label_text Optional. The text to display next to the checkbox or radio button.
* @param bool $label_ui Optional. If true, will place input and label text inside a `<label>` element. Default is true.
* @param bool $reverse Optional. If true, will reverse the order to display the text label first then the button. Has no effect if label text is empty. Default is false.
*
* @return string HTML output of the checkbox or radio button or empty string on failure.
*/
function wpcf7dtx_checkbox_html($atts, $label_text = '', $label_ui = true, $reverse = false)
{
// Default field attributes
$atts = array_merge(array('value' => '', 'dtx-default' => ''), array_change_key_case((array)$atts, CASE_LOWER));
if ($atts['value'] && $atts['dtx-default'] && $atts['value'] == $atts['dtx-default']) {
$atts['checked'] = 'checked';
}
$input = wpcf7dtx_input_html($atts);
if (!empty(trim($label_text))) {
$label_el = $label_ui ? 'span' : 'label'; // If not wrapping with a label element, display it next to it
$label_text = sprintf(
'<%1$s%2$s class="wpcf7-list-item-label">%3$s</%1$s>',
$label_el,
// If not wrapping with a label element and the element has an ID attribute, add a `for` attribute
$label_ui ? '' : (wpcf7dtx_array_has_key('id', $atts) ? ' for="' . esc_attr($atts['id']) . '"' : ''),
esc_html($label_text)
);
if ($reverse) {
$html = $label_text . $input;
} else {
$html = $input . $label_text;
}
} else {
$html = $input;
}
if ($label_ui) {
$html = '<label>' . $html . '</label>';
}
return $html;
}
/**
* Create Checkbox Group HTML
*
* @since 4.0.3
*
* @param array $atts An associative array of select input attributes.
* @param array|string $options Accepts an associative array of key/value pairs to use as the
* select option's value/label pairs. It also accepts an associative array of associative
* arrays with the keys being used as option group labels and the array values used as that
* group's options. It also accepts a string value of HTML already formatted as options or
* option groups. It also accepts a string value of a self-closing shortcode that is
* evaluated and its output is either options or option groups.
* @param bool $label_ui Optional. If true, will place input and label text inside a `<label>` element. Default is true.
* @param bool $reverse Optional. If true, will reverse the order to display the text label first then the button. Has no effect if label text is empty. Default is false.
*
* @return string HTML output of the checkbox or radio button or empty string on failure.
*/
function wpcf7dtx_checkbox_group_html($atts, $options, $label_ui = false, $reverse = false, $exclusive = false)
{
$group_html = '';
if ($count = count($options)) {
// Attributes specific to HTML creation
$atts = array_merge(array(
'type' => 'checkbox',
'id' => '',
'name' => '',
'value' => '',
'dtx-default' => ''
), array_change_key_case((array)$atts, CASE_LOWER));
// Loop all the options
$group_html = array();
$id_prefix = ($atts['id'] ? $atts['id'] : uniqid($atts['name'] . '_')) . '_'; // Create prefix from passed ID or Name
$i = 1;
foreach ($options as $value => $label) {
$my_atts = array_merge($atts, array(
'id' => sanitize_html_class($id_prefix . $i) // Always have unique IDs for group items
));
$dynamic_value = '';
$dynamic_label = $label;
if ($value && $label && $value === $label) {
// These are identical, just handle it as one, could also be a raw shortcode
$dynamic_option = trim(wpcf7dtx_get_dynamic($value, false, 'none')); // Do not sanitize yet, it may have HTML
if (is_string($dynamic_option) && !empty($dynamic_option) && strpos($dynamic_option, '{') === 0 && strpos($dynamic_option, '}') === strlen($dynamic_option) - 1) {
// If it outputs JSON, try parsing it
try {
$dynamic_option = json_decode($dynamic_option, true);
if (is_array($dynamic_option) && count($dynamic_option)) {
$group_html[] = wpcf7dtx_checkbox_group_html(
$my_atts,
$dynamic_option,
$label_ui,
$reverse,
$exclusive
);
}
} catch (Exception $e) {
// Fail quietly
if (WP_DEBUG && WP_DEBUG_LOG) {
error_log('[Contact Form 7 - Dynamic Text Extension] Error parsing JSON value');
error_log($e->getMessage());
}
}
$i++;
continue; // Continue with next iteration
} elseif (is_string($dynamic_option) && !empty($dynamic_option) && esc_html($dynamic_option) != $dynamic_option) {
$group_html[] = force_balance_tags($dynamic_option); // If it outputs HTML, escape and use them as-is
$i++;
continue; // Continue with next iteration
} else {
$dynamic_value = $dynamic_option;
$dynamic_label = $dynamic_option;
}
} else {
// These are different, could be raw shortcodes
$dynamic_value = wpcf7dtx_get_dynamic($value, false);
$dynamic_label = wpcf7dtx_get_dynamic($label, false);
}
// This could be a single??
$class = array('wpcf7-list-item');
$class[] = sanitize_html_class('wpcf7-list-item-' . $i);
if ($i === 1) {
$class[] = 'first';
}
if ($i === $count) {
$class[] = 'last';
}
if ($exclusive) {
$class[] = 'wpcf7-exclusive-checkbox';
}
if ($dynamic_value && $atts['dtx-default'] && $dynamic_value == $atts['dtx-default']) {
$my_atts['checked'] = 'checked';
}
$group_html[] = sprintf(
'<span class="%s">%s</span>',
esc_attr(implode(' ', $class)),
wpcf7dtx_checkbox_html(
// Overwrite name attribute
array_merge($my_atts, array(
'name' => $atts['type'] == 'radio' || $exclusive || $count === 1 ? $atts['name'] : $atts['name'] . '[]', // if there are multiple checkboxes and they aren't exclusive, names are an array
'value' => $dynamic_value
)),
$dynamic_label,
$label_ui,
$reverse
)
);
$i++;
}
$group_html = implode('', $group_html);
}
return $group_html;
}
/**
* Create Textarea Field HTML
*
* @since 4.0.0
*
* @param array $atts An associative array of textarea field attributes.
*
* @return string HTML output of textarea field
*/
function wpcf7dtx_textarea_html($atts)
{
// Attributes specific to HTML creation
$atts = array_merge(array('value' => ''), array_change_key_case((array)$atts, CASE_LOWER));
return sprintf(
'<textarea %s>%s</textarea>',
wpcf7dtx_format_atts($atts),
apply_filters('wpcf7dtx_escape', $atts['value'], false, 'textarea')
);
}
/**
* Create Select Field HTML
*
* @since 4.0.0
*
* @param array $atts An associative array of select input attributes.
* @param array|string $options Accepts an associative array of key/value pairs to use as the
* select option's value/label pairs. It also accepts an associative array of associative
* arrays with the keys being used as option group labels and the array values used as that
* group's options. It also accepts a string value of HTML already formatted as options or
* option groups. It also accepts a string value of a self-closing shortcode that is
* evaluated and its output is either options or option groups.
* @param bool $hide_blank Optional. If true, the first blank placeholder option will have the `hidden` attribute added to it. Default is false.
* @param bool $disable_blank Optional. If true, the first blank placeholder option will have the `disabled` attribute added to it. Default is false.
*
* @return string HTML output of select field
*/
function wpcf7dtx_select_html($atts, $options, $hide_blank = false, $disable_blank = false)
{
// Attributes specific to HTML creation
$atts = array_merge(array('placeholder' => '', 'dtx-default' => ''), array_change_key_case((array)$atts, CASE_LOWER));
$options_html = ''; // Open options HTML
// If using a placeholder, use it as the text of the first option
if ($atts['placeholder']) {
$options_html .= sprintf(
'<option value=""%s%s%s>%s</option>',
empty($atts['dtx-default']) ? ' selected' : '',
$hide_blank ? ' hidden' : '',
$disable_blank ? ' disabled' : '',
apply_filters('wpcf7dtx_escape', $atts['placeholder'])
);
}
if (is_array($options) && count($options)) {
//Check if using option groups
if (is_array(array_values($options)[0])) {
foreach ($options as $group_name => $opt_group) {
$options_html .= sprintf('<optgroup label="%s">', esc_attr(apply_filters('wpcf7dtx_escape', wpcf7dtx_get_dynamic($group_name)))); // Open option group
foreach ($opt_group as $option_value => $option_label) {
// Check if option values and groups are dynamic
$dynamic_option_value = wpcf7dtx_get_dynamic($option_value);
$options_html .= sprintf(
'<option value="%1$s"%3$s>%2$s</option>',
esc_attr(apply_filters('wpcf7dtx_escape', $dynamic_option_value)),
esc_html(apply_filters('wpcf7dtx_escape', wpcf7dtx_get_dynamic($option_label))),
$atts['dtx-default'] == $dynamic_option_value ? ' selected' : ''
);
}
$options_html .= '</optgroup>'; // Close option group
}
} else {
$allowed_html = wpcf7dtx_get_allowed_field_properties('option');
foreach ($options as $option_value => $option_label) {
if ($option_value === $option_label) {
// These are identical, just handle it as one, could also be a raw shortcode
$dynamic_option = trim(wpcf7dtx_get_dynamic($option_value, false, 'none')); // Do not sanitize yet, it may have HTML
if (is_string($dynamic_option) && !empty($dynamic_option) && (strpos($dynamic_option, '<option') === 0 || stripos($dynamic_option, '<optgroup') === 0)) {
$options_html .= wp_kses($dynamic_option, $allowed_html); // If it outputs HTML, escape and use them as-is
} elseif ($dynamic_option) {
// Just output the option
$dynamic_option = apply_filters('wpcf7dtx_escape', $dynamic_option);
$options_html .= sprintf(
'<option value="%1$s"%3$s>%2$s</option>',
esc_attr($dynamic_option),
esc_html($dynamic_option),
$atts['dtx-default'] == $dynamic_option ? ' selected' : ''
);
}
} else {
$dynamic_option_value = wpcf7dtx_get_dynamic($option_value, false);
$options_html .= sprintf(
'<option value="%1$s"%3$s>%2$s</option>',
esc_attr(apply_filters('wpcf7dtx_escape', $dynamic_option_value)),
esc_html(apply_filters('wpcf7dtx_escape', wpcf7dtx_get_dynamic($option_label))),
$atts['dtx-default'] == $dynamic_option_value ? ' selected' : ''
);
}
}
}
return $return;
} elseif (is_string($options) && !empty($options = trim($options))) {
$allowed_html = wpcf7dtx_get_allowed_field_properties('option');
// If options were passed as a string, go ahead and use them
if (strpos($options, '<option') === 0 || stripos($options, '<optgroup') === 0) {
$options_html .= wp_kses($options, $allowed_html);
} else {
// If a shortcode was passed as the options, evaluate it and use the result
$shortcode_output = wpcf7dtx_get_dynamic($options);
if (is_string($shortcode_output) && !empty($shortcode_output) && (strpos($shortcode_output, '<option') === 0) || strpos($shortcode_output, '<optgroup') === 0) {
$options_html .= wp_kses($shortcode_output, $allowed_html);
}
}
}
return sprintf('<select %s>%s</select>', wpcf7dtx_format_atts($atts), $options_html);
}
/**
@@ -253,21 +661,3 @@ function wpcf7dtx_array_has_key($key, $array = array(), $default = '')
}
return $default;
}
if (!function_exists('array_key_first')) {
/**
* Gets the first key of an array
*
* Gets the first key of the given array without affecting the internal array pointer.
*
* @param array $array
* @return int|string|null
*/
function array_key_first($array = array())
{
foreach ($array as $key => $value) {
return $key;
}
return null;
}
}

View File

@@ -0,0 +1,233 @@
<?php
/**
* Add Frontend Validation Messages
*
* @since 4.0.0
*
* @param array An associative array of messages
*
* @return array A modified associative array of messages
*/
function wpcf7dtx_messages($messages)
{
return array_merge($messages, array(
'dtx_invalid_email' => array(
'description' => __('There is a field with an invalid email address', 'contact-form-7-dynamic-text-extension'),
'default' => __('Please enter a valid email address.', 'contact-form-7-dynamic-text-extension')
),
'dtx_invalid_tel' => array(
'description' => __('There is a field with an invalid phone number', 'contact-form-7-dynamic-text-extension'),
'default' => __('Please enter a valid phone number.', 'contact-form-7-dynamic-text-extension')
),
'dtx_invalid_number' => array(
'description' => __('There is a field with an invalid number', 'contact-form-7-dynamic-text-extension'),
'default' => __('Please enter a valid number.', 'contact-form-7-dynamic-text-extension')
),
'dtx_invalid_date' => array(
'description' => __('There is a field with an invalid date', 'contact-form-7-dynamic-text-extension'),
'default' => __('Please enter a valid date.', 'contact-form-7-dynamic-text-extension')
),
));
}
add_filter('wpcf7_messages', 'wpcf7dtx_messages');
/**
* Validate DTX Form Fields
*
* Frontend validation for DTX form tags
*
* @param WPCF7_Validation $result the current validation result object
* @param WPCF7_FormTag $tag the current form tag being filtered for validation
*
* @return WPCF7_Validation a possibly modified validation result object
*/
function wpcf7dtx_validation_filter($result, $tag)
{
$type = str_replace(array('dynamic_', 'dynamic'), '', $tag->basetype);
if (empty($tag->name) || in_array($type, array('hidden', 'submit', 'reset'))) {
return $result; // Bail early for tags without names or if a specific type
}
// Get the value
$user_value = wpcf7dtx_array_has_key($tag->name, $_POST);
if (is_array($user_value)) {
$selection_count = count($user_value);
if (!wpcf7_form_tag_supports($tag->type, 'selectable-values')) {
// Field passed selectable values when it's doesn't support them
$result->invalidate($tag, wpcf7_get_message('validation_error'));
return $result;
} elseif ($selection_count > 1) {
if (!wpcf7_form_tag_supports($tag->type, 'multiple-controls-container')) {
// Field passed multiple values when it's doesn't support them
$result->invalidate($tag, wpcf7_get_message('validation_error'));
return $result;
}
foreach ($user_value as $selection) {
// Validate each selected choice
$result = wpcf7dtx_validate_value($result, sanitize_textarea_field(strval($selection)), $tag, $type);
if (!$result->is_valid($tag->name)) {
return $result; // Return early if any are invalid
}
}
return $result;
}
$user_value = sanitize_text_field(strval(implode(' ', $user_value)));
} elseif ($type == 'textarea') {
$user_value = sanitize_textarea_field(strval($user_value));
} else {
$user_value = sanitize_text_field(strval($user_value));
}
// Validate and return
return wpcf7dtx_validate_value($result, $user_value, $tag, $type);
}
/**
* Validate Single Value
*
* @param WPCF7_Validation $result the current validation result object
* @param string $value the current value being validated, sanitized
* @param WPCF7_FormTag $tag the current form tag being filtered for validation
* @param string $type Optional. The type of the current form tag. Default is blank for lookup.
*
* @return WPCF7_Validation a possibly modified validation result object
*/
function wpcf7dtx_validate_value($result, $value, $tag, $type = '')
{
$type = $type ? $type : str_replace(array('dynamic_', 'dynamic'), '', $tag->basetype);
// Validate required fields for value
if ($tag->is_required() && empty($value)) {
$result->invalidate($tag, wpcf7_get_message('invalid_required'));
return $result;
}
// Validate value by type
if (!empty($value)) {
switch ($type) {
case 'email':
if (!wpcf7_is_email($value)) {
$result->invalidate($tag, wpcf7_get_message('dtx_invalid_email'));
return $result;
}
break;
case 'tel':
if (!wpcf7_is_tel($value)) {
$result->invalidate($tag, wpcf7_get_message('dtx_invalid_tel'));
return $result;
}
break;
case 'number':
case 'range':
if (!wpcf7_is_number($value)) {
$result->invalidate($tag, wpcf7_get_message('dtx_invalid_number'));
return $result;
}
break;
case 'date':
if (!wpcf7_is_date($value)) {
$result->invalidate($tag, wpcf7_get_message('dtx_invalid_date'));
return $result;
}
break;
}
// Finish validating text-based inputs
$maxlength = $tag->get_maxlength_option();
$minlength = $tag->get_minlength_option();
if ($maxlength && $minlength && $maxlength < $minlength) {
$maxlength = $minlength = null;
}
$code_units = wpcf7_count_code_units($value);
if (false !== $code_units) {
if ($maxlength && $maxlength < $code_units) {
$result->invalidate($tag, wpcf7_get_message('invalid_too_long'));
return $result;
} elseif ($minlength && $code_units < $minlength) {
$result->invalidate($tag, wpcf7_get_message('invalid_too_short'));
return $result;
}
}
}
return $result;
}
/**
* Backend Mail Configuration Validation
*
* Validate dynamic form tags used in mail configuration.
*
* @since 4.0.0
*
* @param WPCF7_ConfigValidator
*
* @return void
*/
function wpcf7dtx_validate($validator)
{
if (!$validator->is_valid()) {
$contact_form = null;
$form_tags = null;
foreach ($validator->collect_error_messages() as $component => $errors) {
$components = explode('.', $component);
if (count($components) === 2 && strpos($components[0], 'mail') === 0 && in_array($components[1], array('sender', 'recipient', 'additional_headers'))) {
foreach ($errors as $error) {
// Focus on email fields that flag the invalid mailbox syntax warning, have to test link because code isn't sent and message could be in any language
if (strpos(wpcf7dtx_array_has_key('link', $error), 'invalid-mailbox-syntax') !== false) {
if (is_null($contact_form)) {
$contact_form = $validator->contact_form();
}
if (is_null($form_tags)) {
$form_tags = wpcf7_scan_form_tags();
}
$raw_value = $contact_form->prop($components[0])[$components[1]];
foreach ($form_tags as $tag) {
if (!empty($tag->name)) {
// Check if this form tag is in the raw value
$form_tag = '[' . $tag->name . ']';
if (strpos($raw_value, $form_tag) !== false && in_array($tag->basetype, array_keys(wpcf7dtx_config()))) {
$validator->remove_error($component, 'invalid_mailbox_syntax'); // Remove error, this is ours to handle now
$utm_source = urlencode(home_url());
if (!in_array($tag->basetype, array('dynamic_hidden', 'dynamic_email'))) {
$validator->add_error($component, 'invalid_mailbox_syntax', array(
'message' => __('Only email, dynamic email, hidden, or dynamic hidden form tags can be used for email addresses.', 'contact-form-7-dynamic-text-extension'),
'link' => esc_url(sprintf('https://aurisecreative.com/docs/contact-form-7-dynamic-text-extension/configuration-errors/?utm_source=%s&utm_medium=link&utm_campaign=contact-form-7-dynamic-text-extension&utm_content=config-error-invalid_mailbox_syntax#valid-form-tags', $utm_source))
));
} else {
$dynamic_value = wpcf7dtx_get_dynamic(false, $tag); // Get the dynamic value of this tag
if (empty($dynamic_value) && $tag->basetype == 'dynamic_hidden') {
$validator->add_error($component, 'maybe_empty', array(
'message' => __('The dynamic hidden form tag must have a default value.', 'contact-form-7-dynamic-text-extension'),
'link' => esc_url(sprintf('https://aurisecreative.com/docs/contact-form-7-dynamic-text-extension/configuration-errors/?utm_source=%s&utm_medium=link&utm_campaign=contact-form-7-dynamic-text-extension&utm_content=config-error-maybe_empty#maybe-empty', $utm_source))
));
} elseif (empty($dynamic_value) && !$tag->is_required()) {
$validator->add_error($component, 'maybe_empty', array(
'message' => __('The dynamic form tag must be required or have a default value.', 'contact-form-7-dynamic-text-extension'),
'link' => esc_url(sprintf('https://aurisecreative.com/docs/contact-form-7-dynamic-text-extension/configuration-errors/?utm_source=%s&utm_medium=link&utm_campaign=contact-form-7-dynamic-text-extension&utm_content=config-error-maybe_empty#maybe-empty', $utm_source))
));
} elseif (!empty($dynamic_value)) {
if (!wpcf7_is_email($dynamic_value)) {
$validator->add_error($component, 'invalid_mailbox_syntax', array(
'message' => __('The default dynamic value does not result in a valid email address.', 'contact-form-7-dynamic-text-extension'),
'link' => esc_url(sprintf('https://aurisecreative.com/docs/contact-form-7-dynamic-text-extension/configuration-errors/?utm_source=%s&utm_medium=link&utm_campaign=contact-form-7-dynamic-text-extension&utm_content=config-error-invalid_mailbox_syntax#invalid-email-address', $utm_source))
));
} elseif ($component[1] == 'sender' && !wpcf7_is_email_in_site_domain($dynamic_value)) {
$validator->add_error($component, 'email_not_in_site_domain', array(
'message' => __('The dynamic email address for the sender does not belong to the site domain.', 'contact-form-7-dynamic-text-extension'),
'link' => esc_url(sprintf('https://aurisecreative.com/docs/contact-form-7-dynamic-text-extension/configuration-errors/?utm_source=%s&utm_medium=link&utm_campaign=contact-form-7-dynamic-text-extension&utm_content=config-error-email_not_in_site_domain#invalid-site-domain', $utm_source))
));
}
}
}
}
}
}
}
}
}
}
}
}
add_action('wpcf7_config_validator_validate', 'wpcf7dtx_validate');

View File

@@ -0,0 +1 @@
<?php // Silence is golden

View File

@@ -3,9 +3,9 @@ Contributors: sevenspark, tessawatkinsllc
Donate link: https://just1voice.com/donate/
Tags: Contact Form 7, autofill, prepopulate, input, form field, contact form, text, hidden, input, dynamic, GET, POST, title, slug, auto-fill, pre-populate
Tested up to: 6.3
Stable tag: 3.5.4
Stable tag: 4.1.0
This plugin provides additional form tags for the Contact Form 7 plugin. It allows dynamic generation of content for text or hidden input fields using any shortcode.
This plugin provides additional form tags for the Contact Form 7 plugin. It allows dynamic generation of content for text-based input fields like text, hidden, and email, checkboxes, radio buttons, and drop-down selections using any shortcode.
== Description ==
@@ -25,11 +25,25 @@ Contact Form 7 is an excellent WordPress plugin and one of the top choices of fr
* Getting custom theme modifications
* Any value using custom shortcodes
For over 10 years, DTX only handled `<input type="text" />` and `<input type="hidden" />` form fields, but version 4 finally introduces more:
* email
* URL
* tel (for phone numbers)
* number
* range (slider)
* textarea (multiline text)
* drop-down menu (select field)
* checkboxes
* radio buttons
* date
* submit (yes, a submit button where you can have dynamic text!)
The possibilities are endless!
## WHAT DOES IT DO? ##
DTX comes with several built-in shortcodes that will allow the contact form to be populated from HTTPS GET variable or any info from the `get_bloginfo()` function, among others. See below for included shortcodes.
DTX provides flexibility to WordPress users in creating dynamic forms in Contact Form 7. DTX comes with several built-in shortcodes that will allow the contact form to be populated from HTTPS GET variable or any info from the `get_bloginfo()` function, among others. See below for included shortcodes.
Don't see the shortcode you need on the list? You can write a [custom one](https://aurisecreative.com/docs/contact-form-7-dynamic-text-extension/shortcodes/custom-shortcodes/?utm_source=wordpress.org&utm_medium=link&utm_campaign=contact-form-7-dynamic-text-extension&utm_content=readme)! Any shortcode that returns a string or numeric value can be used here. The included shortcodes just cover the most common scenarios, but DTX provides the flexibility for you to grab any value you have access to programmatically.
@@ -341,9 +355,20 @@ This method is the most involved as it requires you to be familiar with the proc
== Screenshots ==
1. Screenshot of the form tag buttons in the form editor of Contact Form 7. A red square highlights the addition of two new buttons for "dynamic text" and "dynamic hidden"
1. Screenshot of the form tag buttons in the form editor of Contact Form 7. The dynamic buttons appear in purple instead of blue to visually set them apart.
2. The form tag generator screen for the dynamic text form tag
3. The form tag generator screen for the dynamic hidden form tag
4. The form tag generator screen for the dynamic email form tag
5. The form tag generator screen for the dynamic URL form tag
6. The form tag generator screen for the dynamic phone number (tel) form tag
7. The form tag generator screen for the dynamic number spinbox form tag
8. The form tag generator screen for the dynamic sliding range form tag
9. The form tag generator screen for the dynamic textarea form tag
10. The form tag generator screen for the dynamic drop-down menu (select) form tag
11. The form tag generator screen for the dynamic checkboxes form tag
12. The form tag generator screen for the dynamic radio buttons form tag
13. The form tag generator screen for the dynamic date form tag
14. The form tag generator screen for the dynamic submit form tag
== Frequently Asked Questions ==
@@ -351,111 +376,53 @@ Please check out the [FAQ on our website](https://aurisecreative.com/docs/contac
== Upgrade Notice ==
= 3.5.3 =
Optimized JavaScript used in cache compatibility mode.
= 4.1.0 =
Extend functionality without losing your work!
== Changelog ==
= 3.5.4 =
= 4.1.0 =
* Fix: Updated JavaScript to prevent cacheable fields from making unnecessary AJAX requests
* Feature: Looks for a `dtx.php` file in the `wp_content` directory to maybe load custom shortcodes, [see support thread](https://wordpress.org/support/topic/how-to-avoid-custom-shortcodes-being-overwritten-on-updates/)
* Feature: Looks for a `dtx.php` file in the current active theme's directory to maybe load custom shortcodes, [see support thread](https://wordpress.org/support/topic/how-to-avoid-custom-shortcodes-being-overwritten-on-updates/)
* Feature: Looks for a `dtx.php` file in the current active theme's parent directory to maybe load custom shortcodes, [see support thread](https://wordpress.org/support/topic/how-to-avoid-custom-shortcodes-being-overwritten-on-updates/)
* Fix: addressed user reported bug, [see support thread](https://wordpress.org/support/topic/fatal-error-v4-0-3/)
= 3.5.3 =
= 4.0.3 =
* Update: removed the use of sessions, [see support thread](https://wordpress.org/support/topic/add-option-to-disable-session-data/)
* Feature: Added `exclusive` option to checkbox tag generator
* Fix: addressed bug that put all dynamic checkbox/radio options into one
* Fix: addressed bug in frontend validator for multiple selected values
= 3.5.2 =
= 4.0.2 =
* Fix: Updated the `CF7_URL` shortcode to only use `network_home_url()` for multisite installs that do not use subdomains, and use `home_url()` for all others to [maybe address this support thread](https://wordpress.org/support/topic/cf7_url-return-only-domain-and-not-subdomain/)
* Fix: Removed a lingering debug call
* Fix: addressed bug that put all dynamic select options into one, [see support thread](https://wordpress.org/support/topic/dynamic-select-get-option-values-from-shortcode/)
* Update: sanitizing and escaping filters now accept `none` as value for `$type` to bypass. Use with caution.
= 3.5.1 =
= 4.0.1 =
* Fix: fixed bug so tag generator for dynamic fields work on "Add New Contact Form" page of Contact Form 7
* Updated: Updated text in tag generator for cache compatible checkbox and added link to documentation
* Fix: addressed bug that prevented translation for cache compatibility description
= 3.5.0 =
= 4.0.0 =
* Feature: Added the `dtx_pageload` form tag attribute for cache compatibility. For usage details, see the [knowledge base](https://aurisecreative.com/docs/contact-form-7-dynamic-text-extension/form-tag-attribute-after-page-load/?utm_source=wordpress.org&utm_medium=link&utm_campaign=contact-form-7-dynamic-text-extension&utm_content=readme)
* Fix: Updated to be compatible with WordPress version 6.3
* Fix: Addressed a bug where `scheme` in `CF7_URL part='scheme'` was incorrectly sanitizing as URL instead of text
* Fix: Fixed `wp_kses()` in tag generator that stripped out link opening in new tab
* Update: `CF7_get_current_var` utilizes PHP session variables where appropriate
* Update: All JavaScript assets will load with the `defer` strategy in the footer in [WordPress 6.3](https://make.wordpress.org/core/2023/07/14/registering-scripts-with-async-and-defer-attributes-in-wordpress-6-3/)
= 3.4.0 =
* Feature: Added the `CF7_get_current_var` shortcode, [see support thread for user request](https://wordpress.org/support/topic/wrong-page-title-7/). For usage details, see the [knowledge base](https://aurisecreative.com/docs/contact-form-7-dynamic-text-extension/shortcodes/dtx-shortcode-current-variables/?utm_source=wordpress.org&utm_medium=link&utm_campaign=contact-form-7-dynamic-text-extension&utm_content=readme)
* Fix: Updated the `CF7_URL` shortcode to no longer check for ports since that's handled in `network_home_url()` function, [see support thread](https://wordpress.org/support/topic/version-3-3-0-breaking/)
= 3.3.0 =
* Feature: Added the `CF7_get_cookie` shortcode. For usage details, see the [knowledge base](https://aurisecreative.com/docs/contact-form-7-dynamic-text-extension/shortcodes/dtx-shortcode-cookie/?utm_source=wordpress.org&utm_medium=link&utm_campaign=contact-form-7-dynamic-text-extension&utm_content=readme)
* Feature: Added the `CF7_get_taxonomy` shortcode. For usage details, see the [knowledge base](https://aurisecreative.com/docs/contact-form-7-dynamic-text-extension/shortcodes/dtx-shortcode-taxonomy/?utm_source=wordpress.org&utm_medium=link&utm_campaign=contact-form-7-dynamic-text-extension&utm_content=readme)
* Feature: Added the `CF7_get_theme_option` shortcode. For usage details, see the [knowledge base](https://aurisecreative.com/docs/contact-form-7-dynamic-text-extension/shortcodes/dtx-shortcode-theme-option/?utm_source=wordpress.org&utm_medium=link&utm_campaign=contact-form-7-dynamic-text-extension&utm_content=readme)
* Feature: Added `wpcf7dtx_sanitize` filter that sanitizes attribute values in built-in shortcodes
* Feature: Added `wpcf7dtx_escape` filter that escapes values in built-in shortcodes
* Feature: Added `wpcf7dtx_allow_protocols` filter to customize allowed protocols in escaping URLs in built-in shortcodes
* Fix: Updated how plugin gets dynamic value in form tags, now uses `wpcf7dtx_get_dynamic()` function
* Fix: Added case-insensitive ID in `CF7_get_post_var`
* Fix: Sanitizes post variable keys as keys in `wpcf7dtx_get_post_var()`
* Fix: Updated `wpcf7dtx_get_post_id()` to pull from "the loop" if `$post` is unavailable and now used consistently across built-in shortcodes
* Fix: Updated tag markup to be compatible with Contact Form 7 version 5.6 Beta for successful form validation, [see support thread](https://wordpress.org/support/topic/required-field-no-error-is-output-when-validating-when-field-is-empty/)
* Fix: Updated the `CF7_URL` shortcode to use `network_home_url()`, [see support thread](https://wordpress.org/support/topic/current-url-not-working/)
* Fix: Updated GUID function to return appropriately escaped values
* Fix: Updated all existing built-in shortcodes to use the the sanitizing, escaping, and obfuscating shortcodes, [see support thread](https://wordpress.org/support/topic/cant-get-obfuscate-to-work/)
* Fix: Marked compatible with WordPress core version 6.2.
= 3.2 =
* Feature: Add optional 'part' parameter to CF7_URL shortcode to retrieve Host, Query, or Path from current URL
* Updated minimum PHP requirement to 7.4 moving forward
* Update branding assets
* Update Tested Up To to 6.1.1
* Plugin will now be jointly maintained by [SevenSpark](https://sevenspark.com/) and [AuRise Creative](https://aurisecreative.com)
= 3.1.3 =
* Fix: Fixed the syntax error that reappeared in 3.1.2.
= 3.1.2 =
**Release Date: January 27, 2023**
* Fix: updated the text domain to match the plugin slug
* Fix: updated all of the translated strings to match
= 3.1.1 =
**Release Date: January 26, 2023**
* Fix: Fixed the syntax error: Parse error: syntax error, unexpected `)` in /wp-content/plugins/contact-form-7-dynamic-text extension/includes/admin.php on line 212
= 3.1.0 =
**Release Date: January 25, 2023**
* Feature: Added the `CF7_get_attachment` shortcode. For usage details, see the [knowledge base](https://aurisecreative.com/docs/contact-form-7-dynamic-text-extension/shortcodes/dtx-shortcode-media-attachment/?utm_source=wordpress.org&utm_medium=link&utm_campaign=contact-form-7-dynamic-text-extension&utm_content=readme)
* Feature: Added the `CF7_guid` shortcode. For usage details, see the [knowledge base](https://aurisecreative.com/docs/contact-form-7-dynamic-text-extension/shortcodes/dtx-shortcode-guid/?utm_source=wordpress.org&utm_medium=link&utm_campaign=contact-form-7-dynamic-text-extension&utm_content=readme).
* Feature: Added the dynamic placeholder option to the dynamic form tags that allows you to specify dynamic or static placeholder content while also setting dynamic values. For usage details, see the [knowledge base](https://aurisecreative.com/docs/contact-form-7-dynamic-text-extension/shortcodes/dtx-attribute-placeholder/?utm_source=wordpress.org&utm_medium=link&utm_campaign=contact-form-7-dynamic-text-extension&utm_content=readme)
* Feature: Added a "required" dynamic hidden tag (e.g., `[dynamichidden* ...]`). It is identical to the original dynamic hidden tag (as in the field is not actually validated as required because it is hidden); it just doesn't break your website if you use it. This feature was requested by a user.
* Feature: Added the `obfuscate` attribute to all included shortcodes
= 3.0.0 =
**Release Date: January 17, 2023**
* Major: Plugin was adopted by AuRise Creative
* Major: All functions use the `wpcf7dtx_` prefix
* Feature: Added a `post_id` key for the `CF7_get_post_var` shortcode so you can specify a different post
* Feature: Updated the `CF7_get_current_user` shortcode to be able to pull data from user metadata too
* Feature: Added the "obfuscate" option to `CF7_get_custom_field` shortcode
* Feature: Added the "placeholder" checkbox option to the `dynamictext` tag
* Fix: Added additional validation for post ID input
* Fix: Added additional validation for the `key` attribute in the `CF7_GET` and `CF7_POST` shortcodes
* Fix: Shortcode keys are normalized into lowercase before processing
* Security: Sanitizing URLs for the `CF7_URL` and `CF7_referrer` shortcode outputs
* Feature/Security: Added a `allowed_protocols` attribute to the `CF7_URL` and `CF7_referrer` shortcodes that defaults to `http,https`
* Major: modified function names
* Major: deprecated `dynamictext` and `dynamichidden` form tags in favor of `dynamic_text` and `dynamic_hidden`. For more information, see the [knowledge base](https://aurisecreative.com/docs/contact-form-7-dynamic-text-extension/form-tags/?utm_source=wordpress.org&utm_medium=link&utm_campaign=contact-form-7-dynamic-text-extension&utm_content=readme)
* Feature: introduced `dynamic_email` form tag. For usage details, see the [knowledge base](https://aurisecreative.com/docs/contact-form-7-dynamic-text-extension/form-tags/dynamic-email/?utm_source=wordpress.org&utm_medium=link&utm_campaign=contact-form-7-dynamic-text-extension&utm_content=readme)
* Feature: introduced `dynamic_url` form tag. For usage details, see the [knowledge base](https://aurisecreative.com/docs/contact-form-7-dynamic-text-extension/form-tags/dynamic-url/?utm_source=wordpress.org&utm_medium=link&utm_campaign=contact-form-7-dynamic-text-extension&utm_content=readme)
* Feature: introduced `dynamic_tel` form tag. For usage details, see the [knowledge base](https://aurisecreative.com/docs/contact-form-7-dynamic-text-extension/form-tags/dynamic-tel/?utm_source=wordpress.org&utm_medium=link&utm_campaign=contact-form-7-dynamic-text-extension&utm_content=readme)
* Feature: introduced `dynamic_number` form tag. For usage details, see the [knowledge base](https://aurisecreative.com/docs/contact-form-7-dynamic-text-extension/form-tags/dynamic-number/?utm_source=wordpress.org&utm_medium=link&utm_campaign=contact-form-7-dynamic-text-extension&utm_content=readme)
* Feature: introduced `dynamic_range` form tag. For usage details, see the [knowledge base](https://aurisecreative.com/docs/contact-form-7-dynamic-text-extension/form-tags/dynamic-range/?utm_source=wordpress.org&utm_medium=link&utm_campaign=contact-form-7-dynamic-text-extension&utm_content=readme)
* Feature: introduced `dynamic_textarea` form tag. For usage details, see the [knowledge base](https://aurisecreative.com/docs/contact-form-7-dynamic-text-extension/form-tags/dynamic-textarea/?utm_source=wordpress.org&utm_medium=link&utm_campaign=contact-form-7-dynamic-text-extension&utm_content=readme)
* Feature: introduced `dynamic_select` form tag. For usage details, see the [knowledge base](https://aurisecreative.com/docs/contact-form-7-dynamic-text-extension/form-tags/dynamic-select/?utm_source=wordpress.org&utm_medium=link&utm_campaign=contact-form-7-dynamic-text-extension&utm_content=readme)
* Feature: introduced `dynamic_radio` form tag. For usage details, see the [knowledge base](https://aurisecreative.com/docs/contact-form-7-dynamic-text-extension/form-tags/dynamic-radio/?utm_source=wordpress.org&utm_medium=link&utm_campaign=contact-form-7-dynamic-text-extension&utm_content=readme)
* Feature: introduced `dynamic_date` form tag. For usage details, see the [knowledge base](https://aurisecreative.com/docs/contact-form-7-dynamic-text-extension/form-tags/dynamic-date/?utm_source=wordpress.org&utm_medium=link&utm_campaign=contact-form-7-dynamic-text-extension&utm_content=readme)
* Feature: introduced `dynamic_submit` form tag. For usage details, see the [knowledge base](https://aurisecreative.com/docs/contact-form-7-dynamic-text-extension/form-tags/dynamic-submit/?utm_source=wordpress.org&utm_medium=link&utm_campaign=contact-form-7-dynamic-text-extension&utm_content=readme)
* Feature: introduced `dtx_hide_blank` form tag attribute for `dynamic_select`. For usage details, see the [knowledge base](https://aurisecreative.com/docs/contact-form-7-dynamic-text-extension/form-tags/dynamic-select/?utm_source=wordpress.org&utm_medium=link&utm_campaign=contact-form-7-dynamic-text-extension&utm_content=readme)
* Feature: introduced `dtx_disable_blank` form tag attribute for `dynamic_select`. For usage details, see the [knowledge base](https://aurisecreative.com/docs/contact-form-7-dynamic-text-extension/form-tags/dynamic-select/?utm_source=wordpress.org&utm_medium=link&utm_campaign=contact-form-7-dynamic-text-extension&utm_content=readme)
* Feature: added mail validation for `dynamic_email` and `dynamic_hidden` for backend configuration. For more information, see the [FAQ](https://aurisecreative.com/docs/contact-form-7-dynamic-text-extension/frequently-asked-questions/?utm_source=wordpress.org&utm_medium=link&utm_campaign=contact-form-7-dynamic-text-extension&utm_content=readme)
* Feature: added the Akismet feature to DTX text, email, and URL form tags.
* Update: adjusted how queued values were sent for cache compatibility mode to allow for multiline values in textareas
* Removed unused utility functions
= Older Releases =

View File

@@ -120,7 +120,7 @@ function wpcf7_editor_box_mail( $post, $args = '' ) {
<?php
if ( ! empty( $args['use'] ) ) :
?>
<label for="<?php echo $id; ?>-active"><input type="checkbox" id="<?php echo $id; ?>-active" name="<?php echo $id; ?>[active]" class="toggle-form-table" value="1"<?php echo ( $mail['active'] ) ? ' checked="checked"' : ''; ?> /> <?php echo esc_html( $args['use'] ); ?></label>
<label for="<?php echo $id; ?>-active"><input type="checkbox" id="<?php echo $id; ?>-active" name="<?php echo $id; ?>[active]" data-config-field="" class="toggle-form-table" value="1"<?php echo ( $mail['active'] ) ? ' checked="checked"' : ''; ?> /> <?php echo esc_html( $args['use'] ); ?></label>
<p class="description"><?php echo esc_html( __( "Mail (2) is an additional mail template often used as an autoresponder.", 'contact-form-7' ) ); ?></p>
<?php
endif;

View File

@@ -2,6 +2,7 @@
return array(
'dependencies' => array(
'react',
'wp-api-fetch',
'wp-block-editor',
'wp-blocks',

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,27 @@
<?php
add_action(
'wpcf7_update_option',
'wpcf7_config_validator_update_option',
10, 3
);
/**
* Runs bulk validation after the reCAPTCHA integration option is updated.
*/
function wpcf7_config_validator_update_option( $name, $value, $old_option ) {
if ( 'recaptcha' === $name ) {
$contact_forms = WPCF7_ContactForm::find();
$options = array(
'include' => 'unsafe_email_without_protection',
);
foreach ( $contact_forms as $contact_form ) {
$config_validator = new WPCF7_ConfigValidator( $contact_form, $options );
$config_validator->restore();
$config_validator->validate();
$config_validator->save();
}
}
}

View File

@@ -6,17 +6,22 @@ trait WPCF7_ConfigValidator_AdditionalSettings {
* Runs error detection for the additional settings section.
*/
public function validate_additional_settings() {
$section = 'additional_settings.body';
if ( $this->supports( 'deprecated_settings' ) ) {
$deprecated_settings_used =
$this->contact_form->additional_setting( 'on_sent_ok' ) ||
$this->contact_form->additional_setting( 'on_submit' );
if ( $deprecated_settings_used ) {
return $this->add_error( 'additional_settings.body',
'deprecated_settings',
$this->add_error( $section, 'deprecated_settings',
array(
'message' => __( "Deprecated settings are used.", 'contact-form-7' ),
)
);
} else {
$this->remove_error( $section, 'deprecated_settings' );
}
}
}

View File

@@ -8,12 +8,83 @@ trait WPCF7_ConfigValidator_Form {
public function validate_form() {
$section = 'form.body';
$form = $this->contact_form->prop( 'form' );
$this->detect_multiple_controls_in_label( $section, $form );
$this->detect_unavailable_names( $section, $form );
$this->detect_unavailable_html_elements( $section, $form );
$this->detect_dots_in_names( $section, $form );
$this->detect_colons_in_names( $section, $form );
$this->detect_upload_filesize_overlimit( $section, $form );
if ( $this->supports( 'multiple_controls_in_label' ) ) {
if ( $this->detect_multiple_controls_in_label( $section, $form ) ) {
$this->add_error( $section, 'multiple_controls_in_label',
array(
'message' => __( "Multiple form controls are in a single label element.", 'contact-form-7' ),
)
);
} else {
$this->remove_error( $section, 'multiple_controls_in_label' );
}
}
if ( $this->supports( 'unavailable_names' ) ) {
$ng_names = $this->detect_unavailable_names( $section, $form );
if ( $ng_names ) {
$this->add_error( $section, 'unavailable_names',
array(
'message' =>
/* translators: %names%: a list of form control names */
__( "Unavailable names (%names%) are used for form controls.", 'contact-form-7' ),
'params' => array( 'names' => implode( ', ', $ng_names ) ),
)
);
} else {
$this->remove_error( $section, 'unavailable_names' );
}
}
if ( $this->supports( 'unavailable_html_elements' ) ) {
if ( $this->detect_unavailable_html_elements( $section, $form ) ) {
$this->add_error( $section, 'unavailable_html_elements',
array(
'message' => __( "Unavailable HTML elements are used in the form template.", 'contact-form-7' ),
)
);
} else {
$this->remove_error( $section, 'unavailable_html_elements' );
}
}
if ( $this->supports( 'dots_in_names' ) ) {
if ( $this->detect_dots_in_names( $section, $form ) ) {
$this->add_error( $section, 'dots_in_names',
array(
'message' => __( "Dots are used in form-tag names.", 'contact-form-7' ),
)
);
} else {
$this->remove_error( $section, 'dots_in_names' );
}
}
if ( $this->supports( 'colons_in_names' ) ) {
if ( $this->detect_colons_in_names( $section, $form ) ) {
$this->add_error( $section, 'colons_in_names',
array(
'message' => __( "Colons are used in form-tag names.", 'contact-form-7' ),
)
);
} else {
$this->remove_error( $section, 'colons_in_names' );
}
}
if ( $this->supports( 'upload_filesize_overlimit' ) ) {
if ( $this->detect_upload_filesize_overlimit( $section, $form ) ) {
$this->add_error( $section, 'upload_filesize_overlimit',
array(
'message' => __( "Uploadable file size exceeds PHPs maximum acceptable size.", 'contact-form-7' ),
)
);
} else {
$this->remove_error( $section, 'upload_filesize_overlimit' );
}
}
}
@@ -54,12 +125,7 @@ trait WPCF7_ConfigValidator_Form {
}
if ( 1 < $fields_count ) {
return $this->add_error( $section,
'multiple_controls_in_label',
array(
'message' => __( "Multiple form controls are in a single label element.", 'contact-form-7' ),
)
);
return true;
}
}
}
@@ -98,17 +164,7 @@ trait WPCF7_ConfigValidator_Form {
}
if ( $ng_names ) {
$ng_names = array_unique( $ng_names );
return $this->add_error( $section,
'unavailable_names',
array(
'message' =>
/* translators: %names%: a list of form control names */
__( "Unavailable names (%names%) are used for form controls.", 'contact-form-7' ),
'params' => array( 'names' => implode( ', ', $ng_names ) ),
)
);
return array_unique( $ng_names );
}
return false;
@@ -124,12 +180,7 @@ trait WPCF7_ConfigValidator_Form {
$pattern = '%(?:<form[\s\t>]|</form>)%i';
if ( preg_match( $pattern, $content ) ) {
return $this->add_error( $section,
'unavailable_html_elements',
array(
'message' => __( "Unavailable HTML elements are used in the form template.", 'contact-form-7' ),
)
);
return true;
}
return false;
@@ -150,12 +201,7 @@ trait WPCF7_ConfigValidator_Form {
foreach ( $tags as $tag ) {
if ( str_contains( $tag->raw_name, '.' ) ) {
return $this->add_error( $section,
'dots_in_names',
array(
'message' => __( "Dots are used in form-tag names.", 'contact-form-7' ),
)
);
return true;
}
}
@@ -177,12 +223,7 @@ trait WPCF7_ConfigValidator_Form {
foreach ( $tags as $tag ) {
if ( str_contains( $tag->raw_name, ':' ) ) {
return $this->add_error( $section,
'colons_in_names',
array(
'message' => __( "Colons are used in form-tag names.", 'contact-form-7' ),
)
);
return true;
}
}
@@ -227,12 +268,7 @@ trait WPCF7_ConfigValidator_Form {
foreach ( $tags as $tag ) {
if ( $upload_max_filesize < $tag->get_limit_option() ) {
return $this->add_error( $section,
'upload_filesize_overlimit',
array(
'message' => __( "Uploadable file size exceeds PHPs maximum acceptable size.", 'contact-form-7' ),
)
);
return true;
}
}

View File

@@ -2,11 +2,27 @@
trait WPCF7_ConfigValidator_Mail {
/**
* Replaces all mail-tags in the given content.
*/
public function replace_mail_tags( $content, $args = '' ) {
$args = wp_parse_args( $args, array(
'html' => false,
'callback' =>
array( $this, 'replace_mail_tags_with_minimum_input_callback' ),
) );
$content = new WPCF7_MailTaggedText( $content, $args );
return $content->replace_tags();
}
/**
* Callback function for WPCF7_MailTaggedText. Replaces mail-tags with
* the most conservative inputs.
*/
public function replace_mail_tags_with_minimum_input( $matches ) {
public function replace_mail_tags_with_minimum_input_callback( $matches ) {
// allow [[foo]] syntax for escaping a tag
if ( $matches[1] === '[' and $matches[4] === ']' ) {
return substr( $matches[0], 1, -1 );
@@ -124,109 +140,253 @@ trait WPCF7_ConfigValidator_Mail {
'attachments' => '',
) );
$callback = array( $this, 'replace_mail_tags_with_minimum_input' );
$subject = new WPCF7_MailTaggedText(
$components['subject'],
array( 'callback' => $callback )
$this->validate_mail_subject(
$template,
$components['subject']
);
$subject = $subject->replace_tags();
$subject = wpcf7_strip_newline( $subject );
$this->detect_maybe_empty( sprintf( '%s.subject', $template ), $subject );
$sender = new WPCF7_MailTaggedText(
$components['sender'],
array( 'callback' => $callback )
$this->validate_mail_sender(
$template,
$components['sender']
);
$sender = $sender->replace_tags();
$this->validate_mail_recipient(
$template,
$components['recipient']
);
$this->validate_mail_additional_headers(
$template,
$components['additional_headers']
);
$this->validate_mail_body(
$template,
$components['body']
);
$this->validate_mail_attachments(
$template,
$components['attachments']
);
}
/**
* Runs error detection for the mail subject section.
*/
public function validate_mail_subject( $template, $content ) {
$section = sprintf( '%s.subject', $template );
if ( $this->supports( 'maybe_empty' ) ) {
if ( $this->detect_maybe_empty( $section, $content ) ) {
$this->add_error( $section, 'maybe_empty',
array(
'message' => __( "There is a possible empty field.", 'contact-form-7' ),
)
);
} else {
$this->remove_error( $section, 'maybe_empty' );
}
}
}
/**
* Runs error detection for the mail sender section.
*/
public function validate_mail_sender( $template, $content ) {
$section = sprintf( '%s.sender', $template );
if ( $this->supports( 'invalid_mailbox_syntax' ) ) {
if ( $this->detect_invalid_mailbox_syntax( $section, $content ) ) {
$this->add_error( $section, 'invalid_mailbox_syntax',
array(
'message' => __( "Invalid mailbox syntax is used.", 'contact-form-7' ),
)
);
} else {
$this->remove_error( $section, 'invalid_mailbox_syntax' );
}
}
if ( $this->supports( 'email_not_in_site_domain' ) ) {
$this->remove_error( $section, 'email_not_in_site_domain' );
if ( ! $this->has_error( $section, 'invalid_mailbox_syntax' ) ) {
$sender = $this->replace_mail_tags( $content );
$sender = wpcf7_strip_newline( $sender );
$invalid_mailbox = $this->detect_invalid_mailbox_syntax(
sprintf( '%s.sender', $template ),
$sender
);
if ( ! $invalid_mailbox and ! wpcf7_is_email_in_site_domain( $sender ) ) {
$this->add_error( sprintf( '%s.sender', $template ),
'email_not_in_site_domain',
if ( ! wpcf7_is_email_in_site_domain( $sender ) ) {
$this->add_error( $section, 'email_not_in_site_domain',
array(
'message' => __( "Sender email address does not belong to the site domain.", 'contact-form-7' ),
)
);
}
}
}
}
$recipient = new WPCF7_MailTaggedText(
$components['recipient'],
array( 'callback' => $callback )
/**
* Runs error detection for the mail recipient section.
*/
public function validate_mail_recipient( $template, $content ) {
$section = sprintf( '%s.recipient', $template );
if ( $this->supports( 'invalid_mailbox_syntax' ) ) {
if ( $this->detect_invalid_mailbox_syntax( $section, $content ) ) {
$this->add_error( $section, 'invalid_mailbox_syntax',
array(
'message' => __( "Invalid mailbox syntax is used.", 'contact-form-7' ),
)
);
} else {
$this->remove_error( $section, 'invalid_mailbox_syntax' );
}
}
$recipient = $recipient->replace_tags();
$recipient = wpcf7_strip_newline( $recipient );
if ( $this->supports( 'unsafe_email_without_protection' ) ) {
$this->remove_error( $section, 'unsafe_email_without_protection' );
$this->detect_invalid_mailbox_syntax(
sprintf( '%s.recipient', $template ),
$recipient
if ( ! $this->has_error( $section, 'invalid_mailbox_syntax' ) ) {
if (
$this->detect_unsafe_email_without_protection( $section, $content )
) {
$this->add_error( $section, 'unsafe_email_without_protection',
array(
'message' => __( "Unsafe email config is used without sufficient protection.", 'contact-form-7' ),
)
);
}
}
}
}
$additional_headers = new WPCF7_MailTaggedText(
$components['additional_headers'],
array( 'callback' => $callback )
);
$additional_headers = $additional_headers->replace_tags();
$additional_headers = explode( "\n", $additional_headers );
$mailbox_header_types = array( 'reply-to', 'cc', 'bcc' );
$invalid_mail_header_exists = false;
/**
* Runs error detection for the mail additional headers section.
*/
public function validate_mail_additional_headers( $template, $content ) {
$section = sprintf( '%s.additional_headers', $template );
foreach ( $additional_headers as $header ) {
$invalid_mail_headers = array();
$invalid_mailbox_fields = array();
$unsafe_email_fields = array();
foreach ( explode( "\n", $content ) as $header ) {
$header = trim( $header );
if ( '' === $header ) {
continue;
}
if ( ! preg_match( '/^([0-9A-Za-z-]+):(.*)$/', $header, $matches ) ) {
$invalid_mail_header_exists = true;
} else {
$is_valid_header = preg_match(
'/^([0-9A-Za-z-]+):(.*)$/',
$header,
$matches
);
if ( ! $is_valid_header ) {
$invalid_mail_headers[] = $header;
continue;
}
$header_name = $matches[1];
$header_value = trim( $matches[2] );
if ( in_array( strtolower( $header_name ), $mailbox_header_types )
and '' !== $header_value ) {
$this->detect_invalid_mailbox_syntax(
sprintf( '%s.additional_headers', $template ),
$header_value,
array(
'message' => __( "Invalid mailbox syntax is used in the %name% field.", 'contact-form-7' ),
'params' => array( 'name' => $header_name )
)
);
if (
in_array(
strtolower( $header_name ), array( 'reply-to', 'cc', 'bcc' )
) and
'' !== $header_value and
$this->detect_invalid_mailbox_syntax( $section, $header_value )
) {
$invalid_mailbox_fields[] = $header_name;
continue;
}
if (
in_array( strtolower( $header_name ), array( 'cc', 'bcc' ) ) and
$this->detect_unsafe_email_without_protection( $section, $header_value )
) {
$unsafe_email_fields[] = $header_name;
}
}
if ( $invalid_mail_header_exists ) {
$this->add_error( sprintf( '%s.additional_headers', $template ),
'invalid_mail_header',
if ( $this->supports( 'invalid_mail_header' ) ) {
if ( ! empty( $invalid_mail_headers ) ) {
$this->add_error( $section, 'invalid_mail_header',
array(
'message' => __( "There are invalid mail header fields.", 'contact-form-7' ),
)
);
} else {
$this->remove_error( $section, 'invalid_mail_header' );
}
}
$body = new WPCF7_MailTaggedText(
$components['body'],
array( 'callback' => $callback )
if ( $this->supports( 'invalid_mailbox_syntax' ) ) {
if ( ! empty( $invalid_mailbox_fields ) ) {
foreach ( $invalid_mailbox_fields as $header_name ) {
$this->add_error( $section, 'invalid_mailbox_syntax',
array(
'message' => __( "Invalid mailbox syntax is used in the %name% field.", 'contact-form-7' ),
'params' => array( 'name' => $header_name ),
)
);
}
} else {
$this->remove_error( $section, 'invalid_mailbox_syntax' );
}
}
$body = $body->replace_tags();
if ( $this->supports( 'unsafe_email_without_protection' ) ) {
if ( ! empty( $unsafe_email_fields ) ) {
$this->add_error( $section, 'unsafe_email_without_protection',
array(
'message' => __( "Unsafe email config is used without sufficient protection.", 'contact-form-7' ),
)
);
} else {
$this->remove_error( $section, 'unsafe_email_without_protection' );
}
}
}
$this->detect_maybe_empty( sprintf( '%s.body', $template ), $body );
if ( '' !== $components['attachments'] ) {
/**
* Runs error detection for the mail body section.
*/
public function validate_mail_body( $template, $content ) {
$section = sprintf( '%s.body', $template );
if ( $this->supports( 'maybe_empty' ) ) {
if ( $this->detect_maybe_empty( $section, $content ) ) {
$this->add_error( $section, 'maybe_empty',
array(
'message' => __( "There is a possible empty field.", 'contact-form-7' ),
)
);
} else {
$this->remove_error( $section, 'maybe_empty' );
}
}
}
/**
* Runs error detection for the mail attachments section.
*/
public function validate_mail_attachments( $template, $content ) {
$section = sprintf( '%s.attachments', $template );
$total_size = 0;
$files_not_found = array();
$files_out_of_content = array();
if ( '' !== $content ) {
$attachables = array();
$tags = $this->contact_form->scan_form_tags(
@@ -236,7 +396,7 @@ trait WPCF7_ConfigValidator_Mail {
foreach ( $tags as $tag ) {
$name = $tag->name;
if ( ! str_contains( $components['attachments'], "[{$name}]" ) ) {
if ( ! str_contains( $content, "[{$name}]" ) ) {
continue;
}
@@ -249,41 +409,61 @@ trait WPCF7_ConfigValidator_Mail {
$total_size = array_sum( $attachables );
$has_file_not_found = false;
$has_file_not_in_content_dir = false;
foreach ( explode( "\n", $components['attachments'] ) as $line ) {
foreach ( explode( "\n", $content ) as $line ) {
$line = trim( $line );
if ( '' === $line or str_starts_with( $line, '[' ) ) {
continue;
}
$has_file_not_found = $this->detect_file_not_found(
sprintf( '%s.attachments', $template ), $line
);
if ( ! $has_file_not_found and ! $has_file_not_in_content_dir ) {
$has_file_not_in_content_dir = $this->detect_file_not_in_content_dir(
sprintf( '%s.attachments', $template ), $line
);
}
if ( ! $has_file_not_found ) {
$path = path_join( WP_CONTENT_DIR, $line );
if ( $this->detect_file_not_found( $section, $line ) ) {
$files_not_found[] = $line;
} elseif ( $this->detect_file_not_in_content_dir( $section, $line ) ) {
$files_out_of_content[] = $line;
} else {
$total_size += (int) @filesize( $path );
}
}
}
if ( $this->supports( 'file_not_found' ) ) {
if ( ! empty( $files_not_found ) ) {
foreach ( $files_not_found as $line ) {
$this->add_error( $section, 'file_not_found',
array(
'message' => __( "Attachment file does not exist at %path%.", 'contact-form-7' ),
'params' => array( 'path' => $line ),
)
);
}
} else {
$this->remove_error( $section, 'file_not_found' );
}
}
if ( $this->supports( 'file_not_in_content_dir' ) ) {
if ( ! empty( $files_out_of_content ) ) {
$this->add_error( $section, 'file_not_in_content_dir',
array(
'message' => __( "It is not allowed to use files outside the wp-content directory.", 'contact-form-7' ),
)
);
} else {
$this->remove_error( $section, 'file_not_in_content_dir' );
}
}
if ( $this->supports( 'attachments_overweight' ) ) {
$max = 25 * MB_IN_BYTES; // 25 MB
if ( $max < $total_size ) {
$this->add_error( sprintf( '%s.attachments', $template ),
'attachments_overweight',
$this->add_error( $section, 'attachments_overweight',
array(
'message' => __( "The total size of attachment files is too large.", 'contact-form-7' ),
)
);
} else {
$this->remove_error( $section, 'attachments_overweight' );
}
}
}
@@ -294,17 +474,12 @@ trait WPCF7_ConfigValidator_Mail {
*
* @link https://contactform7.com/configuration-errors/invalid-mailbox-syntax/
*/
public function detect_invalid_mailbox_syntax( $section, $content, $args = '' ) {
$args = wp_parse_args( $args, array(
'message' => __( "Invalid mailbox syntax is used.", 'contact-form-7' ),
'params' => array(),
) );
public function detect_invalid_mailbox_syntax( $section, $content ) {
$content = $this->replace_mail_tags( $content );
$content = wpcf7_strip_newline( $content );
if ( ! wpcf7_is_mailbox_list( $content ) ) {
return $this->add_error( $section,
'invalid_mailbox_syntax',
$args
);
return true;
}
return false;
@@ -317,13 +492,11 @@ trait WPCF7_ConfigValidator_Mail {
* @link https://contactform7.com/configuration-errors/maybe-empty/
*/
public function detect_maybe_empty( $section, $content ) {
$content = $this->replace_mail_tags( $content );
$content = wpcf7_strip_newline( $content );
if ( '' === $content ) {
return $this->add_error( $section,
'maybe_empty',
array(
'message' => __( "There is a possible empty field.", 'contact-form-7' ),
)
);
return true;
}
return false;
@@ -339,13 +512,7 @@ trait WPCF7_ConfigValidator_Mail {
$path = path_join( WP_CONTENT_DIR, $content );
if ( ! is_readable( $path ) or ! is_file( $path ) ) {
return $this->add_error( $section,
'file_not_found',
array(
'message' => __( "Attachment file does not exist at %path%.", 'contact-form-7' ),
'params' => array( 'path' => $content ),
)
);
return true;
}
return false;
@@ -361,12 +528,73 @@ trait WPCF7_ConfigValidator_Mail {
$path = path_join( WP_CONTENT_DIR, $content );
if ( ! wpcf7_is_file_path_in_content_dir( $path ) ) {
return $this->add_error( $section,
'file_not_in_content_dir',
array(
'message' => __( "It is not allowed to use files outside the wp-content directory.", 'contact-form-7' ),
)
return true;
}
return false;
}
/**
* Detects errors of that unsafe email config is used without
* sufficient protection.
*
* @link https://contactform7.com/configuration-errors/unsafe-email-without-protection/
*/
public function detect_unsafe_email_without_protection( $section, $content ) {
static $is_recaptcha_active = null;
if ( null === $is_recaptcha_active ) {
$is_recaptcha_active = call_user_func( function () {
$service = WPCF7_RECAPTCHA::get_instance();
return $service->is_active();
} );
}
if ( $is_recaptcha_active ) {
return false;
}
$example_email = 'user-specified@example.com';
// Replace mail-tags connected to an email type form-tag first.
$content = $this->replace_mail_tags( $content, array(
'callback' => function ( $matches ) use ( $example_email ) {
// allow [[foo]] syntax for escaping a tag
if ( $matches[1] === '[' and $matches[4] === ']' ) {
return substr( $matches[0], 1, -1 );
}
$tag = $matches[0];
$tagname = $matches[2];
$values = $matches[3];
$mail_tag = new WPCF7_MailTag( $tag, $tagname, $values );
$field_name = $mail_tag->field_name();
$form_tags = $this->contact_form->scan_form_tags(
array( 'name' => $field_name )
);
if ( $form_tags ) {
$form_tag = new WPCF7_FormTag( $form_tags[0] );
if ( 'email' === $form_tag->basetype ) {
return $example_email;
}
}
return $tag;
},
) );
// Replace remaining mail-tags.
$content = $this->replace_mail_tags( $content );
$content = wpcf7_strip_newline( $content );
if ( str_contains( $content, $example_email ) ) {
return true;
}
return false;

View File

@@ -12,14 +12,27 @@ trait WPCF7_ConfigValidator_Messages {
return;
}
if ( isset( $messages['captcha_not_match'] )
and ! wpcf7_use_really_simple_captcha() ) {
if (
isset( $messages['captcha_not_match'] ) and
! wpcf7_use_really_simple_captcha()
) {
unset( $messages['captcha_not_match'] );
}
foreach ( $messages as $key => $message ) {
$section = sprintf( 'messages.%s', $key );
$this->detect_html_in_message( $section, $message );
if ( $this->supports( 'html_in_message' ) ) {
if ( $this->detect_html_in_message( $section, $message ) ) {
$this->add_error( $section, 'html_in_message',
array(
'message' => __( "HTML tags are used in a message.", 'contact-form-7' ),
)
);
} else {
$this->remove_error( $section, 'html_in_message' );
}
}
}
}
@@ -32,13 +45,8 @@ trait WPCF7_ConfigValidator_Messages {
public function detect_html_in_message( $section, $content ) {
$stripped = wp_strip_all_tags( $content );
if ( $stripped != $content ) {
return $this->add_error( $section,
'html_in_message',
array(
'message' => __( "HTML tags are used in a message.", 'contact-form-7' ),
)
);
if ( $stripped !== $content ) {
return true;
}
return false;

View File

@@ -4,6 +4,7 @@ require_once path_join( __DIR__, 'form.php' );
require_once path_join( __DIR__, 'mail.php' );
require_once path_join( __DIR__, 'messages.php' );
require_once path_join( __DIR__, 'additional-settings.php' );
require_once path_join( __DIR__, 'actions.php' );
/**
@@ -13,15 +14,10 @@ require_once path_join( __DIR__, 'additional-settings.php' );
*/
class WPCF7_ConfigValidator {
use WPCF7_ConfigValidator_Form;
use WPCF7_ConfigValidator_Mail;
use WPCF7_ConfigValidator_Messages;
use WPCF7_ConfigValidator_AdditionalSettings;
/**
* The plugin version in which important updates happened last time.
*/
const last_important_update = '5.6.1';
const last_important_update = '5.8.1';
const error_codes = array(
'maybe_empty',
@@ -39,10 +35,18 @@ class WPCF7_ConfigValidator {
'dots_in_names',
'colons_in_names',
'upload_filesize_overlimit',
'unsafe_email_without_protection',
);
use WPCF7_ConfigValidator_Form;
use WPCF7_ConfigValidator_Mail;
use WPCF7_ConfigValidator_Messages;
use WPCF7_ConfigValidator_AdditionalSettings;
private $contact_form;
private $errors = array();
private $include;
private $exclude;
/**
@@ -66,8 +70,21 @@ class WPCF7_ConfigValidator {
/**
* Constructor.
*/
public function __construct( WPCF7_ContactForm $contact_form ) {
public function __construct( WPCF7_ContactForm $contact_form, $args = '' ) {
$args = wp_parse_args( $args, array(
'include' => null,
'exclude' => null,
) );
$this->contact_form = $contact_form;
if ( isset( $args['include'] ) ) {
$this->include = (array) $args['include'];
}
if ( isset( $args['exclude'] ) ) {
$this->exclude = (array) $args['exclude'];
}
}
@@ -87,6 +104,24 @@ class WPCF7_ConfigValidator {
}
/**
* Returns true if the given error code is supported by this instance.
*/
public function supports( $error_code ) {
if ( isset( $this->include ) ) {
$supported_codes = array_intersect( self::error_codes, $this->include );
} else {
$supported_codes = self::error_codes;
}
if ( isset( $this->exclude ) ) {
$supported_codes = array_diff( $supported_codes, $this->exclude );
}
return in_array( $error_code, $supported_codes, true );
}
/**
* Counts detected errors.
*/
@@ -195,6 +230,27 @@ class WPCF7_ConfigValidator {
}
/**
* Returns true if the specified section has the specified error.
*
* @param string $section The section where the error detected.
* @param string $code The unique code of the error.
*/
public function has_error( $section, $code ) {
if ( empty( $this->errors[$section] ) ) {
return false;
}
foreach ( (array) $this->errors[$section] as $error ) {
if ( isset( $error['code'] ) and $error['code'] === $code ) {
return true;
}
}
return false;
}
/**
* Adds a validation error.
*
@@ -264,8 +320,6 @@ class WPCF7_ConfigValidator {
* @return bool True if there is no error detected.
*/
public function validate() {
$this->errors = array();
$this->validate_form();
$this->validate_mail( 'mail' );
$this->validate_mail( 'mail_2' );

View File

@@ -221,7 +221,7 @@ function wpcf7_contact_form_tag_func( $atts, $content = null, $code = '' ) {
return '[contact-form-7]';
}
if ( 'contact-form-7' == $code ) {
if ( 'contact-form-7' === $code ) {
$atts = shortcode_atts(
array(
'id' => '',
@@ -269,11 +269,15 @@ function wpcf7_contact_form_tag_func( $atts, $content = null, $code = '' ) {
return $contact_form->form_html( $atts );
};
return wpcf7_switch_locale(
$output = wpcf7_switch_locale(
$contact_form->locale(),
$callback,
$contact_form, $atts
);
do_action( 'wpcf7_shortcode_callback', $contact_form, $atts );
return $output;
}

View File

@@ -74,7 +74,7 @@ class WPCF7_ContactFormTemplate {
. '-- ' . "\n"
. sprintf(
/* translators: 1: blog name, 2: blog URL */
__( 'This e-mail was sent from a contact form on %1$s (%2$s)', 'contact-form-7' ),
__( 'This is a notification that a contact form was submitted on your website (%1$s %2$s).', 'contact-form-7' ),
'[_site_title]',
'[_site_url]'
),
@@ -108,7 +108,7 @@ class WPCF7_ContactFormTemplate {
. '-- ' . "\n"
. sprintf(
/* translators: 1: blog name, 2: blog URL */
__( 'This e-mail was sent from a contact form on %1$s (%2$s)', 'contact-form-7' ),
__( 'This email is a receipt for your contact form submission on our website (%1$s %2$s) in which your email address was used. If that was not you, please ignore this message.', 'contact-form-7' ),
'[_site_title]',
'[_site_url]'
),

View File

@@ -140,6 +140,10 @@ form.submitting .wpcf7-spinner {
}
}
.wpcf7 [inert] {
opacity: 0.5;
}
.wpcf7 input[type="file"] {
cursor: pointer;
}

View File

@@ -410,7 +410,7 @@ class WPCF7_FormTag implements ArrayAccess {
if ( $contact_form = WPCF7_ContactForm::get_current() ) {
$val = $contact_form->shortcode_attr( $this->name );
if ( strlen( $val ) ) {
if ( isset( $val ) and strlen( $val ) ) {
if ( $args['multiple'] ) {
$values[] = $val;
} else {

View File

@@ -522,7 +522,13 @@ function wpcf7_format_atts( $atts ) {
}
static $boolean_attributes = array(
'checked', 'disabled', 'multiple', 'readonly', 'required', 'selected',
'checked',
'disabled',
'inert',
'multiple',
'readonly',
'required',
'selected',
);
if ( in_array( $name, $boolean_attributes ) and '' === $value ) {

View File

@@ -210,7 +210,7 @@ function wpcf7_validate_configuration() {
/**
* Returns true if wpcf7_autop() is applied to form content.
* Returns true if wpcf7_autop() is applied.
*/
function wpcf7_autop_or_not() {
return (bool) apply_filters( 'wpcf7_autop_or_not', WPCF7_AUTOP );

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -253,18 +253,46 @@ function wpcf7_is_email_in_site_domain( $email ) {
* false otherwise.
*/
function wpcf7_is_file_path_in_content_dir( $path ) {
if ( ! is_string( $path ) or '' === $path ) {
return false;
}
$callback = static function ( $path, $dir ) {
if ( $real_path = realpath( $path ) ) {
$path = $real_path;
} else {
return false;
}
if ( 0 === strpos( $path, realpath( WP_CONTENT_DIR ) ) ) {
if ( $real_dir = realpath( $dir ) ) {
$dir = trailingslashit( $real_dir );
} else {
return false;
}
return str_starts_with(
wp_normalize_path( $path ),
wp_normalize_path( $dir )
);
};
if (
call_user_func( $callback, $path, WP_CONTENT_DIR )
) {
return true;
}
if ( defined( 'UPLOADS' )
and 0 === strpos( $path, realpath( ABSPATH . UPLOADS ) ) ) {
if (
defined( 'UPLOADS' ) and
call_user_func( $callback, $path, ABSPATH . UPLOADS )
) {
return true;
}
if (
defined( 'WP_TEMP_DIR' ) and
call_user_func( $callback, $path, WP_TEMP_DIR )
) {
return true;
}

View File

@@ -110,10 +110,14 @@ class WPCF7 {
* @param mixed $value Option value.
*/
public static function update_option( $name, $value ) {
$option = get_option( 'wpcf7' );
$option = ( false === $option ) ? array() : (array) $option;
$option = array_merge( $option, array( $name => $value ) );
update_option( 'wpcf7', $option );
$old_option = get_option( 'wpcf7' );
$old_option = ( false === $old_option ) ? array() : (array) $old_option;
update_option( 'wpcf7',
array_merge( $old_option, array( $name => $value ) )
);
do_action( 'wpcf7_update_option', $name, $value, $old_option );
}
}

View File

@@ -1 +1 @@
document.addEventListener("DOMContentLoaded",(t=>{var e;wpcf7_recaptcha={...null!==(e=wpcf7_recaptcha)&&void 0!==e?e:{}};const c=wpcf7_recaptcha.sitekey,{homepage:n,contactform:a}=wpcf7_recaptcha.actions,o=t=>{const{action:e,func:n,params:a}=t;grecaptcha.execute(c,{action:e}).then((t=>{const c=new CustomEvent("wpcf7grecaptchaexecuted",{detail:{action:e,token:t}});document.dispatchEvent(c)})).then((()=>{"function"==typeof n&&n(...a)})).catch((t=>console.error(t)))};if(grecaptcha.ready((()=>{o({action:n})})),document.addEventListener("change",(t=>{o({action:a})})),"undefined"!=typeof wpcf7&&"function"==typeof wpcf7.submit){const t=wpcf7.submit;wpcf7.submit=function(e){o({action:a,func:t,params:[e,arguments.length>1&&void 0!==arguments[1]?arguments[1]:{}]})}}document.addEventListener("wpcf7grecaptchaexecuted",(t=>{const e=document.querySelectorAll('form.wpcf7-form input[name="_wpcf7_recaptcha_response"]');for(let c=0;c<e.length;c++)e[c].setAttribute("value",t.detail.token)}))}));
document.addEventListener("DOMContentLoaded",(e=>{var t;wpcf7_recaptcha={...null!==(t=wpcf7_recaptcha)&&void 0!==t?t:{}};const c=wpcf7_recaptcha.sitekey,{homepage:n,contactform:a}=wpcf7_recaptcha.actions,o=e=>{const{action:t,func:n,params:a}=e;grecaptcha.execute(c,{action:t}).then((e=>{const c=new CustomEvent("wpcf7grecaptchaexecuted",{detail:{action:t,token:e}});document.dispatchEvent(c)})).then((()=>{"function"==typeof n&&n(...a)})).catch((e=>console.error(e)))};if(grecaptcha.ready((()=>{o({action:n})})),document.addEventListener("change",(e=>{o({action:a})})),"undefined"!=typeof wpcf7&&"function"==typeof wpcf7.submit){const e=wpcf7.submit;wpcf7.submit=(t,c={})=>{o({action:a,func:e,params:[t,c]})}}document.addEventListener("wpcf7grecaptchaexecuted",(e=>{const t=document.querySelectorAll('form.wpcf7-form input[name="_wpcf7_recaptcha_response"]');for(let c=0;c<t.length;c++)t[c].setAttribute("value",e.detail.token)}))}));

View File

@@ -98,7 +98,7 @@ function wpcf7_sendinblue_editor_panels( $panels ) {
),
wpcf7_link(
__( 'https://contactform7.com/sendinblue-integration/', 'contact-form-7' ),
__( 'Brevo (formerly Sendinblue) integration', 'contact-form-7' )
__( 'Brevo integration', 'contact-form-7' )
)
);
@@ -106,7 +106,7 @@ function wpcf7_sendinblue_editor_panels( $panels ) {
$templates = $service->get_templates();
?>
<h2><?php echo esc_html( __( 'Brevo (formerly Sendinblue)', 'contact-form-7' ) ); ?></h2>
<h2><?php echo esc_html( __( 'Brevo', 'contact-form-7' ) ); ?></h2>
<fieldset>
<legend><?php echo $description; ?></legend>

View File

@@ -1,6 +1,6 @@
<?php
/**
* Brevo (formerly Sendinblue) module main file
* Brevo module main file
*
* @link https://contactform7.com/sendinblue-integration/
*/

View File

@@ -27,7 +27,7 @@ class WPCF7_Sendinblue extends WPCF7_Service {
}
public function get_title() {
return __( 'Brevo (formerly Sendinblue)', 'contact-form-7' );
return __( 'Brevo', 'contact-form-7' );
}
public function is_active() {
@@ -47,7 +47,7 @@ class WPCF7_Sendinblue extends WPCF7_Service {
public function link() {
echo wpcf7_link(
'https://www.brevo.com/?tap_a=30591-fb13f0&tap_s=1031580-b1bb1d',
'https://www.brevo.com/',
'brevo.com'
);
}
@@ -153,7 +153,7 @@ class WPCF7_Sendinblue extends WPCF7_Service {
'<p><strong>%s</strong></p>',
wpcf7_link(
__( 'https://contactform7.com/sendinblue-integration/', 'contact-form-7' ),
__( 'Brevo (formerly Sendinblue) integration', 'contact-form-7' )
__( 'Brevo integration', 'contact-form-7' )
)
);

View File

@@ -4,8 +4,8 @@ Donate link: https://contactform7.com/donate/
Tags: contact, form, contact form, feedback, email, ajax, captcha, akismet, multilingual
Requires at least: 6.2
Requires PHP: 7.4
Tested up to: 6.3
Stable tag: 5.8
Tested up to: 6.4
Stable tag: 5.8.3
License: GPLv2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html
@@ -37,7 +37,7 @@ If you activate certain features in this plugin, the contact form submitter's pe
* reCAPTCHA ([Google](https://policies.google.com/?hl=en))
* Akismet ([Automattic](https://automattic.com/privacy/))
* Constant Contact ([Endurance International Group](https://www.endurance.com/privacy))
* [Brevo (formerly Sendinblue)](https://www.brevo.com/legal/privacypolicy/)
* [Brevo](https://www.brevo.com/legal/privacypolicy/)
* [Stripe](https://stripe.com/privacy)
= Recommended plugins =
@@ -78,6 +78,18 @@ Do you have questions or issues with Contact Form 7? Use these support channels
For more information, see [Releases](https://contactform7.com/category/releases/).
= 5.8.3 =
[https://contactform7.com/contact-form-7-583/](https://contactform7.com/contact-form-7-583/)
= 5.8.2 =
[https://contactform7.com/contact-form-7-582/](https://contactform7.com/contact-form-7-582/)
= 5.8.1 =
[https://contactform7.com/contact-form-7-581/](https://contactform7.com/contact-form-7-581/)
= 5.8 =
[https://contactform7.com/contact-form-7-58/](https://contactform7.com/contact-form-7-58/)

View File

@@ -7,12 +7,12 @@
* Author URI: https://ideasilo.wordpress.com/
* License: GPL v2 or later
* License URI: https://www.gnu.org/licenses/gpl-2.0.html
* Version: 5.8
* Version: 5.8.3
* Requires at least: 6.2
* Requires PHP: 7.4
*/
define( 'WPCF7_VERSION', '5.8' );
define( 'WPCF7_VERSION', '5.8.3' );
define( 'WPCF7_REQUIRED_WP_VERSION', '6.2' );

View File

@@ -0,0 +1,6 @@
# Security Policy
## Reporting Security Bugs
Please report security bugs found in the site-reviews plugin's source code through the [Patchstack Vulnerability Disclosure Program](https://patchstack.com/database/vdp/imagify). The Patchstack team will assist you with verification, CVE assignment and take care of notifying the developers of this plugin.
---

View File

@@ -0,0 +1,20 @@
/**
* @typedef {import('@roots/bud').Bud} Bud
*
* @param {Bud} bud
*/
module.exports = async bud => {
bud.externals({
jQuery: 'window.jquery',
wp: 'window.wp',
})
bud.runtime('single')
await bud
.setPath('@dist', '../assets/admin')
.entry({
chart: 'chart.js',
bulk: 'bulk.js',
})
//.when( bud.isProduction, () => bud.splitChunks().minimize() )
}

View File

@@ -0,0 +1,15 @@
{
"name": "imagify_dev",
"version": "1.0.0",
"dependencies": {
},
"devDependencies": {
"@roots/bud": "^6.11.0",
"chart.js": "^4.4.0"
},
"scripts": {
"dev": "bud dev",
"build": "bud build",
"bud": "bud"
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,6 @@
import * as chart from 'chart.js/auto'
window.imagify = window.imagify || {};
window.imagify.Color = chart.Colors;
window.imagify.Chart = chart.Chart;

View File

@@ -0,0 +1,14 @@
{
"chart": {
"js": [
"js/runtime.js",
"js/chart.js"
]
},
"bulk": {
"js": [
"js/runtime.js",
"js/bulk.js"
]
}
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
(()=>{"use strict";var r,e={},o={};function t(r){var n=o[r];if(void 0!==n)return n.exports;var s=o[r]={exports:{}};return e[r](s,s.exports,t),s.exports}t.m=e,r=[],t.O=(e,o,n,s)=>{if(!o){var a=1/0;for(u=0;u<r.length;u++){for(var[o,n,s]=r[u],i=!0,f=0;f<o.length;f++)(!1&s||a>=s)&&Object.keys(t.O).every((r=>t.O[r](o[f])))?o.splice(f--,1):(i=!1,s<a&&(a=s));if(i){r.splice(u--,1);var l=n();void 0!==l&&(e=l)}}return e}s=s||0;for(var u=r.length;u>0&&r[u-1][2]>s;u--)r[u]=r[u-1];r[u]=[o,n,s]},t.o=(r,e)=>Object.prototype.hasOwnProperty.call(r,e),(()=>{var r={666:0};t.O.j=e=>0===r[e];var e=(e,o)=>{var n,s,[a,i,f]=o,l=0;if(a.some((e=>0!==r[e]))){for(n in i)t.o(i,n)&&(t.m[n]=i[n]);if(f)var u=f(t)}for(e&&e(o);l<a.length;l++)s=a[l],t.o(r,s)&&r[s]&&r[s][0](),r[s]=0;return t.O(u)},o=self.webpackChunk_roots_bud=self.webpackChunk_roots_bud||[];o.forEach(e.bind(null,0)),o.push=e.bind(null,o.push.bind(o))})()})();

View File

@@ -0,0 +1,6 @@
{
"chart.js": "js/chart.js",
"bulk.js": "js/bulk.js",
"runtime.js": "js/runtime.js",
"entrypoints.json": "entrypoints.json"
}

View File

@@ -160,3 +160,40 @@
color: #FFF;
text-shadow: 0 0 2px rgba(0, 0, 0, 0.2);
}
#wp-admin-bar-imagify-profile .imagify-upsell-admin-bar {
position:relative ;
background: #c51161;
margin: 10px -13px -10px -13px;
padding: 20px;
}
#wp-admin-bar-imagify-profile .imagify-upsell-admin-bar p {
color: #fff;
}
#wp-admin-bar-imagify-profile a.imagify-upsell-admin-bar-button {
display: block;
height: auto !important;
border: 1px solid #fff;
border-radius: 5px;
color: #fff !important;
padding: 5px 10px !important;
text-align: center;
text-decoration: none;
margin-top: 10px;
}
#wpadminbar #wp-admin-bar-imagify-profile a.imagify-upsell-dismiss {
display: inline !important;
height: auto !important;
}
#wpadminbar #wp-admin-bar-imagify-profile .imagify-upsell-dismiss::before {
position: absolute;
top: 5px;
right: 10px;
content: "\2715";
color: #fff;
}

View File

@@ -1543,3 +1543,45 @@ ul.imagify-datas-details.imagify-datas-details {
padding-right: 0em;
}
}
.imagify-upsell {
position:relative;
background: #c51161;
padding: 20px 40px;
}
.imagify-upsell p {
color: #fff !important;
}
.imagify-upsell-button {
display: block;
background: #fff;
border-radius: 5px;
color: #c51161 !important;
font-weight: bold;
padding: 10px;
text-align: center;
text-decoration: none;
text-transform: uppercase;
}
.imagify-upsell-arrow::after {
content: '\2192';
font-size: large;
margin-left: 5px;
vertical-align: top;
}
.imagify-upsell-dismiss::before {
position: absolute;
top: 5px;
right: 5px;
content: "\2715";
color: #2e3243;
font-size: 2em;
}
.imagify-upsell .imagify-meteo-icon {
filter: invert(100%) sepia(100%) saturate(0%) hue-rotate(104deg) brightness(103%) contrast(103%);
}

View File

@@ -330,7 +330,7 @@ body[class*="_imagify-ngg-bulk-optimization"] .media-item .progress .bar {
.imagify-col-content .imagify-meteo-icon {
height: 64px;
margin: 15px 15px 15px 20px;
margin: 15px 15px 15px 0;
}
.imagify-col-content .imagify-section-title + p {

View File

@@ -936,3 +936,19 @@ input:checked + label .imagify-fake-checkbox:after,
margin-top: 20px;
}
}
.imagify-col-content .imagify-space-left {
margin: 15px 30px 15px 0;
}
.imagify-col-content .imagify-space-left p {
margin: 0 0 10px 0;
font-size: 19px;
font-weight: 500;
color: #343A49;
}
.imagify-col-content .imagify-meteo-icon {
height: 64px;
margin: 15px 15px 15px 0;
}

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@@ -6,7 +6,7 @@
*/
$( '.imagify-notice-dismiss' ).on( 'click.imagify', function( e ) {
var $this = $( this ),
$parent = $this.parents( '.imagify-welcome, .imagify-notice, .imagify-rkt-notice' ),
$parent = $this.parents( '.imagify-welcome, .imagify-notice, .imagify-rkt-notice, .imagify-upsell, .imagify-upsell-admin-bar' ),
href = $this.attr( 'href' );
e.preventDefault();

View File

@@ -0,0 +1,81 @@
<?php
namespace Imagify\Admin;
use Imagify\Traits\InstanceGetterTrait;
use Imagify\User\User;
use Imagify_Views;
/**
* Admin bar handler
*/
class AdminBar {
use InstanceGetterTrait;
/**
* Launch the hooks.
*
* @return void
*/
public function init() {
if ( wp_doing_ajax() ) {
add_action( 'wp_ajax_imagify_get_admin_bar_profile', array( $this, 'get_admin_bar_profile_callback' ) );
}
}
/**
* Get admin bar profile output.
*
* @return void
*/
public function get_admin_bar_profile_callback() {
imagify_check_nonce( 'imagify-get-admin-bar-profile', 'imagifygetadminbarprofilenonce' );
if ( ! imagify_get_context( 'wp' )->current_user_can( 'manage' ) ) {
imagify_die();
}
$user = new User();
$views = Imagify_Views::get_instance();
$unconsumed_quota = $views->get_quota_percent();
$text = '';
$button_text = '';
$upgrade_link = '';
if ( $user->is_free() ) {
$text = esc_html__( 'Upgrade your plan now for more!', 'rocket' ) . '<br>' .
esc_html__( 'From $4.99/month only, keep going with image optimization!', 'rocket' );
$button_text = esc_html__( 'Upgrade My Plan', 'rocket' );
$upgrade_link = IMAGIFY_APP_DOMAIN . '/subscription/?utm_source=plugin&utm_medium=notification';
} elseif ( $user->is_growth() ) {
$text = esc_html__( 'Switch to Infinite plan for unlimited optimization:', 'rocket' ) . '<br>';
if ( $user->is_monthly ) {
$text .= esc_html__( 'For $9.99/month, optimize as many images as you like!', 'rocket' );
$upgrade_link = IMAGIFY_APP_DOMAIN . '/subscription/plan_switch/?label=infinite&payment_plan=1&utm_source=plugin&utm_medium=notification ';
} else {
$text .= esc_html__( 'For $99.9/year, optimize as many images as you like!', 'rocket' );
$upgrade_link = IMAGIFY_APP_DOMAIN . '/subscription/plan_switch/?label=infinite&payment_plan=2&utm_source=plugin&utm_medium=notification ';
}
$button_text = esc_html__( 'Switch To Infinite Plan', 'rocket' );
}
$data = [
'quota_icon' => $views->get_quota_icon(),
'quota_class' => $views->get_quota_class(),
'plan_label' => $user->plan_label,
'plan_with_quota' => $user->is_free() || $user->is_growth(),
'unconsumed_quota' => $unconsumed_quota,
'user_quota' => $user->quota,
'next_update' => $user->next_date_update,
'text' => $text,
'button_text' => $button_text,
'upgrade_link' => $upgrade_link,
];
$template = $views->get_template( 'admin/admin-bar-status', $data );
wp_send_json_success( $template );
}
}

View File

@@ -415,11 +415,12 @@ class Bulk {
*
* @param string $method The method used: 'GET' (default), or 'POST'.
* @param string $parameter The name of the parameter to look for.
*
* @return string
*/
public function get_context( $method = 'GET', $parameter = 'context' ) {
$method = 'POST' === $method ? INPUT_POST : INPUT_GET;
$context = filter_input( $method, $parameter, FILTER_SANITIZE_STRING );
$context = 'POST' === $method ? wp_unslash( $_POST[ $parameter ] ) : wp_unslash( $_GET[ $parameter ] ); //phpcs:ignore WordPress.Security.NonceVerification.Missing, WordPress.Security.NonceVerification.Recommended
$context = htmlspecialchars( $context );
return imagify_sanitize_context( $context );
}
@@ -557,7 +558,7 @@ class Bulk {
public function bulk_get_stats_callback() {
imagify_check_nonce( 'imagify-bulk-optimize' );
$folder_types = filter_input( INPUT_GET, 'types', FILTER_SANITIZE_STRING, FILTER_REQUIRE_ARRAY );
$folder_types = filter_input( INPUT_GET, 'types', FILTER_REQUIRE_ARRAY );
$folder_types = is_array( $folder_types ) ? array_filter( $folder_types, 'is_string' ) : [];
if ( ! $folder_types ) {

View File

@@ -95,6 +95,7 @@ class CustomFolders extends AbstractBulk {
$files_table = Imagify_Files_DB::get_instance()->get_table_name();
$folders_table = Imagify_Folders_DB::get_instance()->get_table_name();
$mime_types = Imagify_DB::get_mime_types( 'image' );
$mime_types = str_replace( ",'image/webp'", '', $mime_types );
$webp_suffix = constant( imagify_get_optimization_process_class_name( 'custom-folders' ) . '::WEBP_SUFFIX' );
$files = $wpdb->get_results( $wpdb->prepare( // WPCS: unprepared SQL ok.
"

View File

@@ -184,6 +184,7 @@ class WP extends AbstractBulk {
$this->set_no_time_limit();
$mime_types = Imagify_DB::get_mime_types( 'image' );
$mime_types = str_replace( ",'image/webp'", '', $mime_types );
$statuses = Imagify_DB::get_post_statuses();
$nodata_join = Imagify_DB::get_required_wp_metadata_join_clause();
$nodata_where = Imagify_DB::get_required_wp_metadata_where_clause( [

View File

@@ -4,6 +4,7 @@ declare(strict_types=1);
namespace Imagify\Notices;
use Imagify\Traits\InstanceGetterTrait;
use Imagify\User\User;
/**
* Class that handles the admin notices.
@@ -66,8 +67,6 @@ class Notices {
'http-block-external',
// This warning is displayed when the grid view is active on the library. Dismissible.
'grid-view',
// This warning is displayed to warn the user that the quota is almost consumed for the current month. Dismissible.
'almost-over-quota',
// This warning is displayed if the backup folder is not writable. NOT dismissible.
'backup-folder-not-writable',
// This notice is displayed to rate the plugin after 100 optimizations & 7 days after the first installation. Dismissible.
@@ -76,6 +75,8 @@ class Notices {
'wp-rocket',
'bulk-optimization-complete',
'bulk-optimization-running',
'upsell-banner',
'upsell-admin-bar',
];
/**
@@ -250,7 +251,7 @@ class Notices {
public function renew_almost_over_quota_notice() {
global $wpdb;
$results = $wpdb->get_results( $wpdb->prepare( "SELECT umeta_id, user_id FROM $wpdb->usermeta WHERE meta_key = %s AND meta_value LIKE %s", self::DISMISS_META_NAME, '%almost-over-quota%' ) );
$results = $wpdb->get_results( $wpdb->prepare( "SELECT umeta_id, user_id FROM $wpdb->usermeta WHERE meta_key = %s AND meta_value LIKE %s", self::DISMISS_META_NAME, '%upsell%' ) );
if ( ! $results ) {
return;
@@ -271,7 +272,8 @@ class Notices {
// Renew the notice for all users.
foreach ( $results as $result ) {
self::renew_notice( 'almost-over-quota', $result->user_id );
self::renew_notice( 'upsell-banner', $result->user_id );
self::renew_notice( 'upsell-admin-bar', $result->user_id );
}
}
@@ -439,45 +441,6 @@ class Notices {
return $display;
}
/**
* Tell if the 'almost-over-quota' notice should be displayed.
*
* @since 1.7.0
*
* @return bool|object An Imagify user object. False otherwise.
*/
public function display_almost_over_quota() {
static $display;
if ( isset( $display ) ) {
return $display;
}
$display = false;
if ( ! $this->user_can( 'almost-over-quota' ) ) {
return $display;
}
if ( ! imagify_is_screen( 'imagify-settings' ) && ! imagify_is_screen( 'bulk' ) ) {
return $display;
}
if ( self::notice_is_dismissed( 'almost-over-quota' ) ) {
return $display;
}
$user = new \Imagify_User();
// Don't display the notice if the user's unconsumed quota is superior to 20%.
if ( $user->get_percent_unconsumed_quota() > 20 ) {
return $display;
}
$display = $user;
return $display;
}
/**
* Tell if the 'backup-folder-not-writable' notice should be displayed.
*

View File

@@ -25,6 +25,7 @@ abstract class AbstractData implements DataInterface {
*/
protected $default_optimization_data = [
'status' => '',
'message' => '',
'level' => false,
'sizes' => [],
'stats' => [

View File

@@ -159,6 +159,10 @@ class CustomFolders extends AbstractData {
$old_data['hash'] = md5_file( $file_path );
}
if ( key_exists( 'message', $data ) ) {
$old_data['message'] = $data['message'];
}
if ( ! $data['success'] ) {
/**
* Error.

View File

@@ -93,8 +93,13 @@ class WP extends AbstractData {
'original_size' => 0,
'optimized_size' => 0,
'percent' => 0,
'message' => '',
], $old_data['stats'] );
if ( key_exists( 'message', $data ) ) {
$old_data['message'] = $data['message'];
}
if ( ! $data['success'] ) {
/**
* Error.

View File

@@ -530,6 +530,10 @@ class File {
return new \WP_Error( 'temp_file_not_found', $temp_file->get_error_message() );
}
if ( property_exists( $response, 'message' ) ) {
$args['convert'] = '';
}
if ( 'webp' === $args['convert'] ) {
$destination_path = $this->get_path_to_webp();
$this->path = $destination_path;

View File

@@ -656,6 +656,14 @@ abstract class AbstractProcess implements ProcessInterface {
'non_webp_file_path' => $sizes[ $thumb_size ]['path'], // Don't use $path nor $file->get_path(), it may return the path to a temporary file.
'optimization_level' => $optimization_level,
] );
if ( property_exists( $response, 'message' ) ) {
$path_is_temp = false;
if ( $path !== $sizes[ $thumb_size ]['path'] ) {
$this->filesystem->delete( $path );
}
$path = $sizes[ $thumb_size ]['path'];
}
}
}
}
@@ -731,7 +739,7 @@ abstract class AbstractProcess implements ProcessInterface {
}
// Optimization succeeded.
if ( $args['is_webp'] ) {
if ( ! property_exists( $args['response'], 'message' ) && $args['is_webp'] ) {
/**
* We just created a WebP version:
* Check if it is lighter than the (maybe optimized) non-WebP file.
@@ -776,7 +784,7 @@ abstract class AbstractProcess implements ProcessInterface {
$webp_size = $args['non_webp_thumb_size'] . static::WEBP_SUFFIX;
$webp_file_size = $this->get_data()->get_size_data( $webp_size, 'optimized_size' );
if ( ! $webp_file_size || $webp_file_size < $args['response']->new_size ) {
if ( property_exists( $args['response'], 'message' ) || ! $webp_file_size || $webp_file_size < $args['response']->new_size ) {
// The WebP file is lighter than this one.
return $args['response'];
}
@@ -1531,6 +1539,38 @@ abstract class AbstractProcess implements ProcessInterface {
return is_string( $data ) && strpos( $data, $needle );
}
/**
* Tell if the media has all WebP versions.
*
* @return bool
*/
public function is_full_webp() {
if ( ! $this->is_valid() ) {
return false;
}
if ( ! $this->get_media()->is_image() ) {
return false;
}
$data = $this->get_data()->get_optimization_data();
$sizes = $data['sizes'];
if ( empty( $sizes ) ) {
return false;
}
$keys = array_keys( $sizes );
$non_webp_keys = array_values(array_filter($keys, function ( $key ) {
return strpos( $key, static::WEBP_SUFFIX ) === false;
}));
return array_reduce($non_webp_keys, function ( $is_fully, $key ) use ( $sizes ) {
return key_exists( $key . self::WEBP_SUFFIX, $sizes ) && $is_fully;
}, true);
}
/**
* Tell if a WebP version can be created for the given file.
* Make sure the file is an image before using this method.
@@ -1757,6 +1797,9 @@ abstract class AbstractProcess implements ProcessInterface {
// Size data.
$data['success'] = true;
if ( property_exists( $response, 'message' ) ) {
$data['message'] = imagify_translate_api_message( $response->message );
}
$data['original_size'] = $response->original_size;
$data['optimized_size'] = $response->new_size;
}
@@ -1785,6 +1828,9 @@ abstract class AbstractProcess implements ProcessInterface {
*/
$data = (array) apply_filters( "imagify{$_unauthorized}_file_optimization_data", $data, $response, $size, $level, $this->get_data() );
if ( property_exists( $response, 'message' ) ) {
$size = str_replace( '@imagify-webp', '', $size );
}
// Store.
$this->get_data()->update_size_optimization_data( $size, $data );

View File

@@ -274,6 +274,13 @@ interface ProcessInterface {
*/
public function is_size_webp( $size_name );
/**
* Tell if the media has all WebP versions.
*
* @return bool
*/
public function is_full_webp();
/**
* Tell if the media has WebP versions.
*

View File

@@ -7,6 +7,7 @@ use Imagify\Bulk\Bulk;
use Imagify\CLI\BulkOptimizeCommand;
use Imagify\CLI\GenerateMissingWebpCommand;
use Imagify\Notices\Notices;
use Imagify\Admin\AdminBar;
/**
* Main plugin class.
@@ -56,6 +57,7 @@ class Plugin {
\Imagify\Job\MediaOptimization::get_instance()->init();
\Imagify\Stats\OptimizedMediaWithoutWebp::get_instance()->init();
Bulk::get_instance()->init();
AdminBar::get_instance()->init();
if ( is_admin() ) {
Notices::get_instance()->init();

View File

@@ -0,0 +1,292 @@
<?php
namespace Imagify\User;
use Date;
use Imagify_Data;
use WP_Error;
/**
* Imagify User class.
*
* @since 1.0
*/
class User {
/**
* The Imagify user ID.
*
* @since 1.0
*
* @var string
*/
public $id;
/**
* The user email.
*
* @since 1.0
*
* @var string
*/
public $email;
/**
* The plan ID.
*
* @since 1.0
*
* @var int
*/
public $plan_id;
/**
* The plan label.
*
* @since 1.2
*
* @var string
*/
public $plan_label;
/**
* The total quota.
*
* @since 1.0
*
* @var int
*/
public $quota;
/**
* The total extra quota (Imagify Pack).
*
* @since 1.0
*
* @var int
*/
public $extra_quota;
/**
* The extra quota consumed.
*
* @since 1.0
*
* @var int
*/
public $extra_quota_consumed;
/**
* The current month consumed quota.
*
* @since 1.0
*
* @var int
*/
public $consumed_current_month_quota;
/**
* The next month date to credit the account.
*
* @since 1.1.1
*
* @var Date
*/
public $next_date_update;
/**
* If the account is activate or not.
*
* @since 1.0.1
*
* @var bool
*/
public $is_active;
/**
* If the account is monthly or yearly.
*
* @var bool
*/
public $is_monthly;
/**
* Store a \WP_Error object if the request to fetch the user data failed.
* False overwise.
*
* @var bool|WP_Error
* @since 1.9.9
*/
private $error;
/**
* The constructor.
*
* @since 1.0
*
* @return void
*/
public function __construct() {
$user = get_imagify_user();
if ( is_wp_error( $user ) ) {
$this->error = $user;
return;
}
$this->id = $user->id;
$this->email = $user->email;
$this->plan_id = (int) $user->plan_id;
$this->plan_label = ucfirst( $user->plan_label );
$this->quota = $user->quota;
$this->extra_quota = $user->extra_quota;
$this->extra_quota_consumed = $user->extra_quota_consumed;
$this->consumed_current_month_quota = $user->consumed_current_month_quota;
$this->next_date_update = $user->next_date_update;
$this->is_active = $user->is_active;
$this->is_monthly = $user->is_monthly;
$this->error = false;
}
/**
* Get the possible error returned when fetching user data.
*
* @return bool|WP_Error A \WP_Error object if the request to fetch the user data failed. False overwise.
* @since 1.9.9
*/
public function get_error() {
return $this->error;
}
/**
* Percentage of consumed quota, including extra quota.
*
* @since 1.0
*
* @return float|int
*/
public function get_percent_consumed_quota() {
static $done = false;
if ( $this->get_error() ) {
return 0;
}
$quota = $this->quota;
$consumed_quota = $this->consumed_current_month_quota;
if ( imagify_round_half_five( $this->extra_quota_consumed ) < $this->extra_quota ) {
$quota += $this->extra_quota;
$consumed_quota += $this->extra_quota_consumed;
}
if ( ! $quota || ! $consumed_quota ) {
$percent = 0;
} else {
$percent = 100 * $consumed_quota / $quota;
$percent = round( $percent, 1 );
$percent = min( max( 0, $percent ), 100 );
}
$percent = (float) $percent;
if ( $done ) {
return $percent;
}
$previous_percent = Imagify_Data::get_instance()->get( 'previous_quota_percent' );
// Percent is not 100% anymore.
if ( 100.0 === (float) $previous_percent && $percent < 100 ) {
/**
* Triggered when the consumed quota percent decreases below 100%.
*
* @since 1.7
* @author Grégory Viguier
*
* @param float|int $percent The current percentage of consumed quota.
*/
do_action( 'imagify_not_over_quota_anymore', $percent );
}
// Percent is not >= 80% anymore.
if ( ( (float) $previous_percent >= 80.0 && $percent < 80 ) ) {
/**
* Triggered when the consumed quota percent decreases below 80%.
*
* @since 1.7
* @author Grégory Viguier
*
* @param float|int $percent The current percentage of consumed quota.
* @param float|int $previous_percent The previous percentage of consumed quota.
*/
do_action( 'imagify_not_almost_over_quota_anymore', $percent, $previous_percent );
}
if ( (float) $previous_percent !== (float) $percent ) {
Imagify_Data::get_instance()->set( 'previous_quota_percent', $percent );
}
$done = true;
return $percent;
}
/**
* Count percent of unconsumed quota.
*
* @since 1.0
*
* @return float|int
*/
public function get_percent_unconsumed_quota() {
return 100 - $this->get_percent_consumed_quota();
}
/**
* Check if the user has a free account.
*
* @since 1.1.1
*
* @return bool
*/
public function is_free() {
return 1 === $this->plan_id;
}
/**
* Check if the user is a growth account
*
* @return bool
*/
public function is_growth() {
return ( 16 === $this->plan_id || 18 === $this->plan_id );
}
/**
* Check if the user is an infinite account
*
* @return bool
*/
public function is_infinite() {
return ( 15 === $this->plan_id || 17 === $this->plan_id );
}
/**
* Check if the user has consumed all his/her quota.
*
* @since 1.1.1
* @since 1.9.9 Return false if the request to fetch the user data failed.
*
* @return bool
*/
public function is_over_quota() {
if ( $this->get_error() ) {
return false;
}
return (
$this->is_free()
&&
floatval( 100 ) === round( $this->get_percent_consumed_quota() )
);
}
}

View File

@@ -3,6 +3,7 @@ namespace Imagify\Webp\RewriteRules;
use Imagify\Notices\Notices;
use Imagify\Traits\InstanceGetterTrait;
use Imagify\WriteFile\AbstractWriteDirConfFile;
/**
* Display WebP images on the site with rewrite rules.
@@ -12,6 +13,13 @@ use Imagify\Traits\InstanceGetterTrait;
class Display {
use InstanceGetterTrait;
/**
* Configuration file writer.
*
* @var AbstractWriteDirConfFile
*/
protected $server_conf;
/**
* Option value.
*

View File

@@ -3,7 +3,7 @@
* Plugin Name: Imagify
* Plugin URI: https://wordpress.org/plugins/imagify/
* Description: Dramatically reduce image file sizes without losing quality, make your website load faster, boost your SEO and save money on your bandwidth using Imagify, the new most advanced image optimization tool.
* Version: 2.1.1
* Version: 2.1.3
* Requires at least: 5.3
* Requires PHP: 7.0
* Author: Imagify Optimize Images & Convert WebP
@@ -13,13 +13,13 @@
* Text Domain: imagify
* Domain Path: languages
*
* Copyright 2022 WP Media
* Copyright 2023 WP Media
*/
defined( 'ABSPATH' ) || die( 'Cheatin uh?' );
// Imagify defines.
define( 'IMAGIFY_VERSION', '2.1.1' );
define( 'IMAGIFY_VERSION', '2.1.3' );
define( 'IMAGIFY_SLUG', 'imagify' );
define( 'IMAGIFY_FILE', __FILE__ );
define( 'IMAGIFY_PATH', realpath( plugin_dir_path( IMAGIFY_FILE ) ) . '/' );

View File

@@ -123,7 +123,9 @@ function _imagify_sort_attachments_by_status( $vars ) {
),
) );
if ( ! key_exists( 'post_mime_type', $vars ) ) {
$vars['post_mime_type'] = imagify_get_mime_types();
}
return $vars;
}

View File

@@ -18,6 +18,13 @@ abstract class Imagify_Abstract_Background_Process extends Imagify_WP_Background
*/
protected $prefix = 'imagify';
/**
* URL to query on.
*
* @var string
*/
protected $query_url = '';
/**
* Set to true to automatically displatch at the end of the page.
*

View File

@@ -49,7 +49,6 @@ class Imagify_Admin_Ajax_Post extends Imagify_Admin_Ajax_Post_Deprecated {
// Account.
'imagify_signup',
'imagify_check_api_key_validity',
'imagify_get_admin_bar_profile',
'imagify_get_prices',
'imagify_check_coupon',
'imagify_get_discount',
@@ -220,13 +219,13 @@ class Imagify_Admin_Ajax_Post extends Imagify_Admin_Ajax_Post_Deprecated {
$process = imagify_get_optimization_process( $media_id, $context );
if ( ! $process->is_valid() ) {
return new \WP_Error( 'invalid_media', __( 'This media is not valid.', 'imagify' ) );
return new WP_Error( 'invalid_media', __( 'This media is not valid.', 'imagify' ) );
}
$data = $process->get_data();
if ( ! $data->is_already_optimized() ) {
return new \WP_Error( 'not_already_optimized', __( 'This media does not have the right optimization status.', 'imagify' ) );
return new WP_Error( 'not_already_optimized', __( 'This media does not have the right optimization status.', 'imagify' ) );
}
if ( ! $process->has_webp() ) {
@@ -237,7 +236,7 @@ class Imagify_Admin_Ajax_Post extends Imagify_Admin_Ajax_Post_Deprecated {
$deleted = $process->delete_webp_files();
if ( is_wp_error( $deleted ) ) {
return new \WP_Error( 'webp_not_deleted', __( 'Previous WebP files could not be deleted.', 'imagify' ) );
return new WP_Error( 'webp_not_deleted', __( 'Previous WebP files could not be deleted.', 'imagify' ) );
}
return true;
@@ -829,83 +828,6 @@ class Imagify_Admin_Ajax_Post extends Imagify_Admin_Ajax_Post_Deprecated {
wp_send_json_success();
}
/**
* Get admin bar profile output.
*
* @since 1.6.11
*/
public function imagify_get_admin_bar_profile_callback() {
imagify_check_nonce( 'imagify-get-admin-bar-profile', 'imagifygetadminbarprofilenonce' );
if ( ! imagify_get_context( 'wp' )->current_user_can( 'manage' ) ) {
imagify_die();
}
$user = new Imagify_User();
$views = Imagify_Views::get_instance();
$unconsumed_quota = $views->get_quota_percent();
$message = '';
if ( $unconsumed_quota <= 20 ) {
$message = '<div class="imagify-error">';
$message .= '<p><i class="dashicons dashicons-warning" aria-hidden="true"></i><strong>' . __( 'Oops, It\'s almost over!', 'imagify' ) . '</strong></p>';
/* translators: %s is a line break. */
$message .= '<p>' . sprintf( __( 'You have almost used all your credit.%sDon\'t forget to upgrade your subscription to continue optimizing your images.', 'imagify' ), '<br/><br/>' ) . '</p>';
$message .= '<p class="center txt-center text-center"><a class="btn imagify-btn-ghost" href="' . esc_url( imagify_get_external_url( 'subscription' ) ) . '" target="_blank">' . __( 'View My Subscription', 'imagify' ) . '</a></p>';
$message .= '</div>';
}
if ( 0 === $unconsumed_quota ) {
$message = '<div class="imagify-error">';
$message .= '<p><i class="dashicons dashicons-warning" aria-hidden="true"></i><strong>' . __( 'Oops, It\'s Over!', 'imagify' ) . '</strong></p>';
$message .= '<p>' . sprintf(
/* translators: 1 is a data quota, 2 is a date. */
__( 'You have consumed all your credit for this month. You will have <strong>%1$s back on %2$s</strong>.', 'imagify' ),
imagify_size_format( $user->quota * pow( 1024, 2 ) ),
date_i18n( get_option( 'date_format' ), strtotime( $user->next_date_update ) )
) . '</p>';
$message .= '<p class="center txt-center text-center"><a class="btn imagify-btn-ghost" href="' . esc_url( imagify_get_external_url( 'subscription' ) ) . '" target="_blank">' . __( 'Upgrade My Subscription', 'imagify' ) . '</a></p>';
$message .= '</div>';
}
// Custom HTML.
$quota_section = '<div class="imagify-admin-bar-quota">';
$quota_section .= '<div class="imagify-abq-row">';
if ( 1 === $user->plan_id ) {
$quota_section .= '<div class="imagify-meteo-icon">' . $views->get_quota_icon() . '</div>';
}
$quota_section .= '<div class="imagify-account">';
$quota_section .= '<p class="imagify-meteo-title">' . __( 'Account status', 'imagify' ) . '</p>';
$quota_section .= '<p class="imagify-meteo-subs">' . __( 'Your subscription:', 'imagify' ) . '&nbsp;<strong class="imagify-user-plan">' . $user->plan_label . '</strong></p>';
$quota_section .= '</div>'; // .imagify-account
$quota_section .= '</div>'; // .imagify-abq-row
if ( 1 === $user->plan_id ) {
$quota_section .= '<div class="imagify-abq-row">';
$quota_section .= '<div class="imagify-space-left">';
/* translators: %s is a data quota. */
$quota_section .= '<p>' . sprintf( __( 'You have %s space credit left', 'imagify' ), '<span class="imagify-unconsumed-percent">' . $unconsumed_quota . '%</span>' ) . '</p>';
$quota_section .= '<div class="' . $views->get_quota_class() . '">';
$quota_section .= '<div style="width: ' . $unconsumed_quota . '%;" class="imagify-unconsumed-bar imagify-progress"></div>';
$quota_section .= '</div>'; // .imagify-bar-{negative|neutral|positive}
$quota_section .= '</div>'; // .imagify-space-left
$quota_section .= '</div>'; // .imagify-abq-row
}
$quota_section .= '<p class="imagify-abq-row">';
$quota_section .= '<a class="imagify-account-link" href="' . esc_url( imagify_get_external_url( 'subscription' ) ) . '" target="_blank">';
$quota_section .= '<span class="dashicons dashicons-admin-users"></span>';
$quota_section .= '<span class="button-text">' . __( 'View my subscription', 'imagify' ) . '</span>';
$quota_section .= '</a>'; // .imagify-account-link
$quota_section .= '</p>'; // .imagify-abq-row
$quota_section .= '</div>'; // .imagify-admin-bar-quota
$quota_section .= $message;
wp_send_json_success( $quota_section );
}
/**
* Get pricings from API for Onetime and Plans at the same time.
*
@@ -1148,7 +1070,7 @@ class Imagify_Admin_Ajax_Post extends Imagify_Admin_Ajax_Post_Deprecated {
imagify_die();
}
$notice = filter_input( INPUT_GET, 'ad', FILTER_SANITIZE_STRING );
$notice = htmlspecialchars( wp_unslash( $_GET['ad'] ) );
if ( ! $notice ) {
imagify_maybe_redirect();
@@ -1215,8 +1137,8 @@ class Imagify_Admin_Ajax_Post extends Imagify_Admin_Ajax_Post_Deprecated {
* @return string
*/
public function get_context( $method = 'GET', $parameter = 'context' ) {
$method = 'POST' === $method ? INPUT_POST : INPUT_GET;
$context = filter_input( $method, $parameter, FILTER_SANITIZE_STRING );
$context = 'POST' === $method ? wp_unslash( $_POST[ $parameter ] ) : wp_unslash( $_GET[ $parameter ] ); // phpcs:ignore WordPress.Security.NonceVerification.Missing, WordPress.Security.NonceVerification.Recommended
$context = htmlspecialchars( $context );
return imagify_sanitize_context( $context );
}
@@ -1248,12 +1170,13 @@ class Imagify_Admin_Ajax_Post extends Imagify_Admin_Ajax_Post_Deprecated {
*
* @param string $method The method used: 'GET' (default), or 'POST'.
* @param string $parameter The name of the parameter to look for.
*
* @return string
*/
public function get_folder_type( $method = 'GET', $parameter = 'folder_type' ) {
$method = 'POST' === $method ? INPUT_POST : INPUT_GET;
$folder_type = 'POST' === $method ? wp_unslash( $_POST[ $parameter ] ) : wp_unslash( $_GET[ $parameter ] ); // phpcs:ignore WordPress.Security.NonceVerification.Missing, WordPress.Security.NonceVerification.Recommended
return filter_input( $method, $parameter, FILTER_SANITIZE_STRING );
return htmlspecialchars( $folder_type );
}
/**
@@ -1263,11 +1186,12 @@ class Imagify_Admin_Ajax_Post extends Imagify_Admin_Ajax_Post_Deprecated {
*
* @param string $method The method used: 'GET' (default), or 'POST'.
* @param string $parameter The name of the parameter to look for.
*
* @return string
*/
public function get_imagify_action( $method = 'GET', $parameter = 'imagify_action' ) {
$method = 'POST' === $method ? INPUT_POST : INPUT_GET;
$action = filter_input( $method, $parameter, FILTER_SANITIZE_STRING );
$action = 'POST' === $method ? wp_unslash( $_POST[ $parameter ] ) : wp_unslash( $_GET[ $parameter ] ); // phpcs:ignore WordPress.Security.NonceVerification.Missing, WordPress.Security.NonceVerification.Recommended
$action = htmlspecialchars( $action );
return $action ? $action : 'optimize';
}

View File

@@ -186,7 +186,8 @@ class Imagify_Assets extends Imagify_Assets_Deprecated {
$this->register_script( 'sweetalert', 'sweetalert2', [ 'promise-polyfill' ], '4.6.6' )->localize( 'imagifySwal' );
$this->register_script( 'chart', 'chart', [], '2.7.1.0' );
$this->register_bud_script( 'runtime', 'runtime' );
$this->register_bud_script( 'chart', 'chart', [ 'runtime' ], '4.4.0' );
$this->register_script( 'event-move', 'jquery.event.move', [ 'jquery' ], '2.0.1' );
@@ -211,7 +212,7 @@ class Imagify_Assets extends Imagify_Assets_Deprecated {
$this->register_script( 'async', 'imagify-gulp' );
$this->register_script( 'bulk', 'bulk', [ 'jquery', 'beat', 'underscore', 'chart', 'sweetalert', 'async', 'admin' ] )->defer_localization( 'imagifyBulk' );
$this->register_bud_script( 'bulk', 'bulk', [ 'jquery', 'beat', 'underscore', 'chart', 'sweetalert', 'async', 'admin' ] )->defer_localization( 'imagifyBulk' );
$this->register_script( 'options', 'options', [ 'jquery', 'beat', 'sweetalert', 'underscore', 'admin' ] )->defer_localization( 'imagifyOptions' );
@@ -459,6 +460,42 @@ class Imagify_Assets extends Imagify_Assets_Deprecated {
return $this;
}
/**
* Register a script.
*
* @since 1.6.10
*
* @param string $handle Name of the script. Should be unique.
* @param string|null $file_name The file name, without the extension. If null, $handle is used.
* @param array $dependencies An array of registered script handles this script depends on.
* @param string|null $version String specifying script version number. If set to null, the plugin version is used. If SCRIPT_DEBUG is true, a random string is used.
* @return object This class instance.
*/
public function register_bud_script( $handle, $file_name = null, $dependencies = [], $version = null ) {
// If we register it, it's one of our scripts.
$this->scripts[ $handle ] = 1;
// Set the current handler and handler type.
$this->current_handle = $handle;
$this->current_handle_type = 'js';
$file_name = $file_name ? $file_name : $handle;
$version = $version ? $version : IMAGIFY_VERSION;
$version = $this->is_debug() ? self::$version : $version;
$extension = '.js';
$handle = self::JS_PREFIX . $handle;
$dependencies = $this->prefix_dependencies( $dependencies );
wp_register_script(
$handle,
IMAGIFY_URL . 'assets/admin/js/' . $file_name . $extension,
$dependencies,
$version,
true
);
return $this;
}
/**
* Enqueue a script.
*

View File

@@ -159,18 +159,20 @@ class Imagify_DB {
* It returns an empty string if the database has no attachments without the required metadada.
* It also triggers Imagify_DB::unlimit_joins().
*
* @since 1.7
* @access public
* @author Grégory Viguier
*
* @param string $id_field An ID field to match the metadata ID against in the JOIN clause.
* Default is the posts table `ID` field, using the `p` alias: `p.ID`.
* In case of "false" value or PEBKAC, fallback to the same field without alias.
* @param bool $matching Set to false to get a query to fetch metas NOT matching the file extensions.
* @param bool $test Test if the site has attachments without required metadata before returning the query. False to bypass the test and get the query anyway.
* @param string $special_join_conditions Special conditions to apply on the join.
*
* @return string
* @author Grégory Viguier
*
* @since 1.7
* @access public
*/
public static function get_required_wp_metadata_join_clause( $id_field = 'p.ID', $matching = true, $test = true ) {
public static function get_required_wp_metadata_join_clause( $id_field = 'p.ID', $matching = true, $test = true, $special_join_conditions = '' ) {
global $wpdb;
if ( $test && ! imagify_has_attachments_without_required_metadata() ) {
@@ -186,7 +188,16 @@ class Imagify_DB {
$join = $matching ? 'INNER' : 'LEFT';
$first = true;
foreach ( self::get_required_wp_metadata_aliases() as $meta_name => $alias ) {
if ( $first ) {
$first = false;
$clause .= "
$join JOIN $wpdb->postmeta AS $alias
ON ( $id_field = $alias.post_id AND $alias.meta_key = '$meta_name' $special_join_conditions )";
continue;
}
$clause .= "
$join JOIN $wpdb->postmeta AS $alias
ON ( $id_field = $alias.post_id AND $alias.meta_key = '$meta_name' )";
@@ -299,6 +310,9 @@ class Imagify_DB {
$extensions = array_keys( imagify_get_mime_types() );
$extensions = implode( '|', $extensions );
$extensions = explode( '|', $extensions );
$extensions = array_map(function ( $ex ) {
return strrev( $ex );
}, $extensions);
}
if ( ! $alias ) {
@@ -312,10 +326,12 @@ class Imagify_DB {
return $prepared ? str_replace( '%', '%%', $query[ $key ] ) : $query[ $key ];
}
$regex = '^' . implode( '\..*|^', $extensions ) . '\..*';
if ( $matching ) {
$query[ $key ] = "AND ( LOWER( $alias.meta_value ) LIKE '%." . implode( "' OR LOWER( $alias.meta_value ) LIKE '%.", $extensions ) . "' )";
$query[ $key ] = "AND REVERSE (LOWER( $alias.meta_value )) REGEXP '$regex'";
} else {
$query[ $key ] = "OR ( LOWER( $alias.meta_value ) NOT LIKE '%." . implode( "' AND LOWER( $alias.meta_value ) NOT LIKE '%.", $extensions ) . "' )";
$query[ $key ] = "AND REVERSE (LOWER( $alias.meta_value )) NOT REGEXP '$regex'";
}
return $prepared ? str_replace( '%', '%%', $query[ $key ] ) : $query[ $key ];

View File

@@ -62,7 +62,7 @@ class Imagify_Files_Iterator extends FilterIterator {
*
* @return bool Returns whether the current element of the iterator is acceptable through this filter.
*/
public function accept() {
public function accept(): bool {
static $extensions, $has_extension_method;
$file_path = $this->current()->getPathname();

View File

@@ -4,9 +4,7 @@ defined( 'ABSPATH' ) || die( 'Cheatin uh?' );
/**
* Class that display the "custom folders" files.
*
* @package Imagify
* @since 1.7
* @author Grégory Viguier
*/
class Imagify_Files_List_Table extends WP_List_Table {
@@ -15,7 +13,6 @@ class Imagify_Files_List_Table extends WP_List_Table {
*
* @var string
* @since 1.7
* @author Grégory Viguier
*/
const VERSION = '1.1';
@@ -24,7 +21,6 @@ class Imagify_Files_List_Table extends WP_List_Table {
*
* @var string
* @since 1.7
* @author Grégory Viguier
*/
const PER_PAGE_OPTION = 'imagify_files_per_page';
@@ -33,25 +29,22 @@ class Imagify_Files_List_Table extends WP_List_Table {
*
* @var array
* @since 1.7
* @author Grégory Viguier
*/
protected $folders = array();
/**
* Filesystem object.
*
* @var object Imagify_Filesystem
* @var Imagify_Filesystem
* @since 1.7.1
* @author Grégory Viguier
*/
protected $filesystem;
/**
* Views object.
*
* @var object Imagify_Views
* @var Imagify_Views
* @since 1.9
* @author Grégory Viguier
*/
protected $views;
@@ -59,7 +52,6 @@ class Imagify_Files_List_Table extends WP_List_Table {
* Constructor.
*
* @since 1.7
* @author Grégory Viguier
*
* @param array $args An associative array of arguments.
*/
@@ -81,7 +73,6 @@ class Imagify_Files_List_Table extends WP_List_Table {
* Prepares the list of items for displaying.
*
* @since 1.7
* @author Grégory Viguier
*/
public function prepare_items() {
global $wpdb;
@@ -108,8 +99,8 @@ class Imagify_Files_List_Table extends WP_List_Table {
$file_ids = array();
$where = '';
$sent_orderby = filter_input( INPUT_GET, 'orderby', FILTER_SANITIZE_STRING );
$sent_order = filter_input( INPUT_GET, 'order', FILTER_SANITIZE_STRING );
$sent_orderby = isset( $_GET['orderby'] ) ? htmlspecialchars( wp_unslash( $_GET['orderby'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Missing, WordPress.Security.NonceVerification.Recommended
$sent_order = isset( $_GET['order'] ) ? htmlspecialchars( wp_unslash( $_GET['order'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Missing, WordPress.Security.NonceVerification.Recommended
$folder_filter = self::get_folder_filter();
$status_filter = self::get_status_filter();
@@ -228,7 +219,6 @@ class Imagify_Files_List_Table extends WP_List_Table {
* Message to be displayed when there are no items.
*
* @since 1.7
* @author Grégory Viguier
*/
public function no_items() {
if ( self::get_status_filter() ) {
@@ -296,7 +286,6 @@ class Imagify_Files_List_Table extends WP_List_Table {
* Display views.
*
* @since 1.7
* @author Grégory Viguier
*/
public function views() {
global $wpdb;
@@ -413,7 +402,6 @@ class Imagify_Files_List_Table extends WP_List_Table {
* Get an associative array ( option_name => option_title ) with the list of bulk actions available on this table.
*
* @since 1.7
* @author Grégory Viguier
*
* @return array
*/
@@ -428,7 +416,6 @@ class Imagify_Files_List_Table extends WP_List_Table {
* 'internal-name' => 'Title'
*
* @since 1.7
* @author Grégory Viguier
*
* @return array
*/
@@ -453,7 +440,6 @@ class Imagify_Files_List_Table extends WP_List_Table {
* The second format will make the initial sorting order be descending.
*
* @since 1.7
* @author Grégory Viguier
*
* @return array
*/
@@ -470,7 +456,6 @@ class Imagify_Files_List_Table extends WP_List_Table {
* Get a column contents.
*
* @since 1.7
* @author Grégory Viguier
*
* @param string $column The column "name": "cb", "title", "optimization_level", etc.
* @param object $item The current item. It must contain at least a $process property.
@@ -490,7 +475,6 @@ class Imagify_Files_List_Table extends WP_List_Table {
* Handles the checkbox column output.
*
* @since 1.7
* @author Grégory Viguier
*
* @param object $item The current item. It must contain at least a $process property.
*/
@@ -506,7 +490,6 @@ class Imagify_Files_List_Table extends WP_List_Table {
* Handles the title column output.
*
* @since 1.7
* @author Grégory Viguier
*
* @param object $item The current item. It must contain at least a $process property.
*/
@@ -556,7 +539,6 @@ class Imagify_Files_List_Table extends WP_List_Table {
* Handles the parent folder column output.
*
* @since 1.7
* @author Grégory Viguier
*
* @param object $item The current item. It must contain at least a $process property.
*/
@@ -591,7 +573,6 @@ class Imagify_Files_List_Table extends WP_List_Table {
* Handles the optimization data column output.
*
* @since 1.7
* @author Grégory Viguier
*
* @param object $item The current item. It must contain at least a $process property.
*/
@@ -643,7 +624,6 @@ class Imagify_Files_List_Table extends WP_List_Table {
* Handles the status column output.
*
* @since 1.7
* @author Grégory Viguier
*
* @param object $item The current item. It must contain at least a $process property.
*/
@@ -678,7 +658,6 @@ class Imagify_Files_List_Table extends WP_List_Table {
* Handles the optimization level column output.
*
* @since 1.7
* @author Grégory Viguier
*
* @param object $item The current item. It must contain at least a $process property.
*/
@@ -694,7 +673,6 @@ class Imagify_Files_List_Table extends WP_List_Table {
* Handles the actions column output.
*
* @since 1.7
* @author Grégory Viguier
*
* @param object $item The current item. It must contain at least a $process property.
*/
@@ -731,7 +709,6 @@ class Imagify_Files_List_Table extends WP_List_Table {
* Prints a button to optimize the file.
*
* @since 1.7
* @author Grégory Viguier
*
* @param object $item The current item. It must contain at least a $process property.
*/
@@ -759,7 +736,6 @@ class Imagify_Files_List_Table extends WP_List_Table {
* Prints a button to retry to optimize the file.
*
* @since 1.7
* @author Grégory Viguier
*
* @param object $item The current item. It must contain at least a $process property.
*/
@@ -790,7 +766,6 @@ class Imagify_Files_List_Table extends WP_List_Table {
* Prints buttons to re-optimize the file to other levels.
*
* @since 1.7
* @author Grégory Viguier
*
* @param object $item The current item. It must contain at least a $process property.
*/
@@ -834,7 +809,6 @@ class Imagify_Files_List_Table extends WP_List_Table {
* Prints a button to generate WebP versions if they are missing.
*
* @since 1.7
* @author Grégory Viguier
*
* @param object $item The current item. It must contain at least a $process property.
*/
@@ -850,7 +824,6 @@ class Imagify_Files_List_Table extends WP_List_Table {
* Prints a button to delete WebP versions when the status is "already_optimized".
*
* @since 1.9.6
* @author Grégory Viguier
*
* @param object $item The current item. It must contain at least a $process property.
*/
@@ -866,7 +839,6 @@ class Imagify_Files_List_Table extends WP_List_Table {
* Prints a button to restore the file.
*
* @since 1.7
* @author Grégory Viguier
*
* @param object $item The current item. It must contain at least a $process property.
*/
@@ -889,7 +861,6 @@ class Imagify_Files_List_Table extends WP_List_Table {
* Prints a button to check if the file has been modified or not.
*
* @since 1.7
* @author Grégory Viguier
*
* @param object $item The current item. It must contain at least a $process property.
*/
@@ -906,7 +877,6 @@ class Imagify_Files_List_Table extends WP_List_Table {
* Prints a button for the comparison tool (before / after optimization).
*
* @since 1.7
* @author Grégory Viguier
*
* @param object $item The current item. It must contain at least a $process property.
*/
@@ -953,7 +923,6 @@ class Imagify_Files_List_Table extends WP_List_Table {
* It may happen if the $item doesn't come from the prepare() method.
*
* @since 1.7
* @author Grégory Viguier
*
* @param object $item The current item. It must contain at least a $process property.
* @return object The current item.
@@ -989,7 +958,6 @@ class Imagify_Files_List_Table extends WP_List_Table {
* Get the name of the default primary column.
*
* @since 1.7
* @author Grégory Viguier
*
* @return string Name of the default primary column, in this case, 'title'.
*/
@@ -1001,7 +969,6 @@ class Imagify_Files_List_Table extends WP_List_Table {
* Get a list of CSS classes for the WP_List_Table table tag.
*
* @since 1.7
* @author Grégory Viguier
*
* @return array List of CSS classes for the table tag.
*/
@@ -1013,7 +980,6 @@ class Imagify_Files_List_Table extends WP_List_Table {
* Allow to save the screen options when submitted by the user.
*
* @since 1.7
* @author Grégory Viguier
*
* @param bool|int $status Screen option value. Default false to skip.
* @param string $option The option name.
@@ -1032,7 +998,6 @@ class Imagify_Files_List_Table extends WP_List_Table {
* Get the requested folder filter.
*
* @since 1.7
* @author Grégory Viguier
*
* @return string
*/
@@ -1051,7 +1016,6 @@ class Imagify_Files_List_Table extends WP_List_Table {
* Get the requested status filter.
*
* @since 1.7
* @author Grégory Viguier
*
* @return string
*/
@@ -1067,7 +1031,7 @@ class Imagify_Files_List_Table extends WP_List_Table {
'unoptimized' => 1,
'errors' => 1,
);
$filter = trim( filter_input( INPUT_GET, 'status-filter', FILTER_SANITIZE_STRING ) );
$filter = isset( $_GET['status-filter'] ) ? trim( htmlspecialchars( wp_unslash( $_GET['status-filter'] ) ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Missing, WordPress.Security.NonceVerification.Recommended
$filter = isset( $values[ $filter ] ) ? $filter : '';
return $filter;

View File

@@ -52,7 +52,7 @@ class Imagify_Files_Recursive_Iterator extends RecursiveFilterIterator {
*
* @return bool Returns whether the current element of the iterator is acceptable through this filter.
*/
public function accept() {
public function accept(): bool {
static $extensions, $has_extension_method;
$file_path = $this->current()->getPathname();

View File

@@ -1,4 +1,7 @@
<?php
use Imagify\User\User;
defined( 'ABSPATH' ) || die( 'Cheatin uh?' );
/**
@@ -303,7 +306,7 @@ class Imagify_Requirements {
return self::$supports['over_quota'];
}
$user = new Imagify_User();
$user = new User();
self::$supports['over_quota'] = $user->get_error() ? false : $user->is_over_quota();

Some files were not shown because too many files have changed in this diff Show More