Plugin Updates

This commit is contained in:
Tony Volpe
2024-03-19 15:33:31 +00:00
parent ff5b56dc44
commit 3a70a6e4bf
317 changed files with 8178 additions and 2933 deletions

View File

@@ -1,2 +1,2 @@
/*! Do not edit, this file is generated automatically - 2024-02-13 23:02:47 EST */ /*! Do not edit, this file is generated automatically - 2024-03-18 16:03:44 EDT */
window.$=window.$||jQuery.noConflict();var 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 i=1;i<o.length;i++){var u=o[i].split("="),d;2===u.length&&(c[u[0]]=u[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&&(r.addClass("dtx-ajax-loaded"),dtx.set(r,t.value))})}})},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").trigger("dtx_init")},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); window.$=window.$||jQuery.noConflict();var 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 i=1;i<o.length;i++){var u=o[i].split("="),d;2===u.length&&(c[u[0]]=u[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&&(r.addClass("dtx-ajax-loaded"),dtx.set(r,t.value))})}})},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").trigger("dtx_init")},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

@@ -1,2 +1,2 @@
/*! Do not edit, this file is generated automatically - 2024-02-13 23:02:47 EST */ /*! Do not edit, this file is generated automatically - 2024-03-18 16:03:44 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); !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,5 +1,16 @@
== Changelog == == Changelog ==
= 4.3.1 =
* Fix: Resolved the PHP warning regarding `Undefined array key "value" in /.../contact-form-7-dynamic-text-extension/contact-form-7-dynamic-text-extension.php on line 391`, [see support thread](https://wordpress.org/support/topic/undefined-array-key-value-2/).
* Fix: Resolved a bug introduced in version 4.2.1 that prevented the mail template validator from recognizing DTX form tags, [see support thread](https://wordpress.org/support/topic/email-field-validation-4/).
* Fix: The `default` attribute for `dynamic_checkbox` can now accept multiple values that are delimited by an underscore (_), making it consistent with [Contact Form 7](https://contactform7.com/checkboxes-radio-buttons-and-menus/#checkbox-radio), [see support thread](https://wordpress.org/support/topic/help-dynamic_checkbox-and-default-values/).
= 4.3.0 =
* Feature: Added the `wpcf7dtx_shortcode` filter to all built-in shortcodes as requested. For usage details, see the [knowledge base](https://aurisecreative.com/docs/contact-form-7-dynamic-text-extension/filter-modify-built-in-shortcode-responses/?utm_source=wordpress.org&utm_medium=link&utm_campaign=contact-form-7-dynamic-text-extension&utm_content=readme).
* Fix: Resolved a bug that prevented using the number zero as the value for select fields, checkboxes, and radio buttons, [see support thread](https://wordpress.org/support/topic/error-with-option-value-0-for-a-dynamic-radio-button/).
= 4.2.3 = = 4.2.3 =
* Fix: Resolved a bug where the `dynamic_select` displayed with a default size of 40 instead of 1. * Fix: Resolved a bug where the `dynamic_select` displayed with a default size of 40 instead of 1.
@@ -21,7 +32,7 @@
= 4.2.0 = = 4.2.0 =
* Security Update: ** Please be sure to review this doc, as you may need to adjust the settings: https://sevenspark.com/docs/contact-form-7-dynamic-text-extension/allow-data-access ** * Security Update: ** Please be sure to review this doc, as you may need to adjust the settings: [Documentation by SevenSpark](https://sevenspark.com/docs/contact-form-7-dynamic-text-extension/allow-data-access), [Documentation by AuRise Creative](https://aurisecreative.com/docs/contact-form-7-dynamic-text-extension/security/) **
* Feature: Added Settings Screen with Allow Lists * Feature: Added Settings Screen with Allow Lists
* Feature: Added Form Scanner * Feature: Added Form Scanner
* Feature: Added Allow List key validation in CF7 Form Validator * Feature: Added Allow List key validation in CF7 Form Validator

View File

@@ -2,41 +2,40 @@
/** /**
* Plugin Name: Contact Form 7 - Dynamic Text Extension * Plugin Name: Contact Form 7 - Dynamic Text Extension
* Plugin URI: https://sevenspark.com/goods/contact-form-7-dynamic-text-extension * Description: Extends Contact Form 7 by adding dynamic form fields that accepts shortcodes to prepopulate form fields with default values and dynamic placeholders.
* 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: 4.3.1
* Version: 4.2.3 * Author: AuRise Creative, SevenSpark
* Author: SevenSpark, AuRise Creative * Author URI: https://aurisecreative.com
* Author URI: https://sevenspark.com * Plugin URI: https://aurisecreative.com/products/wordpress-plugin/contact-form-7-dynamic-text-extension/
* License: GPL2 * License: GPL v3
* License URI: http://www.gnu.org/licenses/gpl-3.0.html
* Requires at least: 5.5 * Requires at least: 5.5
* Requires PHP: 7.4 * Requires PHP: 7.4
* Text Domain: contact-form-7-dynamic-text-extension * Text Domain: contact-form-7-dynamic-text-extension
*
* @copyright Copyright (c) 2010-2024 Chris Mavricos, SevenSpark <https://sevenspark.com>
* @copyright Copyright (c) 2022-2024 Tessa Watkins, AuRise Creative <https://aurisecreative.com>
* @license http://www.gnu.org/licenses/gpl-3.0.html GNU General Public License, version 3 or higher
*
* 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 3 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, see <http://www.gnu.org/licenses/>.
*/ */
/* define('WPCF7DTX_VERSION', '4.3.1'); // Define current version of DTX
Copyright 2010-2024 Chris Mavricos, SevenSpark <https://sevenspark.com> define('WPCF7DTX_MINVERSION', '5.7'); // The minimum version of CF7 required to use mail validator
Copyright 2022-2024 Tessa Watkins, AuRise Creative <https://aurisecreative.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License, version 2, as
published by the Free Software Foundation.
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.
License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
define('WPCF7DTX_VERSION', '4.2.3'); // Define current version of DTX
define('WPCF7DTX_MINVERSION', '5.7'); // The minimum version of CF7 required to use all features
defined('WPCF7DTX_DIR') || define('WPCF7DTX_DIR', __DIR__); // Define root directory defined('WPCF7DTX_DIR') || define('WPCF7DTX_DIR', __DIR__); // Define root directory
defined('WPCF7DTX_FILE') || define('WPCF7DTX_FILE', __FILE__); // Define root file defined('WPCF7DTX_FILE') || define('WPCF7DTX_FILE', __FILE__); // Define root file
define('WPCF7DTX_DATA_ACCESS_KB_URL', 'https://aurisecreative.com/docs/contact-form-7-dynamic-text-extension/security/');
define('WPCF7DTX_DATA_ACCESS_KB_URL', 'https://sevenspark.com/docs/contact-form-7-dynamic-text-extension/allow-data-access');
/** /**
* Determine Dependencies are Met * Determine Dependencies are Met
@@ -310,13 +309,14 @@ function wpcf7dtx_shortcode_handler($tag)
//Reverse engineer what JS did (converted quotes to HTML entities --> URL encode) then sanitize //Reverse engineer what JS did (converted quotes to HTML entities --> URL encode) then sanitize
$placeholder = html_entity_decode(urldecode($tag->get_option('placeholder', '', true)), ENT_QUOTES); $placeholder = html_entity_decode(urldecode($tag->get_option('placeholder', '', true)), ENT_QUOTES);
if ($placeholder) { if ($placeholder) {
//If a different placeholder text has been specified, set both attributes // If a different placeholder text has been specified, set both attributes
$placeholder = wpcf7dtx_get_dynamic($placeholder, false, $sanitize_type); $placeholder = wpcf7dtx_get_dynamic($placeholder, false, $sanitize_type);
$atts['placeholder'] = $placeholder; $atts['placeholder'] = $placeholder;
$atts['value'] = $value; $atts['value'] = $value;
} else { } else {
//Default behavior of using the value as the placeholder // Default behavior of using the value as the placeholder
$atts['placeholder'] = $value; $atts['placeholder'] = $value;
$atts['value'] = '';
} }
} else { } else {
$atts['value'] = $value; $atts['value'] = $value;
@@ -352,7 +352,9 @@ function wpcf7dtx_shortcode_handler($tag)
foreach ($pipes as $pipe) { foreach ($pipes as $pipe) {
$key = trim(strval($pipe[0])); $key = trim(strval($pipe[0]));
$value = trim(strval($pipe[1])); $value = trim(strval($pipe[1]));
if ($key && $value) { $valid_key = is_numeric($key) || (is_string($key) && !empty($key)); // Allow falsey numbers but not booleans or strings
$valid_value = is_numeric($value) || (is_string($value) && !empty($value)); // Allow falsey numbers but not booleans or strings
if ($valid_key && $valid_value) {
$options[$key] = $value; $options[$key] = $value;
} }
} }

View File

@@ -45,13 +45,23 @@ add_action('init', 'wpcf7dtx_init_shortcodes'); //Add init hook to add shortcode
*/ */
function wpcf7dtx_get($atts = array()) function wpcf7dtx_get($atts = array())
{ {
extract(shortcode_atts(array( $atts = shortcode_atts(array(
'key' => 0, 'key' => 0,
'default' => '', 'default' => '',
'obfuscate' => '' 'obfuscate' => ''
), array_change_key_case((array)$atts, CASE_LOWER))); ), array_change_key_case((array)$atts, CASE_LOWER));
$value = apply_filters('wpcf7dtx_sanitize', wpcf7dtx_array_has_key($key, $_GET, $default)); $raw = wpcf7dtx_array_has_key($atts['key'], $_GET, $atts['default']);
return apply_filters('wpcf7dtx_escape', $value, $obfuscate); return apply_filters(
'wpcf7dtx_shortcode', // DTX built-in shortcode hook
apply_filters(
'wpcf7dtx_escape',
apply_filters('wpcf7dtx_sanitize', $raw),
$atts['obfuscate']
), // Sanitized & escaped value to output
$raw, // Raw value
'GET', // Shortcode tag
$atts // Shortcode attributes
);
} }
/** /**
@@ -65,13 +75,22 @@ function wpcf7dtx_get($atts = array())
*/ */
function wpcf7dtx_post($atts = array()) function wpcf7dtx_post($atts = array())
{ {
extract(shortcode_atts(array( $atts = shortcode_atts(array(
'key' => '', 'key' => '',
'default' => '', 'default' => '',
'obfuscate' => '' 'obfuscate' => ''
), array_change_key_case((array)$atts, CASE_LOWER))); ), array_change_key_case((array)$atts, CASE_LOWER));
$value = apply_filters('wpcf7dtx_sanitize', wpcf7dtx_array_has_key($key, $_POST, $default)); $raw = wpcf7dtx_array_has_key($atts['key'], $_POST, $atts['default']);
return apply_filters('wpcf7dtx_escape', $value, $obfuscate); return apply_filters(
'wpcf7dtx_shortcode', // DTX built-in shortcode hook
apply_filters('wpcf7dtx_escape', apply_filters(
'wpcf7dtx_sanitize',
apply_filters('wpcf7dtx_sanitize', $raw)
), $atts['obfuscate']), // Sanitized & escaped value to output
$raw, // Raw value
'POST', // Shortcode tag
$atts // Shortcode attributes
);
} }
/** /**
@@ -85,12 +104,13 @@ function wpcf7dtx_post($atts = array())
*/ */
function wpcf7dtx_url($atts = array()) function wpcf7dtx_url($atts = array())
{ {
extract(shortcode_atts(array( $atts = shortcode_atts(array(
'allowed_protocols' => '', 'allowed_protocols' => '',
'part' => '', 'part' => '',
'obfuscate' => '' 'obfuscate' => ''
), array_change_key_case((array)$atts, CASE_LOWER))); ), array_change_key_case((array)$atts, CASE_LOWER));
$allowed_protocols = explode(',', sanitize_text_field($allowed_protocols)); $atts['allowed_protocols'] = explode(',', sanitize_text_field($atts['allowed_protocols']));
extract($atts);
// Get the absolute URL // Get the absolute URL
if (is_multisite() && !is_subdomain_install()) { if (is_multisite() && !is_subdomain_install()) {
@@ -110,12 +130,29 @@ function wpcf7dtx_url($atts = array())
]; ];
$value = ''; $value = '';
if (array_key_exists($part, $part_constant_map)) { if (array_key_exists($part, $part_constant_map)) {
$value = apply_filters('wpcf7dtx_sanitize', strval(wp_parse_url($url, $part_constant_map[$part])), 'text'); $value = strval(wp_parse_url($url, $part_constant_map[$part]));
} }
return apply_filters('wpcf7dtx_escape', $value, $obfuscate, 'text'); return apply_filters(
'wpcf7dtx_shortcode', // DTX built-in shortcode hook
apply_filters(
'wpcf7dtx_escape',
apply_filters('wpcf7dtx_sanitize', $value, 'text'),
$obfuscate,
'text'
), // Sanitized & escaped value to output
$value, // Raw value
'URL', // Shortcode tag
$atts // Shortcode attributes
);
} }
// No part requested, return the absolute URL // No part requested, return the absolute URL
return apply_filters('wpcf7dtx_escape', $url, $obfuscate, 'url', $allowed_protocols); return apply_filters(
'wpcf7dtx_shortcode', // DTX built-in shortcode hook
apply_filters('wpcf7dtx_escape', $url, $obfuscate, 'url', $allowed_protocols), // Sanitized & escaped value to output
$url, // Raw value
'URL', // Shortcode tag
$atts // Shortcode attributes
);
} }
/** /**
@@ -129,15 +166,23 @@ function wpcf7dtx_url($atts = array())
*/ */
function wpcf7dtx_referrer($atts = array()) function wpcf7dtx_referrer($atts = array())
{ {
extract(shortcode_atts(array( $atts = shortcode_atts(array(
'allowed_protocols' => '', 'allowed_protocols' => '',
'obfuscate' => '' 'obfuscate' => ''
), array_change_key_case((array)$atts, CASE_LOWER))); ), array_change_key_case((array)$atts, CASE_LOWER));
if ($value = wpcf7dtx_array_has_key('HTTP_REFERER', $_SERVER)) { $url = wpcf7dtx_array_has_key('HTTP_REFERER', $_SERVER);
$value = apply_filters('wpcf7dtx_sanitize', $value, 'url', $allowed_protocols); return apply_filters(
return apply_filters('wpcf7dtx_escape', $value, $obfuscate, 'url'); 'wpcf7dtx_shortcode', // DTX built-in shortcode hook
} apply_filters('wpcf7dtx_escape', apply_filters(
return ''; 'wpcf7dtx_sanitize',
$url,
'url',
$atts['allowed_protocols']
), $atts['obfuscate'], 'url'), // Sanitized & escaped value to output
$url, // Raw value
'referrer', // Shortcode tag
$atts // Shortcode attributes
);
} }
/** /**
@@ -151,13 +196,21 @@ function wpcf7dtx_referrer($atts = array())
*/ */
function wpcf7dtx_bloginfo($atts = array()) function wpcf7dtx_bloginfo($atts = array())
{ {
extract(shortcode_atts(array( $atts = shortcode_atts(array(
'show' => 'name', //Backwards compatibility 'show' => 'name', //Backwards compatibility
'key' => 'name', 'key' => 'name',
'obfuscate' => '' 'obfuscate' => ''
), array_change_key_case((array)$atts, CASE_LOWER))); ), array_change_key_case((array)$atts, CASE_LOWER));
extract($atts);
$key = $show != $key && $show != 'name' ? $show : $key; // Use old value of "show" if not set to default value $key = $show != $key && $show != 'name' ? $show : $key; // Use old value of "show" if not set to default value
return apply_filters('wpcf7dtx_escape', get_bloginfo($key), $obfuscate); $raw = get_bloginfo($key);
return apply_filters(
'wpcf7dtx_shortcode', // DTX built-in shortcode hook
apply_filters('wpcf7dtx_escape', $raw, $obfuscate), // Sanitized & escaped value to output
$raw, // Raw value
'bloginfo', // Shortcode tag
$atts // Shortcode attributes
);
} }
/** /**
@@ -171,12 +224,12 @@ function wpcf7dtx_bloginfo($atts = array())
*/ */
function wpcf7dtx_get_post_var($atts = array()) function wpcf7dtx_get_post_var($atts = array())
{ {
extract(shortcode_atts(array( $atts = shortcode_atts(array(
'key' => 'post_title', 'key' => 'post_title',
'post_id' => '', 'post_id' => '',
'obfuscate' => '' 'obfuscate' => ''
), array_change_key_case((array)$atts, CASE_LOWER))); ), array_change_key_case((array)$atts, CASE_LOWER));
$key = strtolower(apply_filters('wpcf7dtx_sanitize', $key)); $key = strtolower(apply_filters('wpcf7dtx_sanitize', $atts['key']));
switch ($key) { switch ($key) {
case 'acf_id': // If requesting the handle for ACF, return the post ID case 'acf_id': // If requesting the handle for ACF, return the post ID
case 'id': case 'id':
@@ -191,11 +244,15 @@ function wpcf7dtx_get_post_var($atts = array())
default: default:
break; break;
} }
$post_id = wpcf7dtx_get_post_id($post_id); $atts['post_id'] = wpcf7dtx_get_post_id($atts['post_id']);
if ($post_id) { $raw = $atts['post_id'] ? get_post_field($key, $atts['post_id']) : '';
return apply_filters('wpcf7dtx_escape', get_post_field($key, $post_id), $obfuscate); return apply_filters(
} 'wpcf7dtx_shortcode', // DTX built-in shortcode hook
return ''; apply_filters('wpcf7dtx_escape', $raw, $atts['obfuscate']), // Sanitized & escaped value to output
$raw, // Raw value
'get_post_var', // Shortcode tag
$atts // Shortcode attributes
);
} }
/** /**
@@ -209,25 +266,29 @@ function wpcf7dtx_get_post_var($atts = array())
*/ */
function wpcf7dtx_get_custom_field($atts = array()) function wpcf7dtx_get_custom_field($atts = array())
{ {
extract(shortcode_atts(array( $atts = shortcode_atts(array(
'key' => '', 'key' => '',
'post_id' => '', 'post_id' => '',
'obfuscate' => '' 'obfuscate' => ''
), array_change_key_case((array)$atts, CASE_LOWER))); ), array_change_key_case((array)$atts, CASE_LOWER));
// If this key can't be accessed // If this key can't be accessed
if (!wpcf7dtx_post_meta_key_access_is_allowed($key)) { if (!wpcf7dtx_post_meta_key_access_is_allowed($atts['key'])) {
// Trigger a warning if a denied key is in use // Trigger a warning if a denied key is in use
wpcf7dtx_access_denied_alert($key, 'post_meta'); wpcf7dtx_access_denied_alert($atts['key'], 'post_meta');
return ''; return '';
} }
$post_id = wpcf7dtx_get_post_id($post_id); $key = apply_filters('wpcf7dtx_sanitize', $atts['key'], 'text');
$key = apply_filters('wpcf7dtx_sanitize', $key, 'text'); $atts['post_id'] = wpcf7dtx_get_post_id($atts['post_id']);
if ($post_id && $key) { $raw = $atts['post_id'] && $key ? get_post_meta($atts['post_id'], $key, true) : '';
return apply_filters('wpcf7dtx_escape', get_post_meta($post_id, $key, true), $obfuscate); return apply_filters(
} 'wpcf7dtx_shortcode', // DTX built-in shortcode hook
return ''; apply_filters('wpcf7dtx_escape', $raw, $atts['obfuscate']), // Sanitized & escaped value to output
$raw, // Raw value
'get_custom_field', // Shortcode tag
$atts // Shortcode attributes
);
} }
/** /**
@@ -243,12 +304,15 @@ function wpcf7dtx_get_custom_field($atts = array())
*/ */
function wpcf7dtx_get_current_var($atts = array()) function wpcf7dtx_get_current_var($atts = array())
{ {
extract(shortcode_atts(array( $atts = shortcode_atts(array(
'key' => 'title', 'key' => 'title',
'obfuscate' => '' 'obfuscate' => ''
), array_change_key_case((array)$atts, CASE_LOWER))); ), array_change_key_case((array)$atts, CASE_LOWER));
extract($atts);
$key = apply_filters('wpcf7dtx_sanitize', $key); $key = apply_filters('wpcf7dtx_sanitize', $key);
$temp_key = str_replace('-', '_', sanitize_key($key)); $temp_key = str_replace('-', '_', sanitize_key($key));
$raw = '';
$value = '';
if ($temp_key === 'url') { if ($temp_key === 'url') {
return wpcf7dtx_url($atts); // Getting the current URL is the same for all WordPress pages return wpcf7dtx_url($atts); // Getting the current URL is the same for all WordPress pages
} elseif (!empty($key)) { } elseif (!empty($key)) {
@@ -272,16 +336,26 @@ function wpcf7dtx_get_current_var($atts = array())
case 'user': // This is an author page case 'user': // This is an author page
switch ($temp_key) { switch ($temp_key) {
case 'acf_id': // Get handle for Advanced Custom Fields case 'acf_id': // Get handle for Advanced Custom Fields
return apply_filters('wpcf7dtx_escape', 'user_' . $obj->ID, $obfuscate); $raw = 'user_' . $obj->ID;
$value = apply_filters('wpcf7dtx_escape', $raw, $obfuscate);
break;
case 'image': case 'image':
case 'featured_image': // Get the profile picture of the user being displayed on the page case 'featured_image': // Get the profile picture of the user being displayed on the page
return apply_filters('wpcf7dtx_escape', get_avatar_url($obj->ID), $obfuscate, 'url'); $raw = get_avatar_url($obj->ID);
$value = apply_filters('wpcf7dtx_escape', $raw, $obfuscate, 'url');
break;
case 'title': // Get author's display name case 'title': // Get author's display name
return apply_filters('wpcf7dtx_escape', $obj->display_name, $obfuscate); $raw = $obj->display_name;
$value = apply_filters('wpcf7dtx_escape', $raw, $obfuscate);
break;
case 'slug': // Not all author pages use the `user_login` variable for security reasons, so get what is currently displayed as slug case 'slug': // Not all author pages use the `user_login` variable for security reasons, so get what is currently displayed as slug
return apply_filters('wpcf7dtx_escape', basename(wpcf7dtx_url(array('part' => 'path'))), $obfuscate); $raw = basename(wpcf7dtx_url(array('part' => 'path')));
$value = apply_filters('wpcf7dtx_escape', $raw, $obfuscate);
break;
default: // Get user value by key should it exist default: // Get user value by key should it exist
return apply_filters('wpcf7dtx_escape', $obj->get($key), $obfuscate); $raw = $obj->get($key);
$value = apply_filters('wpcf7dtx_escape', $raw, $obfuscate);
break;
} }
case 'post': // This is a post object case 'post': // This is a post object
switch ($temp_key) { switch ($temp_key) {
@@ -302,35 +376,53 @@ function wpcf7dtx_get_current_var($atts = array())
case 'term': // This is a taxonomy with a term ID case 'term': // This is a taxonomy with a term ID
switch ($key) { switch ($key) {
case 'id': // Get term ID case 'id': // Get term ID
return apply_filters('wpcf7dtx_escape', $obj->term_id, $obfuscate); $raw = $obj->term_id;
$value = apply_filters('wpcf7dtx_escape', $raw, $obfuscate);
break;
case 'acf_id': // Get handle for Advanced Custom Fields case 'acf_id': // Get handle for Advanced Custom Fields
return apply_filters('wpcf7dtx_escape', $obj->taxonomy . '_' . $obj->term_id, $obfuscate); $raw = $obj->taxonomy . '_' . $obj->term_id;
$value = apply_filters('wpcf7dtx_escape', $raw, $obfuscate);
break;
case 'title': // Get term name case 'title': // Get term name
return apply_filters('wpcf7dtx_escape', $obj->name, $obfuscate); $raw = $obj->name;
$value = apply_filters('wpcf7dtx_escape', $raw, $obfuscate);
break;
default: default:
if (property_exists($obj, $key)) { if (property_exists($obj, $key)) {
// Get any property if it exists // Get any property if it exists
return apply_filters('wpcf7dtx_escape', $obj->{$key}, $obfuscate); $raw = $obj->{$key};
} else {
// Otherwise, try meta data if the property doesn't exist
$raw = get_metadata('term', $obj->ID, $key, true);
} }
// Otherwise, try meta data if the property doesn't exist $value = apply_filters('wpcf7dtx_escape', $raw, $obfuscate);
return apply_filters('wpcf7dtx_escape', get_metadata('term', $obj->ID, $key, true), $obfuscate); break;
} }
case 'archive': // Possibly a date or formats archive case 'archive': // Possibly a date or formats archive
switch ($temp_key) { switch ($temp_key) {
case 'title': // Get archive title case 'title': // Get archive title
return apply_filters('wpcf7dtx_escape', get_the_archive_title(), $obfuscate); $raw = get_the_archive_title();
$value = apply_filters('wpcf7dtx_escape', $raw, $obfuscate);
break;
default: default:
break; break;
} }
default: // Possibly a search or 404 page at this point default: // Possibly a search or 404 page at this point
if ($temp_key == 'slug') { if ($temp_key == 'slug') {
// no idea what else to get except the slug maybe // no idea what else to get except the slug maybe
return apply_filters('wpcf7dtx_escape', basename(wpcf7dtx_url(array('part' => 'path'))), $obfuscate); $raw = basename(wpcf7dtx_url(array('part' => 'path')));
$value = apply_filters('wpcf7dtx_escape', $raw, $obfuscate);
} }
break; break;
} }
} }
return ''; return apply_filters(
'wpcf7dtx_shortcode', // DTX built-in shortcode hook
$value, // Sanitized & escaped value to output
$raw, // Raw value
'get_current_var', // Shortcode tag
$atts // Shortcode attributes
);
} }
/** /**
@@ -346,23 +438,29 @@ function wpcf7dtx_get_current_var($atts = array())
*/ */
function wpcf7dtx_get_current_user($atts = array()) function wpcf7dtx_get_current_user($atts = array())
{ {
extract(shortcode_atts(array( $atts = shortcode_atts(array(
'key' => 'user_login', 'key' => 'user_login',
'obfuscate' => '' 'obfuscate' => ''
), array_change_key_case((array)$atts, CASE_LOWER))); ), array_change_key_case((array)$atts, CASE_LOWER));
$raw = '';
if (is_user_logged_in()) { if (is_user_logged_in()) {
// If this key can't be accessed // If this key can't be accessed
if (!wpcf7dtx_user_data_access_is_allowed($key)) { if (!wpcf7dtx_user_data_access_is_allowed($atts['key'])) {
// Trigger a warning if a denied key is in use // Trigger a warning if a denied key is in use
wpcf7dtx_access_denied_alert($key, 'user_data'); wpcf7dtx_access_denied_alert($atts['key'], 'user_data');
return ''; return '';
} }
$user = wp_get_current_user(); $raw = wp_get_current_user()->get($atts['key']);
return apply_filters('wpcf7dtx_escape', $user->get($key), $obfuscate);
} }
return ''; return apply_filters(
'wpcf7dtx_shortcode', // DTX built-in shortcode hook
apply_filters('wpcf7dtx_escape', $raw, $atts['obfuscate']), // Sanitized & escaped value to output
$raw, // Raw value
'get_current_user', // Shortcode tag
$atts // Shortcode attributes
);
} }
/** /**
@@ -380,34 +478,45 @@ function wpcf7dtx_get_current_user($atts = array())
*/ */
function wpcf7dtx_get_attachment($atts = array()) function wpcf7dtx_get_attachment($atts = array())
{ {
extract(shortcode_atts(array( $atts = shortcode_atts(array(
'id' => '', //Get attachment by ID 'id' => '', // Get attachment by ID
'size' => 'full', //Define attachment size 'size' => 'full', // Define attachment size
'post_id' => '', //If attachment ID is empty but post ID is not, get the featured image 'post_id' => '', // If attachment ID is empty but post ID is not, get the featured image
'return' => 'url', //Options are `id` or `url` 'return' => 'url', // Options are `id` or `url`
'obfuscate' => '' 'obfuscate' => ''
), array_change_key_case((array)$atts, CASE_LOWER))); ), array_change_key_case((array)$atts, CASE_LOWER));
//No attachment ID was provided, check for post ID to get it's featured image // No attachment ID was provided, check for post ID to get it's featured image
if (empty($id)) { if (empty($atts['id'])) {
if ($post_id = wpcf7dtx_get_post_id($post_id)) { if ($atts['post_id'] = wpcf7dtx_get_post_id($atts['post_id'])) {
//If a post ID was provided, get it's featured image //If a post ID was provided, get it's featured image
$id = get_post_thumbnail_id($post_id); $atts['id'] = get_post_thumbnail_id($atts['post_id']);
} }
} }
//Get the value //Get the value
if ($id) { $value = '';
$id = intval(sanitize_text_field(strval($id))); $raw = '';
switch ($return) { if ($atts['id']) {
$atts['id'] = intval(sanitize_text_field(strval($atts['id'])));
switch ($atts['return']) {
case 'id': //Return the attachment ID case 'id': //Return the attachment ID
return apply_filters('wpcf7dtx_escape', $id, $obfuscate); $raw = $atts['id'];
$value = apply_filters('wpcf7dtx_escape', $raw, $atts['obfuscate']);
break;
default: //Return attachment URL default: //Return attachment URL
$url = wp_get_attachment_image_url(intval($id), sanitize_text_field(strval($size))); $raw = wp_get_attachment_image_url(intval($atts['id']), sanitize_text_field(strval($atts['size'])));
return apply_filters('wpcf7dtx_escape', $url, $obfuscate, 'url'); $value = apply_filters('wpcf7dtx_escape', $raw, $atts['obfuscate'], 'url');
break;
} }
} }
return ''; return apply_filters(
'wpcf7dtx_shortcode', // DTX built-in shortcode hook
$value, // Sanitized & escaped value to output
$raw, // Raw value
'get_attachment', // Shortcode tag
$atts // Shortcode attributes
);
} }
/** /**
@@ -425,14 +534,19 @@ function wpcf7dtx_get_attachment($atts = array())
*/ */
function wpcf7dtx_get_cookie($atts = array()) function wpcf7dtx_get_cookie($atts = array())
{ {
extract(shortcode_atts(array( $atts = shortcode_atts(array(
'key' => '', 'key' => '',
'default' => '', 'default' => '',
'obfuscate' => '' // Optionally obfuscate returned value 'obfuscate' => '' // Optionally obfuscate returned value
), array_change_key_case((array)$atts, CASE_LOWER))); ), array_change_key_case((array)$atts, CASE_LOWER));
$key = apply_filters('wpcf7dtx_sanitize', $key); $raw = wpcf7dtx_array_has_key(apply_filters('wpcf7dtx_sanitize', $atts['key']), $_COOKIE, $atts['default']);
$value = wpcf7dtx_array_has_key($key, $_COOKIE, $default); return apply_filters(
return apply_filters('wpcf7dtx_escape', $value, $obfuscate); 'wpcf7dtx_shortcode', // DTX built-in shortcode hook
apply_filters('wpcf7dtx_escape', $raw, $atts['obfuscate']), // Sanitized & escaped value to output
$raw, // Raw value
'get_cookie', // Shortcode tag
$atts // Shortcode attributes
);
} }
/** /**
@@ -451,25 +565,34 @@ function wpcf7dtx_get_cookie($atts = array())
*/ */
function wpcf7dtx_get_taxonomy($atts = array()) function wpcf7dtx_get_taxonomy($atts = array())
{ {
extract(shortcode_atts(array( $atts = shortcode_atts(array(
'post_id' => '', 'post_id' => '',
'taxonomy' => 'category', // Default taxonomy is `category` 'taxonomy' => 'category', // Default taxonomy is `category`
'fields' => 'names', // Return an array of term names 'fields' => 'names', // Return an array of term names
'obfuscate' => '' // Optionally obfuscate returned value 'obfuscate' => '' // Optionally obfuscate returned value
), array_change_key_case((array)$atts, CASE_LOWER))); ), array_change_key_case((array)$atts, CASE_LOWER));
$post_id = wpcf7dtx_get_post_id($post_id); $atts['post_id'] = wpcf7dtx_get_post_id($atts['post_id']);
$fields = apply_filters('wpcf7dtx_sanitize', $fields, 'key'); $fields = apply_filters('wpcf7dtx_sanitize', $atts['fields'], 'key');
if ($post_id && in_array($fields, array('names', 'slugs', 'ids'))) { $raw = '';
$value = '';
if ($atts['post_id'] && in_array($fields, array('names', 'slugs', 'ids'))) {
$terms = wp_get_object_terms( $terms = wp_get_object_terms(
$post_id, // Get only the ones assigned to this post $atts['post_id'], // Get only the ones assigned to this post
apply_filters('wpcf7dtx_sanitize', $taxonomy, 'slug'), apply_filters('wpcf7dtx_sanitize', $atts['taxonomy'], 'slug'),
array('fields' => $fields) array('fields' => $fields)
); );
if (is_array($terms) && count($values = array_values($terms)) && (is_string($values[0]) || is_numeric($values[0]))) { if (is_array($terms) && count($raw = array_values($terms)) && (is_string($raw[0]) || is_numeric($raw[0]))) {
return apply_filters('wpcf7dtx_escape', implode(', ', $values), $obfuscate, 'text'); //return apply_filters('wpcf7dtx_escape', implode(', ', $values), $obfuscate, 'text');
$value = implode(', ', $raw);
} }
} }
return ''; return apply_filters(
'wpcf7dtx_shortcode', // DTX built-in shortcode hook
apply_filters('wpcf7dtx_escape', $value, $atts['obfuscate'], 'text'), // Sanitized & escaped value to output
$raw, // Raw value
'get_taxonomy', // Shortcode tag
$atts // Shortcode attributes
);
} }
/** /**
@@ -488,16 +611,23 @@ function wpcf7dtx_get_taxonomy($atts = array())
*/ */
function wpcf7dtx_get_theme_option($atts = array()) function wpcf7dtx_get_theme_option($atts = array())
{ {
extract(shortcode_atts(array( $atts = shortcode_atts(array(
'key' => '', 'key' => '',
'default' => '', // Optional default value 'default' => '', // Optional default value
'obfuscate' => '' // Optionally obfuscate returned value 'obfuscate' => '' // Optionally obfuscate returned value
), array_change_key_case((array)$atts, CASE_LOWER))); ), array_change_key_case((array)$atts, CASE_LOWER));
if ($key = apply_filters('wpcf7dtx_sanitize', $key, 'text')) { $default = apply_filters('wpcf7dtx_sanitize', $atts['default']);
$default = apply_filters('wpcf7dtx_sanitize', $default); $raw = $default;
return apply_filters('wpcf7dtx_escape', get_theme_mod($key, $default), $obfuscate); if ($key = apply_filters('wpcf7dtx_sanitize', $atts['key'], 'text')) {
$raw = get_theme_mod($key, $default);
} }
return ''; return apply_filters(
'wpcf7dtx_shortcode', // DTX built-in shortcode hook
apply_filters('wpcf7dtx_escape', $raw, $atts['obfuscate']), // Sanitized & escaped value to output
$raw, // Raw value
'get_theme_option', // Shortcode tag
$atts // Shortcode attributes
);
} }
/** /**
@@ -513,8 +643,16 @@ function wpcf7dtx_get_theme_option($atts = array())
*/ */
function wpcf7dtx_guid() function wpcf7dtx_guid()
{ {
if (function_exists('com_create_guid') === true) { if (function_exists('com_create_guid')) {
return esc_attr(trim(com_create_guid(), '{}')); $raw = trim(com_create_guid(), '{}');
} else {
$raw = sprintf('%04X%04X-%04X-%04X-%04X-%04X%04X%04X', mt_rand(0, 65535), mt_rand(0, 65535), mt_rand(0, 65535), mt_rand(16384, 20479), mt_rand(32768, 49151), mt_rand(0, 65535), mt_rand(0, 65535), mt_rand(0, 65535));
} }
return esc_attr(sprintf('%04X%04X-%04X-%04X-%04X-%04X%04X%04X', mt_rand(0, 65535), mt_rand(0, 65535), mt_rand(0, 65535), mt_rand(16384, 20479), mt_rand(32768, 49151), mt_rand(0, 65535), mt_rand(0, 65535), mt_rand(0, 65535))); return apply_filters(
'wpcf7dtx_shortcode', // DTX built-in shortcode hook
esc_attr($raw), // Sanitized & escaped value to output
$raw, // Raw value
'guid', // Shortcode tag
array() // Shortcode attributes
);
} }

View File

@@ -350,7 +350,7 @@ function wpcf7dtx_format_atts($atts)
if ($value) { if ($value) {
$sanitized_atts[$key] = $key; $sanitized_atts[$key] = $key;
} }
} elseif ($value && (is_string($value) || is_numeric($value))) { } elseif (is_numeric($value) || (is_string($value) || !empty($value))) {
$sanitized_atts[$key] = $value; $sanitized_atts[$key] = $value;
} }
} }
@@ -396,7 +396,14 @@ function wpcf7dtx_checkbox_html($atts, $label_text = '', $label_ui = true, $reve
{ {
// Default field attributes // Default field attributes
$atts = array_merge(array('value' => '', 'dtx-default' => ''), array_change_key_case((array)$atts, CASE_LOWER)); $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']) {
// Checkboxes can have multiple values checked, check mine if it's listed as a default value
if ($atts['type'] == 'checkbox' && is_string($atts['dtx-default']) && strpos($atts['dtx-default'], '_') !== false) {
$default = array_unique(explode('_', $atts['dtx-default']));
if (in_array($atts['value'], $default)) {
$atts['checked'] = 'checked';
}
} elseif ((is_numeric($atts['dtx-default']) || $atts['dtx-default']) && $atts['value'] == $atts['dtx-default']) {
$atts['checked'] = 'checked'; $atts['checked'] = 'checked';
} }
$input = wpcf7dtx_input_html($atts); $input = wpcf7dtx_input_html($atts);
@@ -463,7 +470,7 @@ function wpcf7dtx_checkbox_group_html($atts, $options, $label_ui = false, $rever
)); ));
$dynamic_value = ''; $dynamic_value = '';
$dynamic_label = $label; $dynamic_label = $label;
if ($value && $label && $value === $label) { if (is_string($value) && !empty($value) && $value === $label) {
// These are identical, just handle it as one, could also be a raw shortcode // 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 $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 (is_string($dynamic_option) && !empty($dynamic_option) && strpos($dynamic_option, '{') === 0 && strpos($dynamic_option, '}') === strlen($dynamic_option) - 1) {
@@ -513,7 +520,8 @@ function wpcf7dtx_checkbox_group_html($atts, $options, $label_ui = false, $rever
if ($exclusive) { if ($exclusive) {
$class[] = 'wpcf7-exclusive-checkbox'; $class[] = 'wpcf7-exclusive-checkbox';
} }
if ($dynamic_value && $atts['dtx-default'] && $dynamic_value == $atts['dtx-default']) { $valid_default = is_numeric($atts['dtx-default']) || (is_string($atts['dtx-default']) && !empty($atts['dtx-default']));
if ($valid_default && $dynamic_value == $atts['dtx-default']) {
$my_atts['checked'] = 'checked'; $my_atts['checked'] = 'checked';
} }
$group_html[] = sprintf( $group_html[] = sprintf(

View File

@@ -98,7 +98,6 @@ function wpcf7dtx_validation_filter($result, $tag)
return wpcf7dtx_validate_value($result, $user_value, $tag, $type); return wpcf7dtx_validate_value($result, $user_value, $tag, $type);
} }
/** /**
* Validate Single Value * Validate Single Value
* *
@@ -171,137 +170,132 @@ function wpcf7dtx_validate_value($result, $value, $tag, $type = '')
} }
/** /**
* Validator Requires Contact Form 7 Minimum Version * Backend Mail Configuration Validation
*
* Validate dynamic form tags used in mail configuration.
*
* @since 4.0.0
*
* @param WPCF7_ConfigValidator
*
* @return void
*/ */
if (wpcf7dtx_dependencies()) { function wpcf7dtx_validate($validator)
/** {
* Backend Mail Configuration Validation // Check for sensitive form tags
* $manager = WPCF7_FormTagsManager::get_instance();
* Validate dynamic form tags used in mail configuration. $contact_form = $validator->contact_form();
* $form = $contact_form->prop('form');
* @since 4.0.0 if (wpcf7_autop_or_not()) {
* $form = $manager->replace_with_placeholders($form);
* @param WPCF7_ConfigValidator $form = wpcf7_autop($form);
* $form = $manager->restore_from_placeholders($form);
* @return void }
*/ $form = $manager->replace_all($form);
function wpcf7dtx_validate($validator) $tags = $manager->get_scanned_tags();
{ foreach ($tags as $tag) {
// Check for sensitive form tags /** @var WPCF7_FormTag $tag */
$manager = WPCF7_FormTagsManager::get_instance();
$contact_form = $validator->contact_form();
$form = $contact_form->prop('form');
if (wpcf7_autop_or_not()) {
$form = $manager->replace_with_placeholders($form);
$form = wpcf7_autop($form);
$form = $manager->restore_from_placeholders($form);
}
$form = $manager->replace_all($form);
$tags = $manager->get_scanned_tags();
foreach ($tags as $tag) {
/** @var WPCF7_FormTag $tag */
// Only validate DTX formtags // Only validate DTX formtags
if (in_array($tag->basetype, array_merge( if (in_array($tag->basetype, array_merge(
array('dynamictext', 'dynamichidden'), // Deprecated DTX form tags array('dynamictext', 'dynamichidden'), // Deprecated DTX form tags
array_keys(wpcf7dtx_config()) // DTX form tags array_keys(wpcf7dtx_config()) // DTX form tags
))) { ))) {
// Check value for sensitive data // Check value for sensitive data
$default = $tag->get_option('defaultvalue', '', true); $default = $tag->get_option('defaultvalue', '', true);
if (!$default) { if (!$default) {
$default = $tag->get_default_option(strval(reset($tag->values))); $default = $tag->get_default_option(strval(reset($tag->values)));
} }
if ( if (
!empty($value = trim(wpcf7_get_hangover($tag->name, $default))) && // Has value !empty($value = trim(wpcf7_get_hangover($tag->name, $default))) && // Has value
($result = wpcf7dtx_validate_sensitive_value($value))['status'] // Has sensitive data ($result = wpcf7dtx_validate_sensitive_value($value))['status'] // Has sensitive data
) { ) {
$validator->add_error('form.body', 'dtx_disallowed', array( $validator->add_error('form.body', 'dtx_disallowed', array(
'message' => sprintf( 'message' => sprintf(
__('[%1$s %2$s]: Access to key "%3$s" in shortcode "%4$s" is disallowed by default. To allow access, add "%3$s" to the %5$s Allow List.', 'contact-form-7-dynamic-text-extension'), __('[%1$s %2$s]: Access to key "%3$s" in shortcode "%4$s" is disallowed by default. To allow access, add "%3$s" to the %5$s Allow List.', 'contact-form-7-dynamic-text-extension'),
esc_html($tag->basetype), esc_html($tag->basetype),
esc_html($tag->name), esc_html($tag->name),
esc_html($result['key']), esc_html($result['key']),
esc_html($result['shortcode']), esc_html($result['shortcode']),
esc_html($result['shortcode'] == 'CF7_get_current_user' ? __('User Data Key', 'contact-form-7-dynamic-text-extension') : __('Meta Key', 'contact-form-7-dynamic-text-extension')) esc_html($result['shortcode'] == 'CF7_get_current_user' ? __('User Data Key', 'contact-form-7-dynamic-text-extension') : __('Meta Key', 'contact-form-7-dynamic-text-extension'))
), ),
'link' => wpcf7dtx_get_admin_settings_screen_url() 'link' => wpcf7dtx_get_admin_settings_screen_url()
)); ));
} }
// Check placeholder for sensitive data // Check placeholder for sensitive data
if ( if (
($tag->has_option('placeholder') || $tag->has_option('watermark')) && // Using placeholder ($tag->has_option('placeholder') || $tag->has_option('watermark')) && // Using placeholder
!empty($placeholder = trim(html_entity_decode(urldecode($tag->get_option('placeholder', '', true)), ENT_QUOTES))) && // Has value !empty($placeholder = trim(html_entity_decode(urldecode($tag->get_option('placeholder', '', true)), ENT_QUOTES))) && // Has value
($result = wpcf7dtx_validate_sensitive_value($placeholder))['status'] // Has sensitive data ($result = wpcf7dtx_validate_sensitive_value($placeholder))['status'] // Has sensitive data
) { ) {
$validator->add_error('form.body', 'dtx_disallowed', array( $validator->add_error('form.body', 'dtx_disallowed', array(
'message' => sprintf( 'message' => sprintf(
__('[%1$s %2$s]: Access to key "%3$s" in shortcode "%4$s" is disallowed by default. To allow access, add "%3$s" to the %5$s Allow List.', 'contact-form-7-dynamic-text-extension'), __('[%1$s %2$s]: Access to key "%3$s" in shortcode "%4$s" is disallowed by default. To allow access, add "%3$s" to the %5$s Allow List.', 'contact-form-7-dynamic-text-extension'),
esc_html($tag->basetype), esc_html($tag->basetype),
esc_html($tag->name), esc_html($tag->name),
esc_html($result['key']), esc_html($result['key']),
esc_html($result['shortcode']), esc_html($result['shortcode']),
esc_html($result['shortcode'] == 'CF7_get_current_user' ? __('User Data Key', 'contact-form-7-dynamic-text-extension') : __('Meta Key', 'contact-form-7-dynamic-text-extension')) esc_html($result['shortcode'] == 'CF7_get_current_user' ? __('User Data Key', 'contact-form-7-dynamic-text-extension') : __('Meta Key', 'contact-form-7-dynamic-text-extension'))
), ),
'link' => wpcf7dtx_get_admin_settings_screen_url() 'link' => wpcf7dtx_get_admin_settings_screen_url()
)); ));
}
} }
} }
}
// Validate email address // Validate email address
if (!$validator->is_valid()) { if (!$validator->is_valid()) {
$contact_form = null; $contact_form = null;
$form_tags = null; $form_tags = null;
foreach ($validator->collect_error_messages() as $component => $errors) { foreach ($validator->collect_error_messages() as $component => $errors) {
$components = explode('.', $component); $components = explode('.', $component);
if (count($components) === 2 && strpos($components[0], 'mail') === 0 && in_array($components[1], array('sender', 'recipient', 'additional_headers'))) { if (count($components) === 2 && strpos($components[0], 'mail') === 0 && in_array($components[1], array('sender', 'recipient', 'additional_headers'))) {
foreach ($errors as $error) { 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 // 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 (strpos(wpcf7dtx_array_has_key('link', $error), 'invalid-mailbox-syntax') !== false) {
if (is_null($contact_form)) { if (is_null($contact_form)) {
$contact_form = $validator->contact_form(); $contact_form = $validator->contact_form();
} }
if (is_null($form_tags)) { if (is_null($form_tags)) {
$form_tags = wpcf7_scan_form_tags(); $form_tags = wpcf7_scan_form_tags();
} }
$raw_value = $contact_form->prop($components[0])[$components[1]]; $raw_value = $contact_form->prop($components[0])[$components[1]];
foreach ($form_tags as $tag) { foreach ($form_tags as $tag) {
if (!empty($tag->name)) { if (!empty($tag->name)) {
// Check if this form tag is in the raw value // Check if this form tag is in the raw value
$form_tag = '[' . $tag->name . ']'; $form_tag = '[' . $tag->name . ']';
if (strpos($raw_value, $form_tag) !== false && in_array($tag->basetype, array_keys(wpcf7dtx_config()))) { 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 $validator->remove_error($component, 'invalid_mailbox_syntax'); // Remove error, this is ours to handle now
$utm_source = urlencode(home_url()); $utm_source = urlencode(home_url());
if (!in_array($tag->basetype, array('dynamic_hidden', 'dynamic_email'))) { if (!in_array($tag->basetype, array('dynamic_hidden', 'dynamic_email'))) {
$validator->add_error($component, 'invalid_mailbox_syntax', array( $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'), '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)) '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))
)); ));
} else { } elseif (empty($dynamic_value) && !$tag->is_required()) {
$dynamic_value = wpcf7dtx_get_dynamic(false, $tag); // Get the dynamic value of this tag $validator->add_error($component, 'maybe_empty', array(
if (empty($dynamic_value) && $tag->basetype == 'dynamic_hidden') { 'message' => __('The dynamic form tag must be required or have a default value.', 'contact-form-7-dynamic-text-extension'),
$validator->add_error($component, 'maybe_empty', array( '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))
'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)) {
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 (empty($dynamic_value) && !$tag->is_required()) { } elseif ($component[1] == 'sender' && !wpcf7_is_email_in_site_domain($dynamic_value)) {
$validator->add_error($component, 'maybe_empty', array( $validator->add_error($component, 'email_not_in_site_domain', array(
'message' => __('The dynamic form tag must be required or have a default value.', 'contact-form-7-dynamic-text-extension'), '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-maybe_empty#maybe-empty', $utm_source)) '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))
)); ));
} 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))
));
}
} }
} }
} }
@@ -313,9 +307,23 @@ if (wpcf7dtx_dependencies()) {
} }
} }
} }
add_action('wpcf7_config_validator_validate', 'wpcf7dtx_validate');
} }
/**
* Initialise Custom Mail Template Validator
*
* Validator requires a minimum version of Contact Form 7.
*
* @return void
*/
function wpcf7dtx_init_validator()
{
if (wpcf7dtx_dependencies()) {
add_action('wpcf7_config_validator_validate', 'wpcf7dtx_validate');
}
}
add_action('plugins_loaded', 'wpcf7dtx_init_validator', 30);
/** /**
* Validate Field Value for Sensitive Data * Validate Field Value for Sensitive Data

View File

@@ -1,11 +1,11 @@
=== Contact Form 7 - Dynamic Text Extension === === Contact Form 7 - Dynamic Text Extension ===
Contributors: sevenspark, tessawatkinsllc Contributors: sevenspark, tessawatkinsllc
Donate link: https://just1voice.com/donate/ 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 Tags: Contact Form 7, autofill, prepopulate, dynamic form, form field
Tested up to: 6.4.2 Tested up to: 6.5
Stable tag: 4.2.3 Stable tag: 4.3.1
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. Extends Contact Form 7 by adding dynamic form fields that accepts shortcodes to prepopulate form fields with default values and dynamic placeholders.
== Description == == Description ==
@@ -380,14 +380,21 @@ Please check out the [FAQ on our website](https://aurisecreative.com/docs/contac
== Upgrade Notice == == Upgrade Notice ==
= 4.2.3 = = 4.3.1 =
Resolved a bug where the `dynamic_select` displayed with a default size of 40 instead of 1. Fixed user-reported bugs regarding `dynamic_checkbox` accepting multiple default values, mail template validator, and some PHP warnings. See [the changelog](https://plugins.trac.wordpress.org/browser/contact-form-7-dynamic-text-extension/trunk/changelog.txt) for more details.
== Changelog == == Changelog ==
= 4.2.3 = = 4.3.1 =
* Fix: Resolved a bug where the `dynamic_select` displayed with a default size of 40 instead of 1. * Fix: Resolved the PHP warning regarding `Undefined array key "value" in /.../contact-form-7-dynamic-text-extension/contact-form-7-dynamic-text-extension.php on line 391`, [see support thread](https://wordpress.org/support/topic/undefined-array-key-value-2/).
* Fix: Resolved a bug introduced in version 4.2.1 that prevented the mail template validator from recognizing DTX form tags, [see support thread](https://wordpress.org/support/topic/email-field-validation-4/).
* Fix: The `default` attribute for `dynamic_checkbox` can now accept multiple values that are delimited by an underscore (_), making it consistent with [Contact Form 7](https://contactform7.com/checkboxes-radio-buttons-and-menus/#checkbox-radio), [see support thread](https://wordpress.org/support/topic/help-dynamic_checkbox-and-default-values/).
= 4.3.0 =
* Feature: Added the `wpcf7dtx_shortcode` filter to all built-in shortcodes as requested. For usage details, see the [knowledge base](https://aurisecreative.com/docs/contact-form-7-dynamic-text-extension/filter-modify-built-in-shortcode-responses/?utm_source=wordpress.org&utm_medium=link&utm_campaign=contact-form-7-dynamic-text-extension&utm_content=readme).
* Fix: Resolved a bug that prevented using the number zero as the value for select fields, checkboxes, and radio buttons, [see support thread](https://wordpress.org/support/topic/error-with-option-value-0-for-a-dynamic-radio-button/).
= 4.2.2 = = 4.2.2 =
@@ -406,7 +413,7 @@ Resolved a bug where the `dynamic_select` displayed with a default size of 40 in
= 4.2.0 = = 4.2.0 =
* Security Update: ** Please be sure to review this doc, as you may need to adjust the settings: https://sevenspark.com/docs/contact-form-7-dynamic-text-extension/allow-data-access ** * Security Update: ** Please be sure to review this doc, as you may need to adjust the settings: [Documentation by SevenSpark](https://sevenspark.com/docs/contact-form-7-dynamic-text-extension/allow-data-access), [Documentation by AuRise Creative](https://aurisecreative.com/docs/contact-form-7-dynamic-text-extension/security/) **
* Feature: Added Settings Screen with Allow Lists * Feature: Added Settings Screen with Allow Lists
* Feature: Added Form Scanner * Feature: Added Form Scanner
* Feature: Added Allow List key validation in CF7 Form Validator * Feature: Added Allow List key validation in CF7 Form Validator

View File

@@ -130,7 +130,8 @@ function wpcf7_admin_enqueue_scripts( $hook_suffix ) {
wp_enqueue_script( 'wpcf7-admin', wp_enqueue_script( 'wpcf7-admin',
wpcf7_plugin_url( 'admin/js/scripts.js' ), wpcf7_plugin_url( 'admin/js/scripts.js' ),
array( 'jquery', 'jquery-ui-tabs' ), array( 'jquery', 'jquery-ui-tabs' ),
WPCF7_VERSION, true WPCF7_VERSION,
array( 'in_footer' => true )
); );
$args = array( $args = array(
@@ -144,8 +145,7 @@ function wpcf7_admin_enqueue_scripts( $hook_suffix ) {
'saveAlert' => __( 'saveAlert' => __(
"The changes you made will be lost if you navigate away from this page.", "The changes you made will be lost if you navigate away from this page.",
'contact-form-7' ), 'contact-form-7' ),
'activeTab' => isset( $_GET['active-tab'] ) 'activeTab' => (int) ( $_GET['active-tab'] ?? 0 ),
? (int) $_GET['active-tab'] : 0,
'configValidator' => array( 'configValidator' => array(
'errors' => array(), 'errors' => array(),
'howToCorrect' => __( "How to resolve?", 'contact-form-7' ), 'howToCorrect' => __( "How to resolve?", 'contact-form-7' ),
@@ -176,7 +176,7 @@ function wpcf7_admin_enqueue_scripts( $hook_suffix ) {
wpcf7_plugin_url( 'admin/js/tag-generator.js' ), wpcf7_plugin_url( 'admin/js/tag-generator.js' ),
array( 'jquery', 'thickbox', 'wpcf7-admin' ), array( 'jquery', 'thickbox', 'wpcf7-admin' ),
WPCF7_VERSION, WPCF7_VERSION,
true array( 'in_footer' => true )
); );
} }
@@ -204,12 +204,13 @@ function wpcf7_load_contact_form_admin() {
$action = wpcf7_current_action(); $action = wpcf7_current_action();
do_action( 'wpcf7_admin_load', do_action( 'wpcf7_admin_load',
isset( $_GET['page'] ) ? trim( $_GET['page'] ) : '', trim( $_GET['page'] ?? '' ),
$action $action
); );
if ( 'save' == $action ) { if ( 'save' === $action ) {
$id = isset( $_POST['post_ID'] ) ? $_POST['post_ID'] : '-1'; $id = $_POST['post_ID'] ?? '-1';
check_admin_referer( 'wpcf7-save-contact-form_' . $id ); check_admin_referer( 'wpcf7-save-contact-form_' . $id );
if ( ! current_user_can( 'wpcf7_edit_contact_form', $id ) ) { if ( ! current_user_can( 'wpcf7_edit_contact_form', $id ) ) {
@@ -218,31 +219,21 @@ function wpcf7_load_contact_form_admin() {
); );
} }
$args = $_REQUEST; $contact_form = wpcf7_save_contact_form(
$args['id'] = $id; array_merge(
$_REQUEST,
$args['title'] = isset( $_POST['post_title'] ) array(
? $_POST['post_title'] : null; 'id' => $id,
'title' => $_POST['post_title'] ?? null,
$args['locale'] = isset( $_POST['wpcf7-locale'] ) 'locale' => $_POST['wpcf7-locale'] ?? null,
? $_POST['wpcf7-locale'] : null; 'form' => $_POST['wpcf7-form'] ?? '',
'mail' => $_POST['wpcf7-mail'] ?? array(),
$args['form'] = isset( $_POST['wpcf7-form'] ) 'mail_2' => $_POST['wpcf7-mail-2'] ?? array(),
? $_POST['wpcf7-form'] : ''; 'messages' => $_POST['wpcf7-messages'] ?? array(),
'additional_settings' => $_POST['wpcf7-additional-settings'] ?? '',
$args['mail'] = isset( $_POST['wpcf7-mail'] ) )
? $_POST['wpcf7-mail'] : array(); )
);
$args['mail_2'] = isset( $_POST['wpcf7-mail-2'] )
? $_POST['wpcf7-mail-2'] : array();
$args['messages'] = isset( $_POST['wpcf7-messages'] )
? $_POST['wpcf7-messages'] : array();
$args['additional_settings'] = isset( $_POST['wpcf7-additional-settings'] )
? $_POST['wpcf7-additional-settings'] : '';
$contact_form = wpcf7_save_contact_form( $args );
if ( $contact_form and wpcf7_validate_configuration() ) { if ( $contact_form and wpcf7_validate_configuration() ) {
$config_validator = new WPCF7_ConfigValidator( $contact_form ); $config_validator = new WPCF7_ConfigValidator( $contact_form );
@@ -252,8 +243,7 @@ function wpcf7_load_contact_form_admin() {
$query = array( $query = array(
'post' => $contact_form ? $contact_form->id() : 0, 'post' => $contact_form ? $contact_form->id() : 0,
'active-tab' => isset( $_POST['active-tab'] ) 'active-tab' => (int) ( $_POST['active-tab'] ?? 0 ),
? (int) $_POST['active-tab'] : 0,
); );
if ( ! $contact_form ) { if ( ! $contact_form ) {
@@ -269,10 +259,8 @@ function wpcf7_load_contact_form_admin() {
exit(); exit();
} }
if ( 'copy' == $action ) { if ( 'copy' === $action ) {
$id = empty( $_POST['post_ID'] ) $id = absint( $_POST['post_ID'] ?? $_REQUEST['post'] ?? '' );
? absint( $_REQUEST['post'] )
: absint( $_POST['post_ID'] );
check_admin_referer( 'wpcf7-copy-contact-form_' . $id ); check_admin_referer( 'wpcf7-copy-contact-form_' . $id );
@@ -298,7 +286,7 @@ function wpcf7_load_contact_form_admin() {
exit(); exit();
} }
if ( 'delete' == $action ) { if ( 'delete' === $action ) {
if ( ! empty( $_POST['post_ID'] ) ) { if ( ! empty( $_POST['post_ID'] ) ) {
check_admin_referer( 'wpcf7-delete-contact-form_' . $_POST['post_ID'] ); check_admin_referer( 'wpcf7-delete-contact-form_' . $_POST['post_ID'] );
} elseif ( ! is_array( $_REQUEST['post'] ) ) { } elseif ( ! is_array( $_REQUEST['post'] ) ) {
@@ -307,9 +295,7 @@ function wpcf7_load_contact_form_admin() {
check_admin_referer( 'bulk-posts' ); check_admin_referer( 'bulk-posts' );
} }
$posts = empty( $_POST['post_ID'] ) $posts = (array) ( $_POST['post_ID'] ?? $_REQUEST['post'] ?? array() );
? (array) $_REQUEST['post']
: (array) $_POST['post_ID'];
$deleted = 0; $deleted = 0;
@@ -347,9 +333,9 @@ function wpcf7_load_contact_form_admin() {
$post = null; $post = null;
if ( 'wpcf7-new' == $plugin_page ) { if ( 'wpcf7-new' === $plugin_page ) {
$post = WPCF7_ContactForm::get_template( array( $post = WPCF7_ContactForm::get_template( array(
'locale' => isset( $_GET['locale'] ) ? $_GET['locale'] : null, 'locale' => $_GET['locale'] ?? null,
) ); ) );
} elseif ( ! empty( $_GET['post'] ) ) { } elseif ( ! empty( $_GET['post'] ) ) {
$post = WPCF7_ContactForm::get_instance( $_GET['post'] ); $post = WPCF7_ContactForm::get_instance( $_GET['post'] );
@@ -470,7 +456,7 @@ function wpcf7_admin_add_new_page() {
function wpcf7_load_integration_page() { function wpcf7_load_integration_page() {
do_action( 'wpcf7_admin_load', do_action( 'wpcf7_admin_load',
isset( $_GET['page'] ) ? trim( $_GET['page'] ) : '', trim( $_GET['page'] ?? '' ),
wpcf7_current_action() wpcf7_current_action()
); );
@@ -520,7 +506,7 @@ function wpcf7_admin_integration_page() {
); );
if ( $service ) { if ( $service ) {
$message = isset( $_REQUEST['message'] ) ? $_REQUEST['message'] : ''; $message = $_REQUEST['message'] ?? '';
$service->admin_notice( $message ); $service->admin_notice( $message );
$integration->list_services( array( $integration->list_services( array(
@@ -547,11 +533,11 @@ function wpcf7_admin_updated_message( $page, $action, $object ) {
return; return;
} }
if ( 'created' == $_REQUEST['message'] ) { if ( 'created' === $_REQUEST['message'] ) {
$updated_message = __( "Contact form created.", 'contact-form-7' ); $updated_message = __( "Contact form created.", 'contact-form-7' );
} elseif ( 'saved' == $_REQUEST['message'] ) { } elseif ( 'saved' === $_REQUEST['message'] ) {
$updated_message = __( "Contact form saved.", 'contact-form-7' ); $updated_message = __( "Contact form saved.", 'contact-form-7' );
} elseif ( 'deleted' == $_REQUEST['message'] ) { } elseif ( 'deleted' === $_REQUEST['message'] ) {
$updated_message = __( "Contact form deleted.", 'contact-form-7' ); $updated_message = __( "Contact form deleted.", 'contact-form-7' );
} }
@@ -564,7 +550,7 @@ function wpcf7_admin_updated_message( $page, $action, $object ) {
return; return;
} }
if ( 'failed' == $_REQUEST['message'] ) { if ( 'failed' === $_REQUEST['message'] ) {
$updated_message = $updated_message =
__( "There was an error saving the contact form.", 'contact-form-7' ); __( "There was an error saving the contact form.", 'contact-form-7' );
@@ -576,7 +562,7 @@ function wpcf7_admin_updated_message( $page, $action, $object ) {
return; return;
} }
if ( 'validated' == $_REQUEST['message'] ) { if ( 'validated' === $_REQUEST['message'] ) {
$bulk_validate = WPCF7::get_option( 'bulk_validate', array() ); $bulk_validate = WPCF7::get_option( 'bulk_validate', array() );
$count_invalid = isset( $bulk_validate['count_invalid'] ) $count_invalid = isset( $bulk_validate['count_invalid'] )
? absint( $bulk_validate['count_invalid'] ) : 0; ? absint( $bulk_validate['count_invalid'] ) : 0;
@@ -693,3 +679,21 @@ function wpcf7_outdated_php_warning( $page, $action, $object ) {
esc_html( $message ) esc_html( $message )
); );
} }
add_action( 'wpcf7_admin_warnings', 'wpcf7_ctct_deprecated_warning', 10, 3 );
function wpcf7_ctct_deprecated_warning( $page, $action, $object ) {
$service = WPCF7_ConstantContact::get_instance();
if ( ! $service->is_active() ) {
return;
}
$message = __( "The Constant Contact integration is deprecated. It is not recommended to continue using the feature.", 'contact-form-7' );
echo sprintf(
'<div class="notice notice-warning"><p>%s</p></div>',
esc_html( $message )
);
}

View File

@@ -85,7 +85,7 @@ if ( $post ) :
<input type="hidden" id="post_ID" name="post_ID" value="<?php echo (int) $post_id; ?>" /> <input type="hidden" id="post_ID" name="post_ID" value="<?php echo (int) $post_id; ?>" />
<input type="hidden" id="wpcf7-locale" name="wpcf7-locale" value="<?php echo esc_attr( $post->locale() ); ?>" /> <input type="hidden" id="wpcf7-locale" name="wpcf7-locale" value="<?php echo esc_attr( $post->locale() ); ?>" />
<input type="hidden" id="hiddenaction" name="action" value="save" /> <input type="hidden" id="hiddenaction" name="action" value="save" />
<input type="hidden" id="active-tab" name="active-tab" value="<?php echo isset( $_GET['active-tab'] ) ? (int) $_GET['active-tab'] : '0'; ?>" /> <input type="hidden" id="active-tab" name="active-tab" value="<?php echo (int) ( $_GET['active-tab'] ?? '0' ); ?>" />
<div id="poststuff"> <div id="poststuff">
<div id="post-body" class="metabox-holder columns-2"> <div id="post-body" class="metabox-holder columns-2">

View File

@@ -146,15 +146,11 @@ class WPCF7_WelcomePanelColumn_Integration extends WPCF7_WelcomePanelColumn {
protected function content() { protected function content() {
return array( return array(
sprintf( sprintf(
/* translators: 1: link labeled 'Brevo', 2: link labeled 'Constant Contact' */ /* translators: 1: link labeled 'Brevo' */
esc_html( __( 'Your contact forms will become more powerful and versatile by integrating them with external APIs. With CRM and email marketing services, you can build your own contact lists (%1$s and %2$s).', 'contact-form-7' ) ), esc_html( __( 'Your contact forms will become more powerful and versatile by integrating them with external APIs. With CRM and email marketing services, you can build your own contact lists (%1$s).', 'contact-form-7' ) ),
wpcf7_link( wpcf7_link(
__( 'https://contactform7.com/sendinblue-integration/', 'contact-form-7' ), __( 'https://contactform7.com/sendinblue-integration/', 'contact-form-7' ),
__( 'Brevo', 'contact-form-7' ) __( 'Brevo', 'contact-form-7' )
),
wpcf7_link(
__( 'https://contactform7.com/constant-contact-integration/', 'contact-form-7' ),
__( 'Constant Contact', 'contact-form-7' )
) )
), ),
sprintf( sprintf(

View File

@@ -1,6 +1,6 @@
{ {
"$schema": "https://schemas.wp.org/trunk/block.json", "$schema": "https://schemas.wp.org/trunk/block.json",
"apiVersion": 2, "apiVersion": 3,
"name": "contact-form-7/contact-form-selector", "name": "contact-form-7/contact-form-selector",
"title": "Contact Form 7", "title": "Contact Form 7",
"category": "widgets", "category": "widgets",

File diff suppressed because one or more lines are too long

View File

@@ -39,74 +39,49 @@ trait WPCF7_ConfigValidator_Mail {
$example_text = 'example'; $example_text = 'example';
$example_blank = ''; $example_blank = '';
$form_tags = $this->contact_form->scan_form_tags( // for back-compat
array( 'name' => $field_name ) $field_name = preg_replace( '/^wpcf7\./', '_', $field_name );
);
if ( $form_tags ) { if ( '_site_admin_email' === $field_name ) {
$form_tag = new WPCF7_FormTag( $form_tags[0] ); return get_bloginfo( 'admin_email', 'raw' );
$is_required = $form_tag->is_required() || 'radio' === $form_tag->type; } elseif ( '_user_agent' === $field_name ) {
return $example_text;
if ( ! $is_required ) { } elseif ( '_user_email' === $field_name ) {
return $example_blank; return $this->contact_form->is_true( 'subscribers_only' )
} ? $example_email
: $example_blank;
if ( wpcf7_form_tag_supports( $form_tag->type, 'selectable-values' ) ) { } elseif ( str_starts_with( $field_name, '_user_' ) ) {
if ( $form_tag->pipes instanceof WPCF7_Pipes ) { return $this->contact_form->is_true( 'subscribers_only' )
if ( $mail_tag->get_option( 'do_not_heat' ) ) { ? $example_text
$before_pipes = $form_tag->pipes->collect_befores(); : $example_blank;
$last_item = array_pop( $before_pipes );
} else {
$after_pipes = $form_tag->pipes->collect_afters();
$last_item = array_pop( $after_pipes );
}
} else {
$last_item = array_pop( $form_tag->values );
}
if ( $last_item and wpcf7_is_mailbox_list( $last_item ) ) { } elseif ( str_starts_with( $field_name, '_' ) ) {
return $example_email; return str_ends_with( $field_name, '_email' )
} else { ? $example_email
return $example_text; : $example_text;
}
}
if ( 'email' === $form_tag->basetype ) {
return $example_email;
} else {
return $example_text;
}
} else { // maybe special mail tag
// for back-compat
$field_name = preg_replace( '/^wpcf7\./', '_', $field_name );
if ( '_site_admin_email' === $field_name ) {
return get_bloginfo( 'admin_email', 'raw' );
} elseif ( '_user_agent' === $field_name ) {
return $example_text;
} elseif ( '_user_email' === $field_name ) {
return $this->contact_form->is_true( 'subscribers_only' )
? $example_email
: $example_blank;
} elseif ( str_starts_with( $field_name, '_user_' ) ) {
return $this->contact_form->is_true( 'subscribers_only' )
? $example_text
: $example_blank;
} elseif ( str_starts_with( $field_name, '_' ) ) {
return str_ends_with( $field_name, '_email' )
? $example_email
: $example_text;
}
} }
return $tag; static $opcalcset = array();
if ( ! isset( $opcalcset[$this->contact_form->id()] ) ) {
$opcalcset[$this->contact_form->id()] =
new WPCF7_MailTag_OutputCalculator( $this->contact_form );
}
$opcalc = $opcalcset[$this->contact_form->id()];
$op = $opcalc->calc_output( $mail_tag );
if ( WPCF7_MailTag_OutputCalculator::email === $op ) {
return $example_email;
} elseif ( ! ( WPCF7_MailTag_OutputCalculator::blank & $op ) ) {
return $example_text;
} else {
return $example_blank;
}
} }

View File

@@ -3,6 +3,7 @@
class WPCF7_ContactForm { class WPCF7_ContactForm {
use WPCF7_SWV_SchemaHolder; use WPCF7_SWV_SchemaHolder;
use WPCF7_PipesHolder;
const post_type = 'wpcf7_contact_form'; const post_type = 'wpcf7_contact_form';

View File

@@ -54,7 +54,7 @@ add_action(
array( 'swv' ) array( 'swv' )
), ),
$assets['version'], $assets['version'],
true array( 'in_footer' => true )
); );
wp_register_script( wp_register_script(
@@ -62,7 +62,7 @@ add_action(
wpcf7_plugin_url( 'includes/js/html5-fallback.js' ), wpcf7_plugin_url( 'includes/js/html5-fallback.js' ),
array( 'jquery-ui-datepicker' ), array( 'jquery-ui-datepicker' ),
WPCF7_VERSION, WPCF7_VERSION,
true array( 'in_footer' => true )
); );
if ( wpcf7_load_js() ) { if ( wpcf7_load_js() ) {

View File

@@ -34,13 +34,15 @@ function wpcf7_unship_uploaded_file( $file, $args = '' ) {
} }
if ( isset( $args['schema'] ) and isset( $args['name'] ) ) { if ( isset( $args['schema'] ) and isset( $args['name'] ) ) {
$result = $args['schema']->validate( array( $context = array(
'file' => true, 'file' => true,
'field' => $args['name'], 'field' => $args['name'],
) ); );
if ( is_wp_error( $result ) ) { foreach ( $args['schema']->validate( $context ) as $result ) {
return $result; if ( is_wp_error( $result ) ) {
return $result;
}
} }
} }

View File

@@ -141,6 +141,8 @@ class WPCF7_Integration {
<h2 class="title"><?php echo esc_html( $service->get_title() ); ?></h2> <h2 class="title"><?php echo esc_html( $service->get_title() ); ?></h2>
<div class="infobox"> <div class="infobox">
<?php echo esc_html( implode( ', ', $cats ) ); ?> <?php echo esc_html( implode( ', ', $cats ) ); ?>
<br />
<?php $service->link(); ?>
</div> </div>
<br class="clear" /> <br class="clear" />
@@ -247,8 +249,8 @@ class WPCF7_Service_OAuth2 extends WPCF7_Service {
public function load( $action = '' ) { public function load( $action = '' ) {
if ( 'auth_redirect' == $action ) { if ( 'auth_redirect' === $action ) {
$code = isset( $_GET['code'] ) ? $_GET['code'] : ''; $code = $_GET['code'] ?? '';
if ( $code ) { if ( $code ) {
$this->request_token( $code ); $this->request_token( $code );

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,197 @@
<?php
/**
* Class that represents a mail-tag.
*/
class WPCF7_MailTag {
private $tag;
private $tagname = '';
private $name = '';
private $options = array();
private $values = array();
private $form_tag = null;
/**
* The constructor method.
*/
public function __construct( $tag, $tagname, $values ) {
$this->tag = $tag;
$this->name = $this->tagname = $tagname;
$this->options = array(
'do_not_heat' => false,
'format' => '',
);
if ( ! empty( $values ) ) {
preg_match_all( '/"[^"]*"|\'[^\']*\'/', $values, $matches );
$this->values = wpcf7_strip_quote_deep( $matches[0] );
}
if ( preg_match( '/^_raw_(.+)$/', $tagname, $matches ) ) {
$this->name = trim( $matches[1] );
$this->options['do_not_heat'] = true;
}
if ( preg_match( '/^_format_(.+)$/', $tagname, $matches ) ) {
$this->name = trim( $matches[1] );
$this->options['format'] = $this->values[0];
}
}
/**
* Returns the name part of this mail-tag.
*/
public function tag_name() {
return $this->tagname;
}
/**
* Returns the form field name corresponding to this mail-tag.
*/
public function field_name() {
return strtr( $this->name, '.', '_' );
}
/**
* Returns the value of the specified option.
*/
public function get_option( $option ) {
return $this->options[$option];
}
/**
* Returns the values part of this mail-tag.
*/
public function values() {
return $this->values;
}
/**
* Retrieves the WPCF7_FormTag object that corresponds to this mail-tag.
*/
public function corresponding_form_tag() {
if ( $this->form_tag instanceof WPCF7_FormTag ) {
return $this->form_tag;
}
if ( $submission = WPCF7_Submission::get_instance() ) {
$contact_form = $submission->get_contact_form();
$tags = $contact_form->scan_form_tags( array(
'name' => $this->field_name(),
'feature' => '! zero-controls-container',
) );
if ( $tags ) {
$this->form_tag = $tags[0];
}
}
return $this->form_tag;
}
}
use Contactable\SWV;
/**
* Mail-tag output calculator.
*/
class WPCF7_MailTag_OutputCalculator {
const email = 0b100;
const text = 0b010;
const blank = 0b001;
private $contact_form;
public function __construct( WPCF7_ContactForm $contact_form ) {
$this->contact_form = $contact_form;
}
public function calc_output( WPCF7_MailTag $mail_tag ) {
return $this->calc_swv_result(
$mail_tag,
$this->contact_form->get_schema()
);
}
private function calc_swv_result( WPCF7_MailTag $mail_tag, SWV\Rule $rule ) {
if ( $rule instanceof SWV\AnyRule ) {
$result = 0b000;
foreach ( $rule->rules() as $child_rule ) {
$result |= $this->calc_swv_result( $mail_tag, $child_rule );
}
return $result;
}
if ( $rule instanceof SWV\CompositeRule ) {
$result = 0b111;
foreach ( $rule->rules() as $child_rule ) {
$result &= $this->calc_swv_result( $mail_tag, $child_rule );
}
return $result;
}
$field_prop = $rule->get_property( 'field' );
if ( empty( $field_prop ) or $field_prop !== $mail_tag->field_name() ) {
return self::email | self::text | self::blank;
}
if ( $rule instanceof SWV\RequiredRule ) {
return ~ self::blank;
}
if ( $rule instanceof SWV\EmailRule ) {
return self::email | self::blank;
}
if ( $rule instanceof SWV\EnumRule ) {
$acceptable_values = (array) $rule->get_property( 'accept' );
$acceptable_values = array_map( 'strval', $acceptable_values );
$acceptable_values = array_filter( $acceptable_values );
$acceptable_values = array_unique( $acceptable_values );
if ( ! $mail_tag->get_option( 'do_not_heat' ) ) {
$pipes = $this->contact_form->get_pipes(
$mail_tag->field_name()
);
$acceptable_values = array_map(
static function ( $val ) use ( $pipes ) {
return $pipes->do_pipe( $val );
},
$acceptable_values
);
}
$email_values = array_filter(
$acceptable_values,
'wpcf7_is_mailbox_list'
);
if ( count( $email_values ) === count( $acceptable_values ) ) {
return self::email | self::blank;
} else {
return self::email | self::text | self::blank;
}
}
return self::email | self::text | self::blank;
}
}

View File

@@ -24,6 +24,7 @@ class WPCF7_Mail {
private $name = ''; private $name = '';
private $locale = ''; private $locale = '';
private $template = array(); private $template = array();
private $component = '';
private $use_html = false; private $use_html = false;
private $exclude_blank = false; private $exclude_blank = false;
@@ -36,6 +37,35 @@ class WPCF7_Mail {
} }
/**
* Returns the name of the email template currently processed.
*
* Expected output: 'mail' or 'mail_2'
*/
public static function get_current_template_name() {
$current = self::get_current();
if ( $current instanceof self ) {
return $current->get_template_name();
}
}
/**
* Returns the name of the email template component currently processed.
*
* Expected output: 'recipient', 'sender', 'subject',
* 'additional_headers', 'body', or 'attachments'
*/
public static function get_current_component_name() {
$current = self::get_current();
if ( $current instanceof self ) {
return $current->get_component_name();
}
}
/** /**
* Composes and sends email based on the specified template. * Composes and sends email based on the specified template.
* *
@@ -86,6 +116,22 @@ class WPCF7_Mail {
} }
/**
* Returns the name of the email template. A wrapper method of name().
*/
public function get_template_name() {
return $this->name();
}
/**
* Returns the name of the email template component currently processed.
*/
public function get_component_name() {
return $this->component;
}
/** /**
* Retrieves a component from the email template. * Retrieves a component from the email template.
* *
@@ -95,8 +141,10 @@ class WPCF7_Mail {
* @return string The text representation of the email component. * @return string The text representation of the email component.
*/ */
public function get( $component, $replace_tags = false ) { public function get( $component, $replace_tags = false ) {
$use_html = ( $this->use_html && 'body' == $component ); $this->component = $component;
$exclude_blank = ( $this->exclude_blank && 'body' == $component );
$use_html = ( $this->use_html && 'body' === $component );
$exclude_blank = ( $this->exclude_blank && 'body' === $component );
$template = $this->template; $template = $this->template;
$component = isset( $template[$component] ) ? $template[$component] : ''; $component = isset( $template[$component] ) ? $template[$component] : '';
@@ -127,6 +175,8 @@ class WPCF7_Mail {
} }
} }
$this->component = '';
return $component; return $component;
} }
@@ -502,9 +552,7 @@ class WPCF7_MailTaggedText {
: null; : null;
if ( $mail_tag->get_option( 'do_not_heat' ) ) { if ( $mail_tag->get_option( 'do_not_heat' ) ) {
$submitted = isset( $_POST[$field_name] ) $submitted = wp_unslash( $_POST[$field_name] ?? '' );
? wp_unslash( $_POST[$field_name] )
: '';
} }
$replaced = $submitted; $replaced = $submitted;
@@ -514,8 +562,12 @@ class WPCF7_MailTaggedText {
$replaced = $this->format( $replaced, $format ); $replaced = $this->format( $replaced, $format );
} }
$separator = ( 'body' === WPCF7_Mail::get_current_component_name() )
? wp_get_list_item_separator()
: ', ';
$replaced = wpcf7_flat_join( $replaced, array( $replaced = wpcf7_flat_join( $replaced, array(
'separator' => wp_get_list_item_separator(), 'separator' => $separator,
) ); ) );
if ( $html ) { if ( $html ) {
@@ -578,103 +630,3 @@ class WPCF7_MailTaggedText {
} }
} }
/**
* Class that represents a mail-tag.
*/
class WPCF7_MailTag {
private $tag;
private $tagname = '';
private $name = '';
private $options = array();
private $values = array();
private $form_tag = null;
/**
* The constructor method.
*/
public function __construct( $tag, $tagname, $values ) {
$this->tag = $tag;
$this->name = $this->tagname = $tagname;
$this->options = array(
'do_not_heat' => false,
'format' => '',
);
if ( ! empty( $values ) ) {
preg_match_all( '/"[^"]*"|\'[^\']*\'/', $values, $matches );
$this->values = wpcf7_strip_quote_deep( $matches[0] );
}
if ( preg_match( '/^_raw_(.+)$/', $tagname, $matches ) ) {
$this->name = trim( $matches[1] );
$this->options['do_not_heat'] = true;
}
if ( preg_match( '/^_format_(.+)$/', $tagname, $matches ) ) {
$this->name = trim( $matches[1] );
$this->options['format'] = $this->values[0];
}
}
/**
* Returns the name part of this mail-tag.
*/
public function tag_name() {
return $this->tagname;
}
/**
* Returns the form field name corresponding to this mail-tag.
*/
public function field_name() {
return strtr( $this->name, '.', '_' );
}
/**
* Returns the value of the specified option.
*/
public function get_option( $option ) {
return $this->options[$option];
}
/**
* Returns the values part of this mail-tag.
*/
public function values() {
return $this->values;
}
/**
* Retrieves the WPCF7_FormTag object that corresponds to this mail-tag.
*/
public function corresponding_form_tag() {
if ( $this->form_tag instanceof WPCF7_FormTag ) {
return $this->form_tag;
}
if ( $submission = WPCF7_Submission::get_instance() ) {
$contact_form = $submission->get_contact_form();
$tags = $contact_form->scan_form_tags( array(
'name' => $this->field_name(),
'feature' => '! zero-controls-container',
) );
if ( $tags ) {
$this->form_tag = $tags[0];
}
}
return $this->form_tag;
}
}

View File

@@ -36,8 +36,8 @@ class WPCF7_Pipes {
private $pipes = array(); private $pipes = array();
public function __construct( array $texts ) { public function __construct( array $texts = null ) {
foreach ( $texts as $text ) { foreach ( (array) $texts as $text ) {
$this->add_pipe( $text ); $this->add_pipe( $text );
} }
} }
@@ -47,6 +47,10 @@ class WPCF7_Pipes {
$this->pipes[] = $pipe; $this->pipes[] = $pipe;
} }
public function merge( self $another ) {
$this->pipes = array_merge( $this->pipes, $another->pipes );
}
public function do_pipe( $input ) { public function do_pipe( $input ) {
$input_canonical = wpcf7_canonicalize( $input, array( $input_canonical = wpcf7_canonicalize( $input, array(
'strto' => 'as-is', 'strto' => 'as-is',
@@ -109,3 +113,37 @@ class WPCF7_Pipes {
); );
} }
} }
/**
* Trait for classes that hold cross-tag WPCF7_Pipes object.
*/
trait WPCF7_PipesHolder {
protected $pipes;
public function get_pipes( $field_name ) {
if ( isset( $this->pipes[$field_name] ) ) {
return $this->pipes[$field_name];
}
$result = new WPCF7_Pipes;
$tags = $this->scan_form_tags( array(
'name' => $field_name,
) );
foreach ( $tags as $tag ) {
if ( $tag->pipes instanceof WPCF7_Pipes ) {
$result->merge( $tag->pipes );
}
}
return $this->pipes[$field_name] = $result;
}
public function scan_form_tags() {
return array();
}
}

View File

@@ -259,11 +259,7 @@ class WPCF7_Submission {
* or false when no invalid field. * or false when no invalid field.
*/ */
public function get_invalid_field( $name ) { public function get_invalid_field( $name ) {
if ( isset( $this->invalid_fields[$name] ) ) { return $this->invalid_fields[$name] ?? false;
return $this->invalid_fields[$name];
} else {
return false;
}
} }
@@ -285,9 +281,7 @@ class WPCF7_Submission {
* null otherwise. * null otherwise.
*/ */
public function get_meta( $name ) { public function get_meta( $name ) {
if ( isset( $this->meta[$name] ) ) { return $this->meta[$name] ?? null;
return $this->meta[$name];
}
} }
@@ -295,38 +289,16 @@ class WPCF7_Submission {
* Collects meta information about this submission. * Collects meta information about this submission.
*/ */
private function setup_meta_data() { private function setup_meta_data() {
$timestamp = time();
$remote_ip = $this->get_remote_ip_addr();
$remote_port = isset( $_SERVER['REMOTE_PORT'] )
? (int) $_SERVER['REMOTE_PORT'] : '';
$user_agent = isset( $_SERVER['HTTP_USER_AGENT'] )
? substr( $_SERVER['HTTP_USER_AGENT'], 0, 254 ) : '';
$url = $this->get_request_url();
$unit_tag = isset( $_POST['_wpcf7_unit_tag'] )
? wpcf7_sanitize_unit_tag( $_POST['_wpcf7_unit_tag'] ) : '';
$container_post_id = isset( $_POST['_wpcf7_container_post'] )
? (int) $_POST['_wpcf7_container_post'] : 0;
$current_user_id = get_current_user_id();
$do_not_store = $this->contact_form->is_true( 'do_not_store' );
$this->meta = array( $this->meta = array(
'timestamp' => $timestamp, 'timestamp' => time(),
'remote_ip' => $remote_ip, 'remote_ip' => $this->get_remote_ip_addr(),
'remote_port' => $remote_port, 'remote_port' => $_SERVER['REMOTE_PORT'] ?? '',
'user_agent' => $user_agent, 'user_agent' => substr( $_SERVER['HTTP_USER_AGENT'] ?? '', 0, 254 ),
'url' => $url, 'url' => $this->get_request_url(),
'unit_tag' => $unit_tag, 'unit_tag' => wpcf7_sanitize_unit_tag( $_POST['_wpcf7_unit_tag'] ?? '' ),
'container_post_id' => $container_post_id, 'container_post_id' => absint( $_POST['_wpcf7_container_post'] ?? 0 ),
'current_user_id' => $current_user_id, 'current_user_id' => get_current_user_id(),
'do_not_store' => $do_not_store, 'do_not_store' => $this->contact_form->is_true( 'do_not_store' ),
); );
return $this->meta; return $this->meta;
@@ -342,11 +314,7 @@ class WPCF7_Submission {
*/ */
public function get_posted_data( $name = '' ) { public function get_posted_data( $name = '' ) {
if ( ! empty( $name ) ) { if ( ! empty( $name ) ) {
if ( isset( $this->posted_data[$name] ) ) { return $this->posted_data[$name] ?? null;
return $this->posted_data[$name];
} else {
return null;
}
} }
return $this->posted_data; return $this->posted_data;
@@ -377,86 +345,56 @@ class WPCF7_Submission {
* Constructs posted data property based on user input values. * Constructs posted data property based on user input values.
*/ */
private function setup_posted_data() { private function setup_posted_data() {
$posted_data = array_filter( (array) $_POST, static function ( $key ) { $posted_data = array_filter(
return '_' !== substr( $key, 0, 1 ); (array) $_POST,
}, ARRAY_FILTER_USE_KEY ); static function ( $key ) {
return ! str_starts_with( $key, '_' );
},
ARRAY_FILTER_USE_KEY
);
$posted_data = wp_unslash( $posted_data ); $posted_data = wp_unslash( $posted_data );
$posted_data = $this->sanitize_posted_data( $posted_data ); $posted_data = $this->sanitize_posted_data( $posted_data );
$tags = $this->contact_form->scan_form_tags(); $tags = $this->contact_form->scan_form_tags( array(
'feature' => array(
'name-attr',
'! not-for-mail',
),
) );
foreach ( (array) $tags as $tag ) { $tags = array_reduce( $tags, static function ( $carry, $tag ) {
if ( empty( $tag->name ) ) { if ( $tag->name and ! isset( $carry[$tag->name] ) ) {
continue; $carry[$tag->name] = $tag;
} }
$type = $tag->type; return $carry;
$name = $tag->name; }, array() );
$pipes = $tag->pipes;
$value_orig = $value = ''; foreach ( $tags as $tag ) {
$value_orig = $value = $posted_data[$tag->name] ?? '';
if ( isset( $posted_data[$name] ) ) { if ( wpcf7_form_tag_supports( $tag->type, 'selectable-values' ) ) {
$value_orig = $value = $posted_data[$name];
}
if ( WPCF7_USE_PIPE
and $pipes instanceof WPCF7_Pipes
and ! $pipes->zero() ) {
if ( is_array( $value_orig ) ) {
$value = array();
foreach ( $value_orig as $v ) {
$value[] = $pipes->do_pipe( $v );
}
} else {
$value = $pipes->do_pipe( $value_orig );
}
}
if ( wpcf7_form_tag_supports( $type, 'selectable-values' ) ) {
$value = (array) $value; $value = (array) $value;
if ( $tag->has_option( 'free_text' ) if ( WPCF7_USE_PIPE ) {
and isset( $posted_data[$name . '_free_text'] ) ) { $pipes = $this->contact_form->get_pipes( $tag->name );
$last_val = array_pop( $value );
list( $tied_item ) = array_slice( $value = array_map( static function ( $value ) use ( $pipes ) {
WPCF7_USE_PIPE ? $tag->pipes->collect_afters() : $tag->values, return $pipes->do_pipe( $value );
-1, 1 }, $value );
);
list( $last_val, $tied_item ) = array_map(
static function ( $item ) {
return wpcf7_canonicalize( $item, array(
'strto' => 'as-is',
) );
},
array( $last_val, $tied_item )
);
if ( $last_val === $tied_item ) {
$value[] = sprintf( '%s %s',
$last_val,
$posted_data[$name . '_free_text']
);
} else {
$value[] = $last_val;
}
unset( $posted_data[$name . '_free_text'] );
} }
} }
$value = apply_filters( "wpcf7_posted_data_{$type}", $value, $value = apply_filters( "wpcf7_posted_data_{$tag->type}",
$value_orig, $tag $value,
$value_orig,
$tag
); );
$posted_data[$name] = $value; $posted_data[$tag->name] = $value;
if ( $tag->has_option( 'consent_for:storage' ) if ( $tag->has_option( 'consent_for:storage' ) and empty( $value ) ) {
and empty( $posted_data[$name] ) ) {
$this->meta['do_not_store'] = true; $this->meta['do_not_store'] = true;
} }
} }
@@ -595,8 +533,7 @@ class WPCF7_Submission {
$home_url = untrailingslashit( home_url() ); $home_url = untrailingslashit( home_url() );
if ( self::is_restful() ) { if ( self::is_restful() ) {
$referer = isset( $_SERVER['HTTP_REFERER'] ) $referer = trim( $_SERVER['HTTP_REFERER'] ?? '' );
? trim( $_SERVER['HTTP_REFERER'] ) : '';
if ( $referer if ( $referer
and 0 === strpos( $referer, $home_url ) ) { and 0 === strpos( $referer, $home_url ) ) {
@@ -758,7 +695,7 @@ class WPCF7_Submission {
return true; return true;
} }
$nonce = isset( $_POST['_wpnonce'] ) ? $_POST['_wpnonce'] : ''; $nonce = $_POST['_wpnonce'] ?? '';
return wpcf7_verify_nonce( $nonce ); return wpcf7_verify_nonce( $nonce );
} }

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,191 @@
<?php
namespace Contactable\SWV;
use WP_Error;
/**
* The base class of SWV rules.
*/
abstract class Rule {
protected $properties = array();
public function __construct( $properties = '' ) {
$this->properties = wp_parse_args( $properties, array() );
}
/**
* Returns true if this rule matches the given context.
*
* @param array $context Context.
*/
public function matches( $context ) {
$field = $this->get_property( 'field' );
if ( ! empty( $context['field'] ) ) {
if ( $field and ! in_array( $field, (array) $context['field'], true ) ) {
return false;
}
}
return true;
}
/**
* Validates with this rule's logic.
*
* @param array $context Context.
*/
public function validate( $context ) {
return true;
}
/**
* Converts the properties to an array.
*
* @return array Array of properties.
*/
public function to_array() {
$properties = (array) $this->properties;
if ( defined( 'static::rule_name' ) and static::rule_name ) {
$properties = array( 'rule' => static::rule_name ) + $properties;
}
return $properties;
}
/**
* Returns the property value specified by the given property name.
*
* @param string $name Property name.
* @return mixed Property value.
*/
public function get_property( $name ) {
if ( isset( $this->properties[$name] ) ) {
return $this->properties[$name];
}
}
/**
* Returns the default user input value from $_POST.
*
* @return mixed Default user input value.
*/
public function get_default_input() {
$field = $this->get_property( 'field' );
if ( isset( $_POST[$field] ) ) {
return wp_unslash( $_POST[$field] );
}
return '';
}
/**
* Creates an error object. Returns false if the error property is omitted.
*/
protected function create_error() {
$error_code = defined( 'static::rule_name' )
? sprintf( 'swv_%s', static::rule_name )
: 'swv';
return new WP_Error(
$error_code,
(string) $this->get_property( 'error' ),
$this
);
}
}
/**
* The base class of SWV composite rules.
*/
abstract class CompositeRule extends Rule {
protected $rules = array();
/**
* Adds a sub-rule to this composite rule.
*
* @param Rule $rule Sub-rule to be added.
*/
public function add_rule( $rule ) {
if ( $rule instanceof Rule ) {
$this->rules[] = $rule;
}
}
/**
* Returns an iterator of sub-rules.
*/
public function rules() {
foreach ( $this->rules as $rule ) {
yield $rule;
}
}
/**
* Returns true if this rule matches the given context.
*
* @param array $context Context.
*/
public function matches( $context ) {
return true;
}
/**
* Validates with this rule's logic.
*
* @param array $context Context.
*/
public function validate( $context ) {
foreach ( $this->rules() as $rule ) {
if ( $rule->matches( $context ) ) {
$result = $rule->validate( $context );
if ( is_wp_error( $result ) ) {
return $result;
}
}
}
return true;
}
/**
* Converts the properties to an array.
*
* @return array Array of properties.
*/
public function to_array() {
$rules_arrays = array_map(
static function ( $rule ) {
return $rule->to_array();
},
$this->rules
);
return array_merge(
parent::to_array(),
array(
'rules' => $rules_arrays,
)
);
}
}

View File

@@ -0,0 +1,35 @@
<?php
namespace Contactable\SWV;
class AllRule extends CompositeRule {
const rule_name = 'all';
public function matches( $context ) {
if ( false === parent::matches( $context ) ) {
return false;
}
return true;
}
public function validate( $context ) {
foreach ( $this->rules() as $rule ) {
if ( $rule->matches( $context ) ) {
$result = $rule->validate( $context );
if ( is_wp_error( $result ) ) {
if ( $result->get_error_message() ) {
return $result;
} else {
return $this->create_error();
}
}
}
}
return true;
}
}

View File

@@ -0,0 +1,31 @@
<?php
namespace Contactable\SWV;
class AnyRule extends CompositeRule {
const rule_name = 'any';
public function matches( $context ) {
if ( false === parent::matches( $context ) ) {
return false;
}
return true;
}
public function validate( $context ) {
foreach ( $this->rules() as $rule ) {
if ( $rule->matches( $context ) ) {
$result = $rule->validate( $context );
if ( ! is_wp_error( $result ) ) {
return true;
}
}
}
return $this->create_error();
}
}

View File

@@ -1,6 +1,8 @@
<?php <?php
class WPCF7_SWV_DateRule extends WPCF7_SWV_Rule { namespace Contactable\SWV;
class DateRule extends Rule {
const rule_name = 'date'; const rule_name = 'date';
@@ -17,16 +19,13 @@ class WPCF7_SWV_DateRule extends WPCF7_SWV_Rule {
} }
public function validate( $context ) { public function validate( $context ) {
$field = $this->get_property( 'field' ); $input = $this->get_default_input();
$input = isset( $_POST[$field] ) ? $_POST[$field] : '';
$input = wpcf7_array_flatten( $input ); $input = wpcf7_array_flatten( $input );
$input = wpcf7_exclude_blank( $input ); $input = wpcf7_exclude_blank( $input );
foreach ( $input as $i ) { foreach ( $input as $i ) {
if ( ! wpcf7_is_date( $i ) ) { if ( ! wpcf7_is_date( $i ) ) {
return new WP_Error( 'wpcf7_invalid_date', return $this->create_error();
$this->get_property( 'error' )
);
} }
} }

View File

@@ -1,6 +1,8 @@
<?php <?php
class WPCF7_SWV_DayofweekRule extends WPCF7_SWV_Rule { namespace Contactable\SWV;
class DayofweekRule extends Rule {
const rule_name = 'dayofweek'; const rule_name = 'dayofweek';
@@ -17,10 +19,7 @@ class WPCF7_SWV_DayofweekRule extends WPCF7_SWV_Rule {
} }
public function validate( $context ) { public function validate( $context ) {
$field = $this->get_property( 'field' ); $input = $this->get_default_input();
$input = isset( $_POST[$field] ) ? $_POST[$field] : '';
$input = wpcf7_array_flatten( $input ); $input = wpcf7_array_flatten( $input );
$input = wpcf7_exclude_blank( $input ); $input = wpcf7_exclude_blank( $input );
@@ -35,9 +34,7 @@ class WPCF7_SWV_DayofweekRule extends WPCF7_SWV_Rule {
$dow = (int) $datetime->format( 'N' ); $dow = (int) $datetime->format( 'N' );
if ( ! in_array( $dow, $acceptable_values, true ) ) { if ( ! in_array( $dow, $acceptable_values, true ) ) {
return new WP_Error( 'wpcf7_invalid_dayofweek', return $this->create_error();
$this->get_property( 'error' )
);
} }
} }
} }

View File

@@ -1,6 +1,8 @@
<?php <?php
class WPCF7_SWV_EmailRule extends WPCF7_SWV_Rule { namespace Contactable\SWV;
class EmailRule extends Rule {
const rule_name = 'email'; const rule_name = 'email';
@@ -17,16 +19,13 @@ class WPCF7_SWV_EmailRule extends WPCF7_SWV_Rule {
} }
public function validate( $context ) { public function validate( $context ) {
$field = $this->get_property( 'field' ); $input = $this->get_default_input();
$input = isset( $_POST[$field] ) ? $_POST[$field] : '';
$input = wpcf7_array_flatten( $input ); $input = wpcf7_array_flatten( $input );
$input = wpcf7_exclude_blank( $input ); $input = wpcf7_exclude_blank( $input );
foreach ( $input as $i ) { foreach ( $input as $i ) {
if ( ! wpcf7_is_email( $i ) ) { if ( ! wpcf7_is_email( $i ) ) {
return new WP_Error( 'wpcf7_invalid_email', return $this->create_error();
$this->get_property( 'error' )
);
} }
} }

View File

@@ -1,6 +1,8 @@
<?php <?php
class WPCF7_SWV_EnumRule extends WPCF7_SWV_Rule { namespace Contactable\SWV;
class EnumRule extends Rule {
const rule_name = 'enum'; const rule_name = 'enum';
@@ -17,23 +19,23 @@ class WPCF7_SWV_EnumRule extends WPCF7_SWV_Rule {
} }
public function validate( $context ) { public function validate( $context ) {
$field = $this->get_property( 'field' ); $input = $this->get_default_input();
$input = isset( $_POST[$field] ) ? $_POST[$field] : '';
$input = wpcf7_array_flatten( $input ); $input = wpcf7_array_flatten( $input );
$input = wpcf7_exclude_blank( $input ); $input = wpcf7_exclude_blank( $input );
$acceptable_values = (array) $this->get_property( 'accept' ); $acceptable_values = (array) $this->get_property( 'accept' );
$acceptable_values = array_map( 'strval', $acceptable_values ); $acceptable_values = array_map( 'strval', $acceptable_values );
$acceptable_values = array_filter( $acceptable_values );
$acceptable_values = array_unique( $acceptable_values ); $acceptable_values = array_unique( $acceptable_values );
$acceptable_values = array_filter( $acceptable_values,
static function ( $val ) {
return '' !== $val;
}
);
foreach ( $input as $i ) { foreach ( $input as $i ) {
if ( ! in_array( $i, $acceptable_values, true ) ) { if ( ! in_array( $i, $acceptable_values, true ) ) {
return new WP_Error( 'wpcf7_invalid_enum', return $this->create_error();
$this->get_property( 'error' )
);
} }
} }

View File

@@ -1,6 +1,8 @@
<?php <?php
class WPCF7_SWV_FileRule extends WPCF7_SWV_Rule { namespace Contactable\SWV;
class FileRule extends Rule {
const rule_name = 'file'; const rule_name = 'file';
@@ -18,7 +20,7 @@ class WPCF7_SWV_FileRule extends WPCF7_SWV_Rule {
public function validate( $context ) { public function validate( $context ) {
$field = $this->get_property( 'field' ); $field = $this->get_property( 'field' );
$input = isset( $_FILES[$field]['name'] ) ? $_FILES[$field]['name'] : ''; $input = $_FILES[$field]['name'] ?? '';
$input = wpcf7_array_flatten( $input ); $input = wpcf7_array_flatten( $input );
$input = wpcf7_exclude_blank( $input ); $input = wpcf7_exclude_blank( $input );
@@ -43,17 +45,13 @@ class WPCF7_SWV_FileRule extends WPCF7_SWV_Rule {
$last_period_pos = strrpos( $i, '.' ); $last_period_pos = strrpos( $i, '.' );
if ( false === $last_period_pos ) { // no period if ( false === $last_period_pos ) { // no period
return new WP_Error( 'wpcf7_invalid_file', return $this->create_error();
$this->get_property( 'error' )
);
} }
$suffix = strtolower( substr( $i, $last_period_pos ) ); $suffix = strtolower( substr( $i, $last_period_pos ) );
if ( ! in_array( $suffix, $acceptable_filetypes, true ) ) { if ( ! in_array( $suffix, $acceptable_filetypes, true ) ) {
return new WP_Error( 'wpcf7_invalid_file', return $this->create_error();
$this->get_property( 'error' )
);
} }
} }

View File

@@ -1,6 +1,8 @@
<?php <?php
class WPCF7_SWV_MaxDateRule extends WPCF7_SWV_Rule { namespace Contactable\SWV;
class MaxDateRule extends Rule {
const rule_name = 'maxdate'; const rule_name = 'maxdate';
@@ -17,8 +19,7 @@ class WPCF7_SWV_MaxDateRule extends WPCF7_SWV_Rule {
} }
public function validate( $context ) { public function validate( $context ) {
$field = $this->get_property( 'field' ); $input = $this->get_default_input();
$input = isset( $_POST[$field] ) ? $_POST[$field] : '';
$input = wpcf7_array_flatten( $input ); $input = wpcf7_array_flatten( $input );
$input = wpcf7_exclude_blank( $input ); $input = wpcf7_exclude_blank( $input );
@@ -30,9 +31,7 @@ class WPCF7_SWV_MaxDateRule extends WPCF7_SWV_Rule {
foreach ( $input as $i ) { foreach ( $input as $i ) {
if ( wpcf7_is_date( $i ) and $threshold < $i ) { if ( wpcf7_is_date( $i ) and $threshold < $i ) {
return new WP_Error( 'wpcf7_invalid_maxdate', return $this->create_error();
$this->get_property( 'error' )
);
} }
} }

View File

@@ -1,6 +1,8 @@
<?php <?php
class WPCF7_SWV_MaxFileSizeRule extends WPCF7_SWV_Rule { namespace Contactable\SWV;
class MaxFileSizeRule extends Rule {
const rule_name = 'maxfilesize'; const rule_name = 'maxfilesize';
@@ -18,7 +20,7 @@ class WPCF7_SWV_MaxFileSizeRule extends WPCF7_SWV_Rule {
public function validate( $context ) { public function validate( $context ) {
$field = $this->get_property( 'field' ); $field = $this->get_property( 'field' );
$input = isset( $_FILES[$field]['size'] ) ? $_FILES[$field]['size'] : ''; $input = $_FILES[$field]['size'] ?? '';
$input = wpcf7_array_flatten( $input ); $input = wpcf7_array_flatten( $input );
$input = wpcf7_exclude_blank( $input ); $input = wpcf7_exclude_blank( $input );
@@ -29,9 +31,7 @@ class WPCF7_SWV_MaxFileSizeRule extends WPCF7_SWV_Rule {
$threshold = $this->get_property( 'threshold' ); $threshold = $this->get_property( 'threshold' );
if ( $threshold < array_sum( $input ) ) { if ( $threshold < array_sum( $input ) ) {
return new WP_Error( 'wpcf7_invalid_maxfilesize', return $this->create_error();
$this->get_property( 'error' )
);
} }
return true; return true;

View File

@@ -1,6 +1,8 @@
<?php <?php
class WPCF7_SWV_MaxItemsRule extends WPCF7_SWV_Rule { namespace Contactable\SWV;
class MaxItemsRule extends Rule {
const rule_name = 'maxitems'; const rule_name = 'maxitems';
@@ -17,8 +19,7 @@ class WPCF7_SWV_MaxItemsRule extends WPCF7_SWV_Rule {
} }
public function validate( $context ) { public function validate( $context ) {
$field = $this->get_property( 'field' ); $input = $this->get_default_input();
$input = isset( $_POST[$field] ) ? $_POST[$field] : '';
$input = wpcf7_array_flatten( $input ); $input = wpcf7_array_flatten( $input );
$input = wpcf7_exclude_blank( $input ); $input = wpcf7_exclude_blank( $input );
@@ -29,9 +30,7 @@ class WPCF7_SWV_MaxItemsRule extends WPCF7_SWV_Rule {
} }
if ( (int) $threshold < count( $input ) ) { if ( (int) $threshold < count( $input ) ) {
return new WP_Error( 'wpcf7_invalid_maxitems', return $this->create_error();
$this->get_property( 'error' )
);
} }
return true; return true;

View File

@@ -1,6 +1,8 @@
<?php <?php
class WPCF7_SWV_MaxLengthRule extends WPCF7_SWV_Rule { namespace Contactable\SWV;
class MaxLengthRule extends Rule {
const rule_name = 'maxlength'; const rule_name = 'maxlength';
@@ -17,8 +19,7 @@ class WPCF7_SWV_MaxLengthRule extends WPCF7_SWV_Rule {
} }
public function validate( $context ) { public function validate( $context ) {
$field = $this->get_property( 'field' ); $input = $this->get_default_input();
$input = isset( $_POST[$field] ) ? $_POST[$field] : '';
$input = wpcf7_array_flatten( $input ); $input = wpcf7_array_flatten( $input );
$input = wpcf7_exclude_blank( $input ); $input = wpcf7_exclude_blank( $input );
@@ -37,9 +38,7 @@ class WPCF7_SWV_MaxLengthRule extends WPCF7_SWV_Rule {
if ( $total <= $threshold ) { if ( $total <= $threshold ) {
return true; return true;
} else { } else {
return new WP_Error( 'wpcf7_invalid_maxlength', return $this->create_error();
$this->get_property( 'error' )
);
} }
} }

View File

@@ -1,6 +1,8 @@
<?php <?php
class WPCF7_SWV_MaxNumberRule extends WPCF7_SWV_Rule { namespace Contactable\SWV;
class MaxNumberRule extends Rule {
const rule_name = 'maxnumber'; const rule_name = 'maxnumber';
@@ -17,8 +19,7 @@ class WPCF7_SWV_MaxNumberRule extends WPCF7_SWV_Rule {
} }
public function validate( $context ) { public function validate( $context ) {
$field = $this->get_property( 'field' ); $input = $this->get_default_input();
$input = isset( $_POST[$field] ) ? $_POST[$field] : '';
$input = wpcf7_array_flatten( $input ); $input = wpcf7_array_flatten( $input );
$input = wpcf7_exclude_blank( $input ); $input = wpcf7_exclude_blank( $input );
@@ -30,9 +31,7 @@ class WPCF7_SWV_MaxNumberRule extends WPCF7_SWV_Rule {
foreach ( $input as $i ) { foreach ( $input as $i ) {
if ( wpcf7_is_number( $i ) and (float) $threshold < (float) $i ) { if ( wpcf7_is_number( $i ) and (float) $threshold < (float) $i ) {
return new WP_Error( 'wpcf7_invalid_maxnumber', return $this->create_error();
$this->get_property( 'error' )
);
} }
} }

View File

@@ -1,6 +1,8 @@
<?php <?php
class WPCF7_SWV_MinDateRule extends WPCF7_SWV_Rule { namespace Contactable\SWV;
class MinDateRule extends Rule {
const rule_name = 'mindate'; const rule_name = 'mindate';
@@ -17,8 +19,7 @@ class WPCF7_SWV_MinDateRule extends WPCF7_SWV_Rule {
} }
public function validate( $context ) { public function validate( $context ) {
$field = $this->get_property( 'field' ); $input = $this->get_default_input();
$input = isset( $_POST[$field] ) ? $_POST[$field] : '';
$input = wpcf7_array_flatten( $input ); $input = wpcf7_array_flatten( $input );
$input = wpcf7_exclude_blank( $input ); $input = wpcf7_exclude_blank( $input );
@@ -30,9 +31,7 @@ class WPCF7_SWV_MinDateRule extends WPCF7_SWV_Rule {
foreach ( $input as $i ) { foreach ( $input as $i ) {
if ( wpcf7_is_date( $i ) and $i < $threshold ) { if ( wpcf7_is_date( $i ) and $i < $threshold ) {
return new WP_Error( 'wpcf7_invalid_mindate', return $this->create_error();
$this->get_property( 'error' )
);
} }
} }

View File

@@ -1,6 +1,8 @@
<?php <?php
class WPCF7_SWV_MinFileSizeRule extends WPCF7_SWV_Rule { namespace Contactable\SWV;
class MinFileSizeRule extends Rule {
const rule_name = 'minfilesize'; const rule_name = 'minfilesize';
@@ -18,7 +20,7 @@ class WPCF7_SWV_MinFileSizeRule extends WPCF7_SWV_Rule {
public function validate( $context ) { public function validate( $context ) {
$field = $this->get_property( 'field' ); $field = $this->get_property( 'field' );
$input = isset( $_FILES[$field]['size'] ) ? $_FILES[$field]['size'] : ''; $input = $_FILES[$field]['size'] ?? '';
$input = wpcf7_array_flatten( $input ); $input = wpcf7_array_flatten( $input );
$input = wpcf7_exclude_blank( $input ); $input = wpcf7_exclude_blank( $input );
@@ -29,9 +31,7 @@ class WPCF7_SWV_MinFileSizeRule extends WPCF7_SWV_Rule {
$threshold = $this->get_property( 'threshold' ); $threshold = $this->get_property( 'threshold' );
if ( array_sum( $input ) < $threshold ) { if ( array_sum( $input ) < $threshold ) {
return new WP_Error( 'wpcf7_invalid_minfilesize', return $this->create_error();
$this->get_property( 'error' )
);
} }
return true; return true;

View File

@@ -1,6 +1,8 @@
<?php <?php
class WPCF7_SWV_MinItemsRule extends WPCF7_SWV_Rule { namespace Contactable\SWV;
class MinItemsRule extends Rule {
const rule_name = 'minitems'; const rule_name = 'minitems';
@@ -17,8 +19,7 @@ class WPCF7_SWV_MinItemsRule extends WPCF7_SWV_Rule {
} }
public function validate( $context ) { public function validate( $context ) {
$field = $this->get_property( 'field' ); $input = $this->get_default_input();
$input = isset( $_POST[$field] ) ? $_POST[$field] : '';
$input = wpcf7_array_flatten( $input ); $input = wpcf7_array_flatten( $input );
$input = wpcf7_exclude_blank( $input ); $input = wpcf7_exclude_blank( $input );
@@ -29,9 +30,7 @@ class WPCF7_SWV_MinItemsRule extends WPCF7_SWV_Rule {
} }
if ( count( $input ) < (int) $threshold ) { if ( count( $input ) < (int) $threshold ) {
return new WP_Error( 'wpcf7_invalid_minitems', return $this->create_error();
$this->get_property( 'error' )
);
} }
return true; return true;

View File

@@ -1,6 +1,8 @@
<?php <?php
class WPCF7_SWV_MinLengthRule extends WPCF7_SWV_Rule { namespace Contactable\SWV;
class MinLengthRule extends Rule {
const rule_name = 'minlength'; const rule_name = 'minlength';
@@ -17,8 +19,7 @@ class WPCF7_SWV_MinLengthRule extends WPCF7_SWV_Rule {
} }
public function validate( $context ) { public function validate( $context ) {
$field = $this->get_property( 'field' ); $input = $this->get_default_input();
$input = isset( $_POST[$field] ) ? $_POST[$field] : '';
$input = wpcf7_array_flatten( $input ); $input = wpcf7_array_flatten( $input );
$input = wpcf7_exclude_blank( $input ); $input = wpcf7_exclude_blank( $input );
@@ -37,9 +38,7 @@ class WPCF7_SWV_MinLengthRule extends WPCF7_SWV_Rule {
if ( $threshold <= $total ) { if ( $threshold <= $total ) {
return true; return true;
} else { } else {
return new WP_Error( 'wpcf7_invalid_minlength', return $this->create_error();
$this->get_property( 'error' )
);
} }
} }

View File

@@ -1,6 +1,8 @@
<?php <?php
class WPCF7_SWV_MinNumberRule extends WPCF7_SWV_Rule { namespace Contactable\SWV;
class MinNumberRule extends Rule {
const rule_name = 'minnumber'; const rule_name = 'minnumber';
@@ -17,8 +19,7 @@ class WPCF7_SWV_MinNumberRule extends WPCF7_SWV_Rule {
} }
public function validate( $context ) { public function validate( $context ) {
$field = $this->get_property( 'field' ); $input = $this->get_default_input();
$input = isset( $_POST[$field] ) ? $_POST[$field] : '';
$input = wpcf7_array_flatten( $input ); $input = wpcf7_array_flatten( $input );
$input = wpcf7_exclude_blank( $input ); $input = wpcf7_exclude_blank( $input );
@@ -30,9 +31,7 @@ class WPCF7_SWV_MinNumberRule extends WPCF7_SWV_Rule {
foreach ( $input as $i ) { foreach ( $input as $i ) {
if ( wpcf7_is_number( $i ) and (float) $i < (float) $threshold ) { if ( wpcf7_is_number( $i ) and (float) $i < (float) $threshold ) {
return new WP_Error( 'wpcf7_invalid_minnumber', return $this->create_error();
$this->get_property( 'error' )
);
} }
} }

View File

@@ -1,6 +1,8 @@
<?php <?php
class WPCF7_SWV_NumberRule extends WPCF7_SWV_Rule { namespace Contactable\SWV;
class NumberRule extends Rule {
const rule_name = 'number'; const rule_name = 'number';
@@ -17,16 +19,13 @@ class WPCF7_SWV_NumberRule extends WPCF7_SWV_Rule {
} }
public function validate( $context ) { public function validate( $context ) {
$field = $this->get_property( 'field' ); $input = $this->get_default_input();
$input = isset( $_POST[$field] ) ? $_POST[$field] : '';
$input = wpcf7_array_flatten( $input ); $input = wpcf7_array_flatten( $input );
$input = wpcf7_exclude_blank( $input ); $input = wpcf7_exclude_blank( $input );
foreach ( $input as $i ) { foreach ( $input as $i ) {
if ( ! wpcf7_is_number( $i ) ) { if ( ! wpcf7_is_number( $i ) ) {
return new WP_Error( 'wpcf7_invalid_number', return $this->create_error();
$this->get_property( 'error' )
);
} }
} }

View File

@@ -1,6 +1,8 @@
<?php <?php
class WPCF7_SWV_RequiredRule extends WPCF7_SWV_Rule { namespace Contactable\SWV;
class RequiredRule extends Rule {
const rule_name = 'required'; const rule_name = 'required';
@@ -17,17 +19,12 @@ class WPCF7_SWV_RequiredRule extends WPCF7_SWV_Rule {
} }
public function validate( $context ) { public function validate( $context ) {
$field = $this->get_property( 'field' ); $input = $this->get_default_input();
$input = isset( $_POST[$field] ) ? $_POST[$field] : '';
$input = wpcf7_array_flatten( $input ); $input = wpcf7_array_flatten( $input );
$input = wpcf7_exclude_blank( $input ); $input = wpcf7_exclude_blank( $input );
if ( empty( $input ) ) { if ( empty( $input ) ) {
return new WP_Error( 'wpcf7_invalid_required', return $this->create_error();
$this->get_property( 'error' )
);
} }
return true; return true;

View File

@@ -1,6 +1,8 @@
<?php <?php
class WPCF7_SWV_RequiredFileRule extends WPCF7_SWV_Rule { namespace Contactable\SWV;
class RequiredFileRule extends Rule {
const rule_name = 'requiredfile'; const rule_name = 'requiredfile';
@@ -18,17 +20,12 @@ class WPCF7_SWV_RequiredFileRule extends WPCF7_SWV_Rule {
public function validate( $context ) { public function validate( $context ) {
$field = $this->get_property( 'field' ); $field = $this->get_property( 'field' );
$input = $_FILES[$field]['tmp_name'] ?? '';
$input = isset( $_FILES[$field]['tmp_name'] )
? $_FILES[$field]['tmp_name'] : '';
$input = wpcf7_array_flatten( $input ); $input = wpcf7_array_flatten( $input );
$input = wpcf7_exclude_blank( $input ); $input = wpcf7_exclude_blank( $input );
if ( empty( $input ) ) { if ( empty( $input ) ) {
return new WP_Error( 'wpcf7_invalid_requiredfile', return $this->create_error();
$this->get_property( 'error' )
);
} }
return true; return true;

View File

@@ -1,6 +1,8 @@
<?php <?php
class WPCF7_SWV_TelRule extends WPCF7_SWV_Rule { namespace Contactable\SWV;
class TelRule extends Rule {
const rule_name = 'tel'; const rule_name = 'tel';
@@ -17,16 +19,13 @@ class WPCF7_SWV_TelRule extends WPCF7_SWV_Rule {
} }
public function validate( $context ) { public function validate( $context ) {
$field = $this->get_property( 'field' ); $input = $this->get_default_input();
$input = isset( $_POST[$field] ) ? $_POST[$field] : '';
$input = wpcf7_array_flatten( $input ); $input = wpcf7_array_flatten( $input );
$input = wpcf7_exclude_blank( $input ); $input = wpcf7_exclude_blank( $input );
foreach ( $input as $i ) { foreach ( $input as $i ) {
if ( ! wpcf7_is_tel( $i ) ) { if ( ! wpcf7_is_tel( $i ) ) {
return new WP_Error( 'wpcf7_invalid_tel', return $this->create_error();
$this->get_property( 'error' )
);
} }
} }

View File

@@ -1,6 +1,8 @@
<?php <?php
class WPCF7_SWV_TimeRule extends WPCF7_SWV_Rule { namespace Contactable\SWV;
class TimeRule extends Rule {
const rule_name = 'time'; const rule_name = 'time';
@@ -17,16 +19,13 @@ class WPCF7_SWV_TimeRule extends WPCF7_SWV_Rule {
} }
public function validate( $context ) { public function validate( $context ) {
$field = $this->get_property( 'field' ); $input = $this->get_default_input();
$input = isset( $_POST[$field] ) ? $_POST[$field] : '';
$input = wpcf7_array_flatten( $input ); $input = wpcf7_array_flatten( $input );
$input = wpcf7_exclude_blank( $input ); $input = wpcf7_exclude_blank( $input );
foreach ( $input as $i ) { foreach ( $input as $i ) {
if ( ! wpcf7_is_time( $i ) ) { if ( ! wpcf7_is_time( $i ) ) {
return new WP_Error( 'wpcf7_invalid_time', return $this->create_error();
$this->get_property( 'error' )
);
} }
} }

View File

@@ -1,6 +1,8 @@
<?php <?php
class WPCF7_SWV_URLRule extends WPCF7_SWV_Rule { namespace Contactable\SWV;
class URLRule extends Rule {
const rule_name = 'url'; const rule_name = 'url';
@@ -17,16 +19,13 @@ class WPCF7_SWV_URLRule extends WPCF7_SWV_Rule {
} }
public function validate( $context ) { public function validate( $context ) {
$field = $this->get_property( 'field' ); $input = $this->get_default_input();
$input = isset( $_POST[$field] ) ? $_POST[$field] : '';
$input = wpcf7_array_flatten( $input ); $input = wpcf7_array_flatten( $input );
$input = wpcf7_exclude_blank( $input ); $input = wpcf7_exclude_blank( $input );
foreach ( $input as $i ) { foreach ( $input as $i ) {
if ( ! wpcf7_is_url( $i ) ) { if ( ! wpcf7_is_url( $i ) ) {
return new WP_Error( 'wpcf7_invalid_url', return $this->create_error();
$this->get_property( 'error' )
);
} }
} }

View File

@@ -29,29 +29,18 @@ trait WPCF7_SWV_SchemaHolder {
* Validates form inputs based on the schema and given context. * Validates form inputs based on the schema and given context.
*/ */
public function validate_schema( $context, WPCF7_Validation $validity ) { public function validate_schema( $context, WPCF7_Validation $validity ) {
$callback = static function ( $rule ) use ( &$callback, $context, $validity ) { $schema = $this->get_schema();
if ( ! $rule->matches( $context ) ) {
return;
}
if ( $rule instanceof WPCF7_SWV_CompositeRule ) { foreach ( $schema->validate( $context ) as $result ) {
foreach ( $rule->rules() as $child_rule ) { if ( is_wp_error( $result ) ) {
call_user_func( $callback, $child_rule ); $rule = $result->get_error_data();
}
} else {
$field = $rule->get_property( 'field' ); $field = $rule->get_property( 'field' );
if ( $validity->is_valid( $field ) ) { if ( isset( $field ) and $validity->is_valid( $field ) ) {
$result = $rule->validate( $context ); $validity->invalidate( $field, $result );
if ( is_wp_error( $result ) ) {
$validity->invalidate( $field, $result );
}
} }
} }
}; }
call_user_func( $callback, $this->get_schema() );
} }
} }

View File

@@ -19,7 +19,7 @@ add_action(
wpcf7_plugin_url( 'includes/swv/js/index.js' ), wpcf7_plugin_url( 'includes/swv/js/index.js' ),
$assets['dependencies'], $assets['dependencies'],
$assets['version'], $assets['version'],
true array( 'in_footer' => true )
); );
}, },
10, 0 10, 0

View File

@@ -5,6 +5,7 @@
require_once WPCF7_PLUGIN_DIR . '/includes/swv/schema-holder.php'; require_once WPCF7_PLUGIN_DIR . '/includes/swv/schema-holder.php';
require_once WPCF7_PLUGIN_DIR . '/includes/swv/script-loader.php'; require_once WPCF7_PLUGIN_DIR . '/includes/swv/script-loader.php';
require_once WPCF7_PLUGIN_DIR . '/includes/swv/php/abstract-rules.php';
/** /**
@@ -12,27 +13,29 @@ require_once WPCF7_PLUGIN_DIR . '/includes/swv/script-loader.php';
*/ */
function wpcf7_swv_available_rules() { function wpcf7_swv_available_rules() {
$rules = array( $rules = array(
'required' => 'WPCF7_SWV_RequiredRule', 'required' => 'Contactable\SWV\RequiredRule',
'requiredfile' => 'WPCF7_SWV_RequiredFileRule', 'requiredfile' => 'Contactable\SWV\RequiredFileRule',
'email' => 'WPCF7_SWV_EmailRule', 'email' => 'Contactable\SWV\EmailRule',
'url' => 'WPCF7_SWV_URLRule', 'url' => 'Contactable\SWV\URLRule',
'tel' => 'WPCF7_SWV_TelRule', 'tel' => 'Contactable\SWV\TelRule',
'number' => 'WPCF7_SWV_NumberRule', 'number' => 'Contactable\SWV\NumberRule',
'date' => 'WPCF7_SWV_DateRule', 'date' => 'Contactable\SWV\DateRule',
'time' => 'WPCF7_SWV_TimeRule', 'time' => 'Contactable\SWV\TimeRule',
'file' => 'WPCF7_SWV_FileRule', 'file' => 'Contactable\SWV\FileRule',
'enum' => 'WPCF7_SWV_EnumRule', 'enum' => 'Contactable\SWV\EnumRule',
'dayofweek' => 'WPCF7_SWV_DayofweekRule', 'dayofweek' => 'Contactable\SWV\DayofweekRule',
'minitems' => 'WPCF7_SWV_MinItemsRule', 'minitems' => 'Contactable\SWV\MinItemsRule',
'maxitems' => 'WPCF7_SWV_MaxItemsRule', 'maxitems' => 'Contactable\SWV\MaxItemsRule',
'minlength' => 'WPCF7_SWV_MinLengthRule', 'minlength' => 'Contactable\SWV\MinLengthRule',
'maxlength' => 'WPCF7_SWV_MaxLengthRule', 'maxlength' => 'Contactable\SWV\MaxLengthRule',
'minnumber' => 'WPCF7_SWV_MinNumberRule', 'minnumber' => 'Contactable\SWV\MinNumberRule',
'maxnumber' => 'WPCF7_SWV_MaxNumberRule', 'maxnumber' => 'Contactable\SWV\MaxNumberRule',
'mindate' => 'WPCF7_SWV_MinDateRule', 'mindate' => 'Contactable\SWV\MinDateRule',
'maxdate' => 'WPCF7_SWV_MaxDateRule', 'maxdate' => 'Contactable\SWV\MaxDateRule',
'minfilesize' => 'WPCF7_SWV_MinFileSizeRule', 'minfilesize' => 'Contactable\SWV\MinFileSizeRule',
'maxfilesize' => 'WPCF7_SWV_MaxFileSizeRule', 'maxfilesize' => 'Contactable\SWV\MaxFileSizeRule',
'all' => 'Contactable\SWV\AllRule',
'any' => 'Contactable\SWV\AnyRule',
); );
return apply_filters( 'wpcf7_swv_available_rules', $rules ); return apply_filters( 'wpcf7_swv_available_rules', $rules );
@@ -49,7 +52,7 @@ function wpcf7_swv_load_rules() {
foreach ( array_keys( $rules ) as $rule ) { foreach ( array_keys( $rules ) as $rule ) {
$file = sprintf( '%s.php', $rule ); $file = sprintf( '%s.php', $rule );
$path = path_join( WPCF7_PLUGIN_DIR . '/includes/swv/rules', $file ); $path = path_join( WPCF7_PLUGIN_DIR . '/includes/swv/php/rules', $file );
if ( file_exists( $path ) ) { if ( file_exists( $path ) ) {
include_once $path; include_once $path;
@@ -63,7 +66,7 @@ function wpcf7_swv_load_rules() {
* *
* @param string $rule_name Rule name. * @param string $rule_name Rule name.
* @param string|array $properties Optional. Rule properties. * @param string|array $properties Optional. Rule properties.
* @return WPCF7_SWV_Rule|null The rule object, or null if it failed. * @return Rule|null The rule object, or null if it failed.
*/ */
function wpcf7_swv_create_rule( $rule_name, $properties = '' ) { function wpcf7_swv_create_rule( $rule_name, $properties = '' ) {
$rules = wpcf7_swv_available_rules(); $rules = wpcf7_swv_available_rules();
@@ -124,172 +127,38 @@ function wpcf7_swv_get_meta_schema() {
} }
/**
* The base class of SWV rules.
*/
abstract class WPCF7_SWV_Rule {
protected $properties = array();
public function __construct( $properties = '' ) {
$this->properties = wp_parse_args( $properties, array() );
}
/**
* Returns true if this rule matches the given context.
*
* @param array $context Context.
*/
public function matches( $context ) {
$field = $this->get_property( 'field' );
if ( ! empty( $context['field'] ) ) {
if ( $field and ! in_array( $field, (array) $context['field'], true ) ) {
return false;
}
}
return true;
}
/**
* Validates with this rule's logic.
*
* @param array $context Context.
*/
public function validate( $context ) {
return true;
}
/**
* Converts the properties to an array.
*
* @return array Array of properties.
*/
public function to_array() {
$properties = (array) $this->properties;
if ( defined( 'static::rule_name' ) and static::rule_name ) {
$properties = array( 'rule' => static::rule_name ) + $properties;
}
return $properties;
}
/**
* Returns the property value specified by the given property name.
*
* @param string $name Property name.
* @return mixed Property value.
*/
public function get_property( $name ) {
if ( isset( $this->properties[$name] ) ) {
return $this->properties[$name];
}
}
}
/**
* The base class of SWV composite rules.
*/
abstract class WPCF7_SWV_CompositeRule extends WPCF7_SWV_Rule {
protected $rules = array();
/**
* Adds a sub-rule to this composite rule.
*
* @param WPCF7_SWV_Rule $rule Sub-rule to be added.
*/
public function add_rule( $rule ) {
if ( $rule instanceof WPCF7_SWV_Rule ) {
$this->rules[] = $rule;
}
}
/**
* Returns an iterator of sub-rules.
*/
public function rules() {
foreach ( $this->rules as $rule ) {
yield $rule;
}
}
/**
* Returns true if this rule matches the given context.
*
* @param array $context Context.
*/
public function matches( $context ) {
return true;
}
/**
* Validates with this rule's logic.
*
* @param array $context Context.
*/
public function validate( $context ) {
foreach ( $this->rules() as $rule ) {
if ( $rule->matches( $context ) ) {
$result = $rule->validate( $context );
if ( is_wp_error( $result ) ) {
return $result;
}
}
}
return true;
}
/**
* Converts the properties to an array.
*
* @return array Array of properties.
*/
public function to_array() {
$rules_arrays = array_map(
static function ( $rule ) {
return $rule->to_array();
},
$this->rules
);
return array_merge(
parent::to_array(),
array(
'rules' => $rules_arrays,
)
);
}
}
/** /**
* The schema class as a composite rule. * The schema class as a composite rule.
*/ */
class WPCF7_SWV_Schema extends WPCF7_SWV_CompositeRule { class WPCF7_SWV_Schema extends \Contactable\SWV\CompositeRule {
const version = 'Contact Form 7 SWV Schema 2023-07'; /**
* The human-readable version of the schema.
*/
const version = 'Contact Form 7 SWV Schema 2024-02';
/**
* Constructor.
*/
public function __construct( $properties = '' ) { public function __construct( $properties = '' ) {
$this->properties = wp_parse_args( $properties, array( $this->properties = wp_parse_args( $properties, array(
'version' => self::version, 'version' => self::version,
) ); ) );
} }
/**
* Validates with this schema.
*
* @param array $context Context.
*/
public function validate( $context ) {
foreach ( $this->rules() as $rule ) {
if ( $rule->matches( $context ) ) {
yield $rule->validate( $context );
}
}
}
} }

View File

@@ -14,6 +14,7 @@ require_once WPCF7_PLUGIN_DIR . '/includes/contact-form-functions.php';
require_once WPCF7_PLUGIN_DIR . '/includes/contact-form-template.php'; require_once WPCF7_PLUGIN_DIR . '/includes/contact-form-template.php';
require_once WPCF7_PLUGIN_DIR . '/includes/contact-form.php'; require_once WPCF7_PLUGIN_DIR . '/includes/contact-form.php';
require_once WPCF7_PLUGIN_DIR . '/includes/mail.php'; require_once WPCF7_PLUGIN_DIR . '/includes/mail.php';
require_once WPCF7_PLUGIN_DIR . '/includes/mail-tag.php';
require_once WPCF7_PLUGIN_DIR . '/includes/special-mail-tags.php'; require_once WPCF7_PLUGIN_DIR . '/includes/special-mail-tags.php';
require_once WPCF7_PLUGIN_DIR . '/includes/file.php'; require_once WPCF7_PLUGIN_DIR . '/includes/file.php';
require_once WPCF7_PLUGIN_DIR . '/includes/validation-functions.php'; require_once WPCF7_PLUGIN_DIR . '/includes/validation-functions.php';

View File

@@ -12,6 +12,7 @@ function wpcf7_add_form_tag_acceptance() {
'wpcf7_acceptance_form_tag_handler', 'wpcf7_acceptance_form_tag_handler',
array( array(
'name-attr' => true, 'name-attr' => true,
'selectable-values' => true,
) )
); );
} }

View File

@@ -52,8 +52,7 @@ function wpcf7_akismet( $spam, $submission ) {
'blog_charset' => get_option( 'blog_charset' ), 'blog_charset' => get_option( 'blog_charset' ),
'user_ip' => $submission->get_meta( 'remote_ip' ), 'user_ip' => $submission->get_meta( 'remote_ip' ),
'user_agent' => $submission->get_meta( 'user_agent' ), 'user_agent' => $submission->get_meta( 'user_agent' ),
'referrer' => isset( $_SERVER['HTTP_REFERER'] ) 'referrer' => $_SERVER['HTTP_REFERER'] ?? '',
? $_SERVER['HTTP_REFERER'] : '',
); );
$datetime = date_create_immutable( $datetime = date_create_immutable(

View File

@@ -72,11 +72,14 @@ function wpcf7_checkbox_form_tag_handler( $tag ) {
$tag->values = array_merge( $tag->values = array_merge(
array_slice( $tag->values, 0, -1 ), array_slice( $tag->values, 0, -1 ),
array_values( $data ), array_values( $data ),
array_slice( $tag->values, -1 ) ); array_slice( $tag->values, -1 )
);
$tag->labels = array_merge( $tag->labels = array_merge(
array_slice( $tag->labels, 0, -1 ), array_slice( $tag->labels, 0, -1 ),
array_values( $data ), array_values( $data ),
array_slice( $tag->labels, -1 ) ); array_slice( $tag->labels, -1 )
);
} else { } else {
$tag->values = array_merge( $tag->values, array_values( $data ) ); $tag->values = array_merge( $tag->values, array_values( $data ) );
$tag->labels = array_merge( $tag->labels, array_values( $data ) ); $tag->labels = array_merge( $tag->labels, array_values( $data ) );
@@ -148,7 +151,7 @@ function wpcf7_checkbox_form_tag_handler( $tag ) {
$class .= ' last'; $class .= ' last';
if ( $free_text ) { if ( $free_text ) {
$free_text_name = $tag->name . '_free_text'; $free_text_name = sprintf( '_wpcf7_free_text_%s', $tag->name );
$free_text_atts = array( $free_text_atts = array(
'name' => $free_text_name, 'name' => $free_text_name,
@@ -207,6 +210,120 @@ function wpcf7_swv_add_checkbox_rules( $schema, $contact_form ) {
} }
add_action(
'wpcf7_swv_create_schema',
'wpcf7_swv_add_checkbox_enum_rules',
20, 2
);
function wpcf7_swv_add_checkbox_enum_rules( $schema, $contact_form ) {
$tags = $contact_form->scan_form_tags( array(
'basetype' => array( 'checkbox', 'radio' ),
) );
$values = array_reduce(
$tags,
function ( $values, $tag ) {
if ( $tag->has_option( 'free_text' ) ) {
$values[$tag->name] = 'free_text';
}
if (
isset( $values[$tag->name] ) and
! is_array( $values[$tag->name] ) // Maybe 'free_text'
) {
return $values;
}
if ( ! isset( $values[$tag->name] ) ) {
$values[$tag->name] = array();
}
$tag_values = array_merge(
(array) $tag->values,
(array) $tag->get_data_option()
);
$values[$tag->name] = array_merge(
$values[$tag->name],
$tag_values
);
return $values;
},
array()
);
foreach ( $values as $field => $field_values ) {
if ( ! is_array( $field_values ) ) { // Maybe 'free_text'
continue;
}
$field_values = array_map(
static function ( $value ) {
return html_entity_decode(
(string) $value,
ENT_QUOTES | ENT_HTML5,
'UTF-8'
);
},
$field_values
);
$field_values = array_filter(
array_unique( $field_values ),
static function ( $value ) {
return '' !== $value;
}
);
$schema->add_rule(
wpcf7_swv_create_rule( 'enum', array(
'field' => $field,
'accept' => array_values( $field_values ),
'error' => $contact_form->filter_message(
__( "Undefined value was submitted through this field.", 'contact-form-7' )
),
) )
);
}
}
add_filter( 'wpcf7_posted_data_checkbox',
'wpcf7_posted_data_checkbox',
10, 3
);
add_filter( 'wpcf7_posted_data_checkbox*',
'wpcf7_posted_data_checkbox',
10, 3
);
add_filter( 'wpcf7_posted_data_radio',
'wpcf7_posted_data_checkbox',
10, 3
);
function wpcf7_posted_data_checkbox( $value, $value_orig, $form_tag ) {
if ( $form_tag->has_option( 'free_text' ) ) {
$value = (array) $value;
$free_text_name = sprintf( '_wpcf7_free_text_%s', $form_tag->name );
$free_text = wp_unslash( $_POST[$free_text_name] ?? '' );
$last_val = array_pop( $value );
if ( isset( $last_val ) ) {
$last_val = sprintf( '%s %s', $last_val, $free_text );
$value[] = trim( $last_val );
}
}
return $value;
}
/* Tag generator */ /* Tag generator */
add_action( 'wpcf7_admin_init', add_action( 'wpcf7_admin_init',

View File

@@ -14,7 +14,7 @@ wpcf7_include_module_file( 'constant-contact/doi.php' );
add_action( add_action(
'wpcf7_init', 'wpcf7_init',
'wpcf7_constant_contact_register_service', 'wpcf7_constant_contact_register_service',
20, 0 120, 0
); );
/** /**

View File

@@ -113,10 +113,7 @@ class WPCF7_ConstantContact extends WPCF7_Service_OAuth2 {
} }
public function link() { public function link() {
echo sprintf( '<a href="%1$s">%2$s</a>', echo 'constantcontact.com';
'https://constant-contact.evyy.net/c/1293104/205991/3411',
'constantcontact.com'
);
} }
protected function get_redirect_uri() { protected function get_redirect_uri() {
@@ -366,6 +363,15 @@ class WPCF7_ConstantContact extends WPCF7_Service_OAuth2 {
} }
public function display( $action = '' ) { public function display( $action = '' ) {
echo sprintf(
'<p><strong>%1$s</strong> %2$s</p>',
esc_html( __( 'Warning:', 'contact-form-7' ) ),
wpcf7_link(
__( 'https://contactform7.com/2024/02/02/we-end-the-constant-contact-integration/', 'contact-form-7' ),
__( "This feature is deprecated. You are not recommended to use it.", 'contact-form-7' )
)
);
echo sprintf( echo sprintf(
'<p>%s</p>', '<p>%s</p>',
esc_html( __( "The Constant Contact integration module allows you to send contact data collected through your contact forms to the Constant Contact API. You can create reliable email subscription services in a few easy steps.", 'contact-form-7' ) ) esc_html( __( "The Constant Contact integration module allows you to send contact data collected through your contact forms to the Constant Contact API. You can create reliable email subscription services in a few easy steps.", 'contact-form-7' ) )

View File

@@ -98,7 +98,7 @@ add_filter( 'wpcf7_validate_quiz', 'wpcf7_quiz_validation_filter', 10, 2 );
function wpcf7_quiz_validation_filter( $result, $tag ) { function wpcf7_quiz_validation_filter( $result, $tag ) {
$name = $tag->name; $name = $tag->name;
$answer = isset( $_POST[$name] ) ? wp_unslash( $_POST[$name] ) : ''; $answer = wp_unslash( $_POST[$name] ?? '' );
$answer = wpcf7_canonicalize( $answer, array( $answer = wpcf7_canonicalize( $answer, array(
'strip_separators' => true, 'strip_separators' => true,
@@ -106,9 +106,7 @@ function wpcf7_quiz_validation_filter( $result, $tag ) {
$answer_hash = wp_hash( $answer, 'wpcf7_quiz' ); $answer_hash = wp_hash( $answer, 'wpcf7_quiz' );
$expected_hash = isset( $_POST['_wpcf7_quiz_answer_' . $name] ) $expected_hash = (string) ( $_POST['_wpcf7_quiz_answer_' . $name] ?? '' );
? (string) $_POST['_wpcf7_quiz_answer_' . $name]
: '';
if ( ! hash_equals( $expected_hash, $answer_hash ) ) { if ( ! hash_equals( $expected_hash, $answer_hash ) ) {
$result->invalidate( $tag, wpcf7_get_message( 'quiz_answer_not_correct' ) ); $result->invalidate( $tag, wpcf7_get_message( 'quiz_answer_not_correct' ) );
@@ -176,7 +174,7 @@ add_filter( 'wpcf7_mail_tag_replaced_quiz', 'wpcf7_quiz_mail_tag', 10, 4 );
function wpcf7_quiz_mail_tag( $replaced, $submitted, $html, $mail_tag ) { function wpcf7_quiz_mail_tag( $replaced, $submitted, $html, $mail_tag ) {
$field_name = $mail_tag->field_name(); $field_name = $mail_tag->field_name();
$submitted = isset( $_POST[$field_name] ) ? $_POST[$field_name] : ''; $submitted = $_POST[$field_name] ?? '';
$replaced = $submitted; $replaced = $submitted;
if ( $html ) { if ( $html ) {

View File

@@ -167,8 +167,8 @@ function wpcf7_captcha_validation_filter( $result, $tag ) {
$captchac = '_wpcf7_captcha_challenge_' . $name; $captchac = '_wpcf7_captcha_challenge_' . $name;
$prefix = isset( $_POST[$captchac] ) ? (string) $_POST[$captchac] : ''; $prefix = (string) ( $_POST[$captchac] ?? '' );
$response = isset( $_POST[$name] ) ? (string) $_POST[$name] : ''; $response = (string) ( $_POST[$name] ?? '' );
$response = wpcf7_canonicalize( $response ); $response = wpcf7_canonicalize( $response );
if ( 0 === strlen( $prefix ) if ( 0 === strlen( $prefix )

View File

@@ -53,7 +53,7 @@ function wpcf7_recaptcha_enqueue_scripts() {
), ),
array(), array(),
'3.0', '3.0',
true array( 'in_footer' => true )
); );
$assets = array(); $assets = array();
@@ -79,7 +79,7 @@ function wpcf7_recaptcha_enqueue_scripts() {
) )
), ),
$assets['version'], $assets['version'],
true array( 'in_footer' => true )
); );
wp_enqueue_script( 'wpcf7-recaptcha' ); wp_enqueue_script( 'wpcf7-recaptcha' );
@@ -135,8 +135,7 @@ function wpcf7_recaptcha_verify_response( $spam, $submission ) {
return $spam; return $spam;
} }
$token = isset( $_POST['_wpcf7_recaptcha_response'] ) $token = trim( $_POST['_wpcf7_recaptcha_response'] ?? '' );
? trim( $_POST['_wpcf7_recaptcha_response'] ) : '';
if ( $service->verify( $token ) ) { // Human if ( $service->verify( $token ) ) { // Human
$spam = false; $spam = false;

View File

@@ -221,8 +221,8 @@ class WPCF7_RECAPTCHA extends WPCF7_Service {
$this->reset_data(); $this->reset_data();
$redirect_to = $this->menu_page_url( 'action=setup' ); $redirect_to = $this->menu_page_url( 'action=setup' );
} else { } else {
$sitekey = isset( $_POST['sitekey'] ) ? trim( $_POST['sitekey'] ) : ''; $sitekey = trim( $_POST['sitekey'] ?? '' );
$secret = isset( $_POST['secret'] ) ? trim( $_POST['secret'] ) : ''; $secret = trim( $_POST['secret'] ?? '' );
if ( $sitekey and $secret ) { if ( $sitekey and $secret ) {
$this->sitekeys = array( $sitekey => $secret ); $this->sitekeys = array( $sitekey => $secret );

View File

@@ -153,6 +153,75 @@ function wpcf7_swv_add_select_rules( $schema, $contact_form ) {
} }
add_action(
'wpcf7_swv_create_schema',
'wpcf7_swv_add_select_enum_rules',
20, 2
);
function wpcf7_swv_add_select_enum_rules( $schema, $contact_form ) {
$tags = $contact_form->scan_form_tags( array(
'basetype' => array( 'select' ),
) );
$values = array_reduce(
$tags,
function ( $values, $tag ) {
if ( ! isset( $values[$tag->name] ) ) {
$values[$tag->name] = array();
}
$tag_values = array_merge(
(array) $tag->values,
(array) $tag->get_data_option()
);
if ( $tag->has_option( 'first_as_label' ) ) {
$tag_values = array_slice( $tag_values, 1 );
}
$values[$tag->name] = array_merge(
$values[$tag->name],
$tag_values
);
return $values;
},
array()
);
foreach ( $values as $field => $field_values ) {
$field_values = array_map(
static function ( $value ) {
return html_entity_decode(
(string) $value,
ENT_QUOTES | ENT_HTML5,
'UTF-8'
);
},
$field_values
);
$field_values = array_filter(
array_unique( $field_values ),
static function ( $value ) {
return '' !== $value;
}
);
$schema->add_rule(
wpcf7_swv_create_rule( 'enum', array(
'field' => $field,
'accept' => array_values( $field_values ),
'error' => $contact_form->filter_message(
__( "Undefined value was submitted through this field.", 'contact-form-7' )
),
) )
);
}
}
/* Tag generator */ /* Tag generator */
add_action( 'wpcf7_admin_init', 'wpcf7_add_tag_generator_menu', 25, 0 ); add_action( 'wpcf7_admin_init', 'wpcf7_add_tag_generator_menu', 25, 0 );

View File

@@ -38,9 +38,7 @@ function wpcf7_sendinblue_save_contact_form( $contact_form, $args, $context ) {
return; return;
} }
$prop = isset( $_POST['wpcf7-sendinblue'] ) $prop = (array) ( $_POST['wpcf7-sendinblue'] ?? array() );
? (array) $_POST['wpcf7-sendinblue']
: array();
$prop = wp_parse_args( $prop = wp_parse_args(
$prop, $prop,

View File

@@ -47,7 +47,7 @@ class WPCF7_Sendinblue extends WPCF7_Service {
public function link() { public function link() {
echo wpcf7_link( echo wpcf7_link(
'https://www.brevo.com/', 'https://get.brevo.com/wpcf7-integration',
'brevo.com' 'brevo.com'
); );
} }
@@ -88,9 +88,7 @@ class WPCF7_Sendinblue extends WPCF7_Service {
$this->reset_data(); $this->reset_data();
$redirect_to = $this->menu_page_url( 'action=setup' ); $redirect_to = $this->menu_page_url( 'action=setup' );
} else { } else {
$this->api_key = isset( $_POST['api_key'] ) $this->api_key = trim( $_POST['api_key'] ?? '' );
? trim( $_POST['api_key'] )
: '';
$confirmed = $this->confirm_key(); $confirmed = $this->confirm_key();

View File

@@ -107,9 +107,8 @@ class WPCF7_Stripe extends WPCF7_Service {
$this->reset_data(); $this->reset_data();
$redirect_to = $this->menu_page_url( 'action=setup' ); $redirect_to = $this->menu_page_url( 'action=setup' );
} else { } else {
$publishable = isset( $_POST['publishable'] ) ? $publishable = trim( $_POST['publishable'] ?? '' );
trim( $_POST['publishable'] ) : ''; $secret = trim( $_POST['secret'] ?? '' );
$secret = isset( $_POST['secret'] ) ? trim( $_POST['secret'] ) : '';
if ( $publishable and $secret ) { if ( $publishable and $secret ) {
$this->api_keys = array( $this->api_keys = array(

View File

@@ -80,7 +80,7 @@ function wpcf7_stripe_enqueue_scripts() {
) )
), ),
$assets['version'], $assets['version'],
true array( 'in_footer' => true )
); );
$api_keys = $service->get_api_keys(); $api_keys = $service->get_api_keys();

View File

@@ -1,11 +1,11 @@
=== Contact Form 7 === === Contact Form 7 ===
Contributors: takayukister Contributors: takayukister
Donate link: https://contactform7.com/donate/ Donate link: https://contactform7.com/donate/
Tags: contact, form, contact form, feedback, email, ajax, captcha, akismet, multilingual Tags: contact form, schema-woven validation
Requires at least: 6.2 Requires at least: 6.3
Requires PHP: 7.4 Requires PHP: 7.4
Tested up to: 6.4 Tested up to: 6.5
Stable tag: 5.8.7 Stable tag: 5.9.2
License: GPLv2 or later License: GPLv2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html License URI: https://www.gnu.org/licenses/gpl-2.0.html
@@ -78,6 +78,14 @@ Do you have questions or issues with Contact Form 7? Use these support channels
For more information, see [Releases](https://contactform7.com/category/releases/). For more information, see [Releases](https://contactform7.com/category/releases/).
= 5.9.2 =
[https://contactform7.com/contact-form-7-592/](https://contactform7.com/contact-form-7-592/)
= 5.9 =
[https://contactform7.com/contact-form-7-59/](https://contactform7.com/contact-form-7-59/)
= 5.8.7 = = 5.8.7 =
[https://contactform7.com/contact-form-7-587/](https://contactform7.com/contact-form-7-587/) [https://contactform7.com/contact-form-7-587/](https://contactform7.com/contact-form-7-587/)
@@ -110,40 +118,4 @@ For more information, see [Releases](https://contactform7.com/category/releases/
[https://contactform7.com/contact-form-7-58/](https://contactform7.com/contact-form-7-58/) [https://contactform7.com/contact-form-7-58/](https://contactform7.com/contact-form-7-58/)
= 5.7.7 =
[https://contactform7.com/contact-form-7-577/](https://contactform7.com/contact-form-7-577/)
= 5.7.6 =
[https://contactform7.com/contact-form-7-576/](https://contactform7.com/contact-form-7-576/)
= 5.7.5.1 =
* Fixes an old PHP compatibility issue.
= 5.7.5 =
[https://contactform7.com/contact-form-7-575/](https://contactform7.com/contact-form-7-575/)
= 5.7.4 =
[https://contactform7.com/contact-form-7-574/](https://contactform7.com/contact-form-7-574/)
= 5.7.3 =
[https://contactform7.com/contact-form-7-573/](https://contactform7.com/contact-form-7-573/)
= 5.7.2 =
[https://contactform7.com/contact-form-7-572/](https://contactform7.com/contact-form-7-572/)
= 5.7.1 =
[https://contactform7.com/contact-form-7-571/](https://contactform7.com/contact-form-7-571/)
= 5.7 =
[https://contactform7.com/contact-form-7-57/](https://contactform7.com/contact-form-7-57/)
== Upgrade Notice == == Upgrade Notice ==

View File

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

View File

@@ -138,7 +138,7 @@ window.imagify = window.imagify || {};
/** /**
* Fade CDN URL field. * Fade CDN URL field.
*/ */
$( '[name="imagify_settings[display_webp_method]"]' ).on( 'change.imagify init.imagify', function( e ) { $( '[name="imagify_settings[display_nextgen_method]"]' ).on( 'change.imagify init.imagify', function( e ) {
if ( 'picture' === e.target.value ) { if ( 'picture' === e.target.value ) {
$( e.target ).closest( '.imagify-radio-group' ).next( '.imagify-options-line' ).removeClass( 'imagify-faded' ); $( e.target ).closest( '.imagify-radio-group' ).next( '.imagify-options-line' ).removeClass( 'imagify-faded' );
} else { } else {
@@ -496,7 +496,7 @@ window.imagify = window.imagify || {};
.on( 'imagifybeat-send', this.addRequirementsImagifybeat ) .on( 'imagifybeat-send', this.addRequirementsImagifybeat )
.on( 'imagifybeat-tick', { imagifyOptionsBulk: this }, this.processRequirementsImagifybeat ); .on( 'imagifybeat-tick', { imagifyOptionsBulk: this }, this.processRequirementsImagifybeat );
if ( false !== imagifyOptions.bulk.progress_webp.total && false !== imagifyOptions.bulk.progress_webp.remaining ) { if ( false !== imagifyOptions.bulk.progress_next_gen.total && false !== imagifyOptions.bulk.progress_next_gen.remaining ) {
// Reset properties. // Reset properties.
w.imagify.optionsBulk.error = false; w.imagify.optionsBulk.error = false;
w.imagify.optionsBulk.working = true; w.imagify.optionsBulk.working = true;
@@ -511,10 +511,10 @@ window.imagify = window.imagify || {};
this.$missingWebpMessage.hide().attr('aria-hidden', 'true'); this.$missingWebpMessage.hide().attr('aria-hidden', 'true');
processed = imagifyOptions.bulk.progress_webp.total - imagifyOptions.bulk.progress_webp.remaining; processed = imagifyOptions.bulk.progress_next_gen.total - imagifyOptions.bulk.progress_next_gen.remaining;
progress = Math.floor( processed / imagifyOptions.bulk.progress_webp.total * 100 ); progress = Math.floor( processed / imagifyOptions.bulk.progress_next_gen.total * 100 );
this.$progressBar.css( 'width', progress + '%' ); this.$progressBar.css( 'width', progress + '%' );
this.$progressText.text( processed + '/' + imagifyOptions.bulk.progress_webp.total ); this.$progressText.text( processed + '/' + imagifyOptions.bulk.progress_next_gen.total );
this.$progressWrap.slideDown().attr( 'aria-hidden', 'false' ).removeClass( 'hidden' ); this.$progressWrap.slideDown().attr( 'aria-hidden', 'false' ).removeClass( 'hidden' );
} }
@@ -655,7 +655,7 @@ window.imagify = window.imagify || {};
_this = this; _this = this;
$.get( this.getAjaxUrl( 'MissingWebp', imagifyOptions.bulk.contexts ) ) $.get( this.getAjaxUrl( 'MissingNextGen', imagifyOptions.bulk.contexts ) )
.done( function( response ) { .done( function( response ) {
var errorMessage; var errorMessage;

File diff suppressed because one or more lines are too long

View File

@@ -44,7 +44,7 @@ class AdminBar {
if ( $user->is_free() ) { if ( $user->is_free() ) {
$text = esc_html__( 'Upgrade your plan now for more!', 'rocket' ) . '<br>' . $text = esc_html__( 'Upgrade your plan now for more!', 'rocket' ) . '<br>' .
esc_html__( 'From $4.99/month only, keep going with image optimization!', 'rocket' ); esc_html__( 'From $5.99/month only, keep going with image optimization!', 'rocket' );
$button_text = esc_html__( 'Upgrade My Plan', 'rocket' ); $button_text = esc_html__( 'Upgrade My Plan', 'rocket' );
$upgrade_link = IMAGIFY_APP_DOMAIN . '/subscription/?utm_source=plugin&utm_medium=notification'; $upgrade_link = IMAGIFY_APP_DOMAIN . '/subscription/?utm_source=plugin&utm_medium=notification';
} elseif ( $user->is_growth() ) { } elseif ( $user->is_growth() ) {

View File

@@ -0,0 +1,31 @@
<?php
declare(strict_types=1);
namespace Imagify\Avif;
use Imagify\WriteFile\AbstractApacheDirConfFile;
/**
* Add and remove contents to the .htaccess file to display AVIF images on the site.
*/
class Apache extends AbstractApacheDirConfFile {
/**
* Name of the tag used as block delemiter.
*
* @var string
*/
const TAG_NAME = 'Imagify: avif file type';
/**
* Get unfiltered new contents to write into the file.
*
* @return string
*/
protected function get_raw_new_contents() {
return trim( '
<IfModule mod_mime.c>
AddType image/avif .avif
</IfModule>' );
}
}

View File

@@ -0,0 +1,169 @@
<?php
declare(strict_types=1);
namespace Imagify\Avif;
use Imagify\EventManagement\SubscriberInterface;
use Imagify\Notices\Notices;
use Imagify\WriteFile\WriteFileInterface;
/**
* Display AVIF images on the site using picture tag.
*/
class Display implements SubscriberInterface {
/**
* Server conf object.
*
* @var WriteFileInterface|null
* @since 1.9
*/
protected $server_conf = null;
/**
* Returns an array of events this subscriber listens to
*
* @return array
*/
public static function get_subscribed_events() {
return [
'imagify_settings_on_save' => [ 'maybe_add_rewrite_rules', 12 ],
'imagify_activation' => 'activate',
'imagify_deactivation' => 'deactivate',
];
}
/**
* If display Next-Gen images, add the AVIF type to the .htaccess/etc file.
*
* @since 1.9
*
* @param array $values The option values.
*
* @return array
*/
public function maybe_add_rewrite_rules( $values ) {
if ( ! $this->get_server_conf() ) {
return $values;
}
$enabled = isset( $values['display_nextgen'] ) ? true : false;
$result = false;
if ( $enabled ) {
// Add the AVIF file type.
$result = $this->get_server_conf()->add();
} elseif ( ! $enabled ) {
// Remove the AVIF file type.
$result = $this->get_server_conf()->remove();
}
if ( ! is_wp_error( $result ) ) {
return $values;
}
// Display an error message.
if ( is_multisite() && strpos( wp_get_referer(), network_admin_url( '/' ) ) === 0 ) {
Notices::get_instance()->add_network_temporary_notice( $result->get_error_message() );
return $values;
}
Notices::get_instance()->add_site_temporary_notice( $result->get_error_message() );
return $values;
}
/**
* Add rules on plugin activation.
*
* @since 1.9
*/
public function activate() {
$conf = $this->get_server_conf();
if ( ! $conf ) {
return;
}
if ( ! get_imagify_option( 'display_nextgen' ) ) {
return;
}
if ( is_wp_error( $conf->is_file_writable() ) ) {
return;
}
$conf->add();
}
/**
* Remove rules on plugin deactivation.
*
* @since 1.9
*/
public function deactivate() {
$conf = $this->get_server_conf();
if ( ! $conf ) {
return;
}
$file_path = $conf->get_file_path();
$filesystem = \Imagify_Filesystem::get_instance();
if ( ! $filesystem->exists( $file_path ) ) {
return;
}
if ( ! $filesystem->is_writable( $file_path ) ) {
return;
}
$conf->remove();
}
/**
* Get the path to the directory conf file.
*
* @since 1.9
*
* @param bool $relative True to get a path relative to the sites root.
* @return string|bool The file path. False on failure.
*/
public function get_file_path( $relative = false ) {
if ( ! $this->get_server_conf() ) {
return false;
}
$file_path = $this->get_server_conf()->get_file_path();
if ( $relative ) {
return \Imagify_Filesystem::get_instance()->make_path_relative( $file_path );
}
return $file_path;
}
/**
* Get the server conf instance.
* Note: nothing needed for nginx.
*
* @since 1.9
*
* @return WriteFileInterface
*/
protected function get_server_conf() {
global $is_apache, $is_iis7;
if ( isset( $this->server_conf ) ) {
return $this->server_conf;
}
if ( $is_apache ) {
$this->server_conf = new Apache();
} elseif ( $is_iis7 ) {
$this->server_conf = new IIS();
}
return $this->server_conf;
}
}

View File

@@ -0,0 +1,32 @@
<?php
declare(strict_types=1);
namespace Imagify\Avif;
use Imagify\WriteFile\AbstractIISDirConfFile;
/**
* Add and remove contents to the web.config file to display AVIF images on the site.
*/
class IIS extends AbstractIISDirConfFile {
/**
* Name of the tag used as block delemiter.
*
* @var string
*/
const TAG_NAME = 'Imagify: avif file type';
/**
* Get unfiltered new contents to write into the file.
*
* @return array
*/
protected function get_raw_new_contents() {
return trim( '
<!-- @parent /configuration/system.webServer -->
<staticContent name="' . esc_attr( static::TAG_NAME ) . ' 1">
<mimeMap fileExtension=".avif" mimeType="image/avif" />
</staticContent>' );
}
}

View File

@@ -0,0 +1,59 @@
<?php
declare(strict_types=1);
namespace Imagify\Avif\RewriteRules;
use Imagify\WriteFile\AbstractApacheDirConfFile;
/**
* Add and remove rewrite rules to the .htaccess file to display AVIF images on the site.
*/
class Apache extends AbstractApacheDirConfFile {
/**
* Name of the tag used as block delimiter.
*
* @var string
*/
const TAG_NAME = 'Imagify: rewrite rules for avif';
/**
* Get unfiltered new contents to write into the file.
*
* @access protected
*
* @return string
*/
protected function get_raw_new_contents() {
$extensions = $this->get_extensions_pattern();
$extensions = str_replace( '|avif', '', $extensions );
$home_root = wp_parse_url( home_url( '/' ) );
$home_root = $home_root['path'];
return trim( '
<IfModule mod_setenvif.c>
# Vary: Accept for all the requests to jpeg, png, and gif.
SetEnvIf Request_URI "\.(' . $extensions . ')$" REQUEST_image
</IfModule>
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase ' . $home_root . '
# Check if browser supports AVIF images.
# Update the MIME type accordingly.
RewriteCond %{HTTP_ACCEPT} image/avif
# Check if AVIF replacement image exists.
RewriteCond %{REQUEST_FILENAME}.avif -f
# Serve AVIF image instead.
RewriteRule (.+)\.(' . $extensions . ')$ $1.$2.avif [T=image/avif,NC]
</IfModule>
<IfModule mod_headers.c>
# Update the MIME type accordingly.
Header append Vary Accept env=REQUEST_image
</IfModule>' );
}
}

View File

@@ -0,0 +1,224 @@
<?php
declare(strict_types=1);
namespace Imagify\Avif\RewriteRules;
use Imagify\EventManagement\SubscriberInterface;
use Imagify\Notices\Notices;
use Imagify\WriteFile\WriteFileInterface;
/**
* Display Avif images on the site with rewrite rules.
*/
class Display implements SubscriberInterface {
/**
* Configuration file writer.
*
* @var WriteFileInterface|null
*/
protected $server_conf = null;
/**
* Option value.
*
* @var string
*/
const OPTION_VALUE = 'rewrite';
/**
* Returns an array of events this subscriber listens to
*
* @return array
*/
public static function get_subscribed_events() {
return [
'imagify_settings_on_save' => [ 'maybe_add_rewrite_rules', 11 ],
'imagify_settings_webp_info' => 'maybe_add_avif_info',
'imagify_activation' => 'activate',
'imagify_deactivation' => 'deactivate',
];
}
/**
* If display AVIF images via rewrite rules, add the rules to the .htaccess/etc file.
*
* @since 1.9
*
* @param array $values The option values.
*
* @return array
*/
public function maybe_add_rewrite_rules( $values ) {
$was_enabled = (bool) get_imagify_option( 'display_nextgen' );
$is_enabled = ! empty( $values['display_nextgen'] );
// Which method?
$old_value = get_imagify_option( 'display_nextgen_method' );
$new_value = ! empty( $values['display_nextgen_method'] ) ? $values['display_nextgen_method'] : '';
// Decide when to add or remove rules.
$is_rewrite = self::OPTION_VALUE === $new_value;
$was_rewrite = self::OPTION_VALUE === $old_value;
if ( ! $this->get_server_conf() ) {
return $values;
}
$result = false;
if ( $is_enabled && $is_rewrite && ( ! $was_enabled || ! $was_rewrite ) ) {
// Add the rewrite rules.
$result = $this->get_server_conf()->add();
} elseif ( $was_enabled && $was_rewrite && ( ! $is_enabled || ! $is_rewrite ) ) {
// Remove the rewrite rules.
$result = $this->get_server_conf()->remove();
}
if ( ! is_wp_error( $result ) ) {
return $values;
}
// Display an error message.
if ( is_multisite() && strpos( wp_get_referer(), network_admin_url( '/' ) ) === 0 ) {
Notices::get_instance()->add_network_temporary_notice( $result->get_error_message() );
return $values;
}
Notices::get_instance()->add_site_temporary_notice( $result->get_error_message() );
return $values;
}
/**
* If the conf file is not writable, add a warning.
*/
public function maybe_add_avif_info() {
$conf = $this->get_server_conf();
if ( ! $conf ) {
return;
}
$writable = $conf->is_file_writable();
if ( is_wp_error( $writable ) ) {
$rules = $conf->get_new_contents();
if ( ! $rules ) {
// Uh?
return;
}
printf(
/* translators: %s is a file name. */
esc_html__( 'If you choose to use rewrite rules, you will have to add the following lines manually to the %s file:', 'imagify' ),
'<code>' . $this->get_file_path( true ) . '</code>'
);
echo '<pre class="code">' . esc_html( $rules ) . '</pre>';
}
}
/**
* Add rules on plugin activation.
*/
public function activate() {
$conf = $this->get_server_conf();
if ( ! $conf ) {
return;
}
if ( ! get_imagify_option( 'display_nextgen' ) ) {
return;
}
if ( self::OPTION_VALUE !== get_imagify_option( 'display_nextgen_method' ) ) {
return;
}
if ( is_wp_error( $conf->is_file_writable() ) ) {
return;
}
$conf->add();
}
/**
* Remove rules on plugin deactivation.
*
* @since 1.9
*/
public function deactivate() {
$conf = $this->get_server_conf();
if ( ! $conf ) {
return;
}
if ( ! get_imagify_option( 'display_nextgen' ) ) {
return;
}
if ( self::OPTION_VALUE !== get_imagify_option( 'display_nextgen_method' ) ) {
return;
}
$file_path = $conf->get_file_path();
$filesystem = \Imagify_Filesystem::get_instance();
if ( ! $filesystem->exists( $file_path ) ) {
return;
}
if ( ! $filesystem->is_writable( $file_path ) ) {
return;
}
$conf->remove();
}
/**
* Get the path to the directory conf file.
*
* @param bool $relative True to get a path relative to the sites root.
*
* @return string|bool The file path. False on failure.
*/
public function get_file_path( $relative = false ) {
if ( ! $this->get_server_conf() ) {
return false;
}
$file_path = $this->get_server_conf()->get_file_path();
if ( $relative ) {
return \Imagify_Filesystem::get_instance()->make_path_relative( $file_path );
}
return $file_path;
}
/**
* Get the server conf instance.
*
* @return WriteFileInterface
*/
protected function get_server_conf() {
global $is_apache, $is_iis7, $is_nginx;
if ( isset( $this->server_conf ) ) {
return $this->server_conf;
}
if ( $is_apache ) {
$this->server_conf = new Apache();
} elseif ( $is_iis7 ) {
$this->server_conf = new IIS();
} elseif ( $is_nginx ) {
$this->server_conf = new Nginx();
}
return $this->server_conf;
}
}

View File

@@ -0,0 +1,58 @@
<?php
declare(strict_types=1);
namespace Imagify\Avif\RewriteRules;
use Imagify\WriteFile\AbstractIISDirConfFile;
/**
* Add and remove rewrite rules to the web.config file to display AVIF images on the site.
*/
class IIS extends AbstractIISDirConfFile {
/**
* Name of the tag used as block delemiter.
*
* @var string
*/
const TAG_NAME = 'Imagify: rewrite rules for avif';
/**
* Get unfiltered new contents to write into the file.
*
* @source https://github.com/igrigorik/webp-detect/blob/master/iis.config
*
* @return string
*/
protected function get_raw_new_contents() {
$extensions = $this->get_extensions_pattern();
$extensions = str_replace( '|avif', '', $extensions );
$home_root = wp_parse_url( home_url( '/' ) );
$home_root = $home_root['path'];
return trim( '
<!-- @parent /configuration/system.webServer/rewrite/rules -->
<rule name="' . esc_attr( static::TAG_NAME ) . ' 2">
<match url="^(' . $home_root . '.+)\.(' . $extensions . ')$" ignoreCase="true" />
<conditions logicalGrouping="MatchAll">
<add input="{HTTP_ACCEPT}" pattern="image/avif" ignoreCase="false" />
<add input="{DOCUMENT_ROOT}/{R:1}{R:2}.avif" matchType="IsFile" />
</conditions>
<action type="Rewrite" url="{R:1}{R:2}.avif" logRewrittenUrl="true" />
<serverVariables>
<set name="ACCEPTS_AVIF" value="true" />
</serverVariables>
</rule>
<!-- @parent /configuration/system.webServer/rewrite/outboundRules -->
<rule preCondition="IsAvif" name="' . esc_attr( static::TAG_NAME ) . ' 3">
<match serverVariable="RESPONSE_Vary" pattern=".*" />
<action type="Rewrite" value="Accept"/>
</rule>
<preConditions name="' . esc_attr( static::TAG_NAME ) . ' 4">
<preCondition name="IsAvif">
<add input="{ACCEPTS_AVIF}" pattern="true" ignoreCase="false" />
</preCondition>
</preConditions>' );
}
}

View File

@@ -0,0 +1,30 @@
<?php
declare(strict_types=1);
namespace Imagify\Avif\RewriteRules;
use Imagify\WriteFile\AbstractNginxDirConfFile;
/**
* Add and remove rewrite rules to the imagify.conf file to display AVIF images on the site.
*/
class Nginx extends AbstractNginxDirConfFile {
/**
* Name of the tag used as block delimiter.
*
* @var string
*/
const TAG_NAME = 'Imagify: rewrite rules for avif';
/**
* Get unfiltered new contents to write into the file.
*
* @access protected
*
* @return string
*/
protected function get_raw_new_contents() {
return '';
}
}

View File

@@ -0,0 +1,51 @@
<?php
declare(strict_types=1);
namespace Imagify\Avif;
use Imagify\Dependencies\League\Container\ServiceProvider\AbstractServiceProvider;
use Imagify\Avif\RewriteRules\Display as RewriteRules;
/**
* Service provider for AVIF rewrite rules
*/
class ServiceProvider extends AbstractServiceProvider {
/**
* Services provided by this provider
*
* @var array
*/
protected $provides = [
'avif_display',
'avif_rewrite_rules',
];
/**
* Subscribers provided by this provider
*
* @var array
*/
public $subscribers = [
'avif_display',
'avif_rewrite_rules',
];
/**
* Registers the provided classes
*
* @return void
*/
public function register() {
$this->getContainer()->share( 'avif_display', Display::class );
$this->getContainer()->share( 'avif_rewrite_rules', RewriteRules::class );
}
/**
* Returns the subscribers array
*
* @return array
*/
public function get_subscribers() {
return $this->subscribers;
}
}

View File

@@ -105,13 +105,19 @@ abstract class AbstractBulk implements BulkInterface {
} }
/** /**
* Tell if there are optimized media without WebP versions. * Tell if there are optimized media without next-gen versions.
* *
* @since 1.9 * @since 1.9
* *
* @return int The number of media. * @return int The number of media.
*/ */
public function has_optimized_media_without_webp() { public function has_optimized_media_without_nextgen() {
return count( $this->get_optimized_media_ids_without_webp()['ids'] ); $format = 'webp';
if ( get_imagify_option( 'convert_to_avif' ) ) {
$format = 'avif';
}
return count( $this->get_optimized_media_ids_without_format( $format )['ids'] );
} }
} }

View File

@@ -17,15 +17,15 @@ class Bulk {
*/ */
public function init() { public function init() {
add_action( 'imagify_optimize_media', [ $this, 'optimize_media' ], 10, 3 ); add_action( 'imagify_optimize_media', [ $this, 'optimize_media' ], 10, 3 );
add_action( 'imagify_convert_webp', [ $this, 'generate_webp_versions' ], 10, 2 ); add_action( 'imagify_convert_next_gen', [ $this, 'generate_nextgen_versions' ], 10, 2 );
add_action( 'imagify_convert_webp_finished', [ $this, 'clear_webp_transients' ], 10, 2 );
add_action( 'wp_ajax_imagify_bulk_optimize', [ $this, 'bulk_optimize_callback' ] ); add_action( 'wp_ajax_imagify_bulk_optimize', [ $this, 'bulk_optimize_callback' ] );
add_action( 'wp_ajax_imagify_missing_webp_generation', [ $this, 'missing_webp_callback' ] ); add_action( 'wp_ajax_imagify_missing_nextgen_generation', [ $this, 'missing_nextgen_callback' ] );
add_action( 'wp_ajax_imagify_get_folder_type_data', [ $this, 'get_folder_type_data_callback' ] ); add_action( 'wp_ajax_imagify_get_folder_type_data', [ $this, 'get_folder_type_data_callback' ] );
add_action( 'wp_ajax_imagify_bulk_info_seen', [ $this, 'bulk_info_seen_callback' ] ); add_action( 'wp_ajax_imagify_bulk_info_seen', [ $this, 'bulk_info_seen_callback' ] );
add_action( 'wp_ajax_imagify_bulk_get_stats', [ $this, 'bulk_get_stats_callback' ] ); add_action( 'wp_ajax_imagify_bulk_get_stats', [ $this, 'bulk_get_stats_callback' ] );
add_action( 'imagify_after_optimize', [ $this, 'check_optimization_status' ], 10, 2 ); add_action( 'imagify_after_optimize', [ $this, 'check_optimization_status' ], 10, 2 );
add_action( 'imagify_deactivation', [ $this, 'delete_transients_data' ] ); add_action( 'imagify_deactivation', [ $this, 'delete_transients_data' ] );
add_action( 'update_option_imagify_settings', [ $this, 'maybe_generate_missing_nextgen' ], 10, 2 );
} }
/** /**
@@ -37,7 +37,7 @@ class Bulk {
delete_transient( 'imagify_custom-folders_optimize_running' ); delete_transient( 'imagify_custom-folders_optimize_running' );
delete_transient( 'imagify_wp_optimize_running' ); delete_transient( 'imagify_wp_optimize_running' );
delete_transient( 'imagify_bulk_optimization_complete' ); delete_transient( 'imagify_bulk_optimization_complete' );
delete_transient( 'imagify_missing_webp_total' ); delete_transient( 'imagify_missing_next_gen_total' );
} }
/** /**
@@ -173,17 +173,31 @@ class Bulk {
'message' => 'over-quota', 'message' => 'over-quota',
]; ];
} }
$formats = imagify_nextgen_images_formats();
$media_ids = [
'ids' => [],
'errors' => [
'no_file_path' => [],
'no_backup' => [],
],
];
foreach ( $formats as $format ) {
$result = $this->get_bulk_instance( $context )->get_optimized_media_ids_without_format( $format );
$media_ids['ids'] = array_merge( $media_ids['ids'], $result['ids'] );
}
$get_unoptimized_media_ids = $this->get_bulk_instance( $context )->get_unoptimized_media_ids( $optimization_level );
$media_ids = $this->get_bulk_instance( $context )->get_unoptimized_media_ids( $optimization_level ); $media_ids['ids'] = array_merge( $media_ids['ids'], $get_unoptimized_media_ids );
if ( empty( $media_ids ) ) { if ( empty( $media_ids['ids'] ) ) {
return [ return [
'success' => false, 'success' => false,
'message' => 'no-images', 'message' => 'no-images',
]; ];
} }
$media_ids['ids'] = array_unique( $media_ids['ids'] );
foreach ( $media_ids as $media_id ) { foreach ( $media_ids['ids'] as $media_id ) {
try { try {
as_enqueue_async_action( as_enqueue_async_action(
'imagify_optimize_media', 'imagify_optimize_media',
@@ -213,13 +227,14 @@ class Bulk {
} }
/** /**
* Runs the WebP generation * Runs the next-gen generation
* *
* @param array $contexts An array of contexts (WP/Custom folders). * @param array $contexts An array of contexts (WP/Custom folders).
* @param array $formats An array of format to generate.
* *
* @return array * @return array
*/ */
public function run_generate_webp( array $contexts ) { public function run_generate_nextgen( array $contexts, array $formats ) {
if ( ! $this->can_optimize() ) { if ( ! $this->can_optimize() ) {
return [ return [
'success' => false, 'success' => false,
@@ -227,28 +242,29 @@ class Bulk {
]; ];
} }
delete_transient( 'imagify_stat_without_webp' ); delete_transient( 'imagify_stat_without_next_gen' );
$medias = []; $medias = [];
foreach ( $contexts as $context ) { foreach ( $contexts as $context ) {
$media = $this->get_bulk_instance( $context )->get_optimized_media_ids_without_webp(); foreach ( $formats as $format ) {
$media = $this->get_bulk_instance( $context )->get_optimized_media_ids_without_format( $format );
if ( ! $media['ids'] && $media['errors']['no_backup'] ) {
// No backup, no next-gen.
return [
'success' => false,
'message' => 'no-backup',
];
} elseif ( ! $media['ids'] && $media['errors']['no_file_path'] ) {
// Error.
return [
'success' => false,
'message' => __( 'The path to the selected files could not be retrieved.', 'imagify' ),
];
}
if ( ! $media['ids'] && $media['errors']['no_backup'] ) { $medias[ $context ] = $media['ids'];
// No backup, no WebP.
return [
'success' => false,
'message' => 'no-backup',
];
} elseif ( ! $media['ids'] && $media['errors']['no_file_path'] ) {
// Error.
return [
'success' => false,
'message' => __( 'The path to the selected files could not be retrieved.', 'imagify' ),
];
} }
$medias[ $context ] = $media['ids'];
} }
if ( empty( $medias ) ) { if ( empty( $medias ) ) {
@@ -266,12 +282,12 @@ class Bulk {
foreach ( $media_ids as $media_id ) { foreach ( $media_ids as $media_id ) {
try { try {
as_enqueue_async_action( as_enqueue_async_action(
'imagify_convert_webp', 'imagify_convert_next_gen',
[ [
'id' => $media_id, 'id' => $media_id,
'context' => $context, 'context' => $context,
], ],
"imagify-{$context}-convert-webp" "imagify-{$context}-convert-nextgen"
); );
} catch ( Exception $exception ) { // phpcs:ignore Generic.CodeAnalysis.EmptyStatement.DetectedCatch } catch ( Exception $exception ) { // phpcs:ignore Generic.CodeAnalysis.EmptyStatement.DetectedCatch
// nothing to do. // nothing to do.
@@ -279,7 +295,7 @@ class Bulk {
} }
} }
set_transient( 'imagify_missing_webp_total', $total, HOUR_IN_SECONDS ); set_transient( 'imagify_missing_next_gen_total', $total, HOUR_IN_SECONDS );
return [ return [
'success' => true, 'success' => true,
@@ -310,13 +326,13 @@ class Bulk {
} }
/** /**
* Filter the name of the class to use for bulk process. * Filter the name of the class to use for bulk process.
* *
* @since 1.9 * @since 1.9
* *
* @param int $class_name The class name. * @param int $class_name The class name.
* @param string $context The context name. * @param string $context The context name.
*/ */
$class_name = apply_filters( 'imagify_bulk_class_name', $class_name, $context ); $class_name = apply_filters( 'imagify_bulk_class_name', $class_name, $context );
return '\\' . ltrim( $class_name, '\\' ); return '\\' . ltrim( $class_name, '\\' );
@@ -374,7 +390,7 @@ class Bulk {
} }
/** /**
* Generate WebP images if they are missing. * Generate next-gen images if they are missing.
* *
* @since 2.1 * @since 2.1
* *
@@ -383,12 +399,12 @@ class Bulk {
* *
* @return bool|WP_Error True if successfully launched. A \WP_Error instance on failure. * @return bool|WP_Error True if successfully launched. A \WP_Error instance on failure.
*/ */
public function generate_webp_versions( int $media_id, string $context ) { public function generate_nextgen_versions( int $media_id, string $context ) {
if ( ! $this->can_optimize() ) { if ( ! $this->can_optimize() ) {
return false; return false;
} }
return imagify_get_optimization_process( $media_id, $context )->generate_webp_versions(); return imagify_get_optimization_process( $media_id, $context )->generate_nextgen_versions();
} }
/** /**
@@ -451,10 +467,6 @@ class Bulk {
return (int) $level; return (int) $level;
} }
/** ----------------------------------------------------------------------------------------- */
/** BULK OPTIMIZATION CALLBACKS ============================================================= */
/** ----------------------------------------------------------------------------------------- */
/** /**
* Launch the bulk optimization action * Launch the bulk optimization action
* *
@@ -480,14 +492,14 @@ class Bulk {
} }
/** /**
* Launch the missing WebP versions generation * Launch the missing Next-gen versions generation
* *
* @return void * @return void
*/ */
public function missing_webp_callback() { public function missing_nextgen_callback() {
imagify_check_nonce( 'imagify-bulk-optimize' ); imagify_check_nonce( 'imagify-bulk-optimize' );
$contexts = explode( '_', sanitize_key( wp_unslash( $_GET['context'] ) ) ); $contexts = $this->get_contexts();
foreach ( $contexts as $context ) { foreach ( $contexts as $context ) {
if ( ! imagify_get_context( $context )->current_user_can( 'bulk-optimize' ) ) { if ( ! imagify_get_context( $context )->current_user_can( 'bulk-optimize' ) ) {
@@ -495,8 +507,9 @@ class Bulk {
} }
} }
$data = $this->run_generate_webp( $contexts ); $formats = imagify_nextgen_images_formats();
$data = $this->run_generate_nextgen( $contexts, $formats );
if ( false === $data['success'] ) { if ( false === $data['success'] ) {
wp_send_json_error( [ 'message' => $data['message'] ] ); wp_send_json_error( [ 'message' => $data['message'] ] );
} }
@@ -575,4 +588,87 @@ class Bulk {
wp_send_json_success( imagify_get_bulk_stats( array_flip( $folder_types ) ) ); wp_send_json_success( imagify_get_bulk_stats( array_flip( $folder_types ) ) );
} }
/**
* Update Options callback to start bulk optimization.
*
* @since 2.2
*
* @param array $old_value The old option value.
* @param array $value The new option value.
*
* Please note that the convert_to_avif new value is a checkbox,
* so it equals 1 when it's set otherwise it's not set.
* That's why we need to use empty function when checking its value.
*
* @return void
*/
public function maybe_generate_missing_nextgen( $old_value, $value ) {
if ( empty( $old_value['convert_to_avif'] ) === empty( $value['convert_to_avif'] ) ) {
// Old value = new value so do nothing.
return;
}
if ( empty( $value['convert_to_avif'] ) ) {
// new value is disabled, do nothing.
return;
}
$contexts = $this->get_contexts();
$formats = imagify_nextgen_images_formats();
$this->run_generate_nextgen( $contexts, $formats );
}
/**
* Get the context for the bulk optimization page.
*
* @since 2.2
*
* @return array The array of unique contexts ('wp' or 'custom-folders').
*/
public function get_contexts() {
$contexts = [];
$types = [];
// Library: in each site.
if ( ! is_network_admin() ) {
$types['library|wp'] = 1;
}
// Custom folders: in network admin only if network activated, in each site otherwise.
if ( imagify_can_optimize_custom_folders() && ( imagify_is_active_for_network() && is_network_admin() || ! imagify_is_active_for_network() ) ) {
$types['custom-folders|custom-folders'] = 1;
}
/**
* Filter the types to display in the bulk optimization page.
*
* @since 1.7.1
*
* @param array $types The folder types displayed on the page. If a folder type is "library", the context should be suffixed after a pipe character. They are passed as array keys.
*/
$types = apply_filters( 'imagify_bulk_page_types', $types );
$types = array_filter( (array) $types );
if ( isset( $types['library|wp'] ) && ! in_array( 'wp', $contexts, true ) ) {
$contexts[] = 'wp';
}
if ( isset( $types['custom-folders|custom-folders'] ) ) {
$folders_instance = \Imagify_Folders_DB::get_instance();
if ( ! $folders_instance->has_items() ) {
// New Feature!
if ( ! in_array( 'wp', $contexts, true ) ) {
$contexts[] = 'wp';
}
} elseif ( $folders_instance->has_active_folders() && ! in_array( 'custom-folders', $contexts, true ) ) {
$contexts[] = 'custom-folders';
}
}
return $contexts;
}
} }

View File

@@ -18,10 +18,11 @@ interface BulkInterface {
public function get_unoptimized_media_ids( $optimization_level ); public function get_unoptimized_media_ids( $optimization_level );
/** /**
* Get ids of all optimized media without WebP versions. * Get ids of all optimized media without Next gen versions.
* *
* @since 1.9 * @since 2.2
* @since 1.9.5 The method doesn't return the IDs directly anymore. *
* @param string $format Format we are looking for. (webp|avif).
* *
* @return array { * @return array {
* @type array $ids A list of media IDs. * @type array $ids A list of media IDs.
@@ -31,16 +32,17 @@ interface BulkInterface {
* } * }
* } * }
*/ */
public function get_optimized_media_ids_without_webp(); public function get_optimized_media_ids_without_format( $format );
/** /**
* Tell if there are optimized media without WebP versions. * Tell if there are optimized media without next-gen versions.
* *
* @since 1.9 * @since 2.2
* *
* @return int The number of media. * @return int The number of media.
*/ */
public function has_optimized_media_without_webp(); public function has_optimized_media_without_nextgen();
/** /**
* Get the context data. * Get the context data.

View File

@@ -74,10 +74,11 @@ class CustomFolders extends AbstractBulk {
} }
/** /**
* Get ids of all optimized media without WebP versions. * Get ids of all optimized media without Next gen versions.
* *
* @since 1.9 * @since 2.2
* @since 1.9.5 The method doesn't return the IDs directly anymore. *
* @param string $format Format we are looking for. (webp|avif).
* *
* @return array { * @return array {
* @type array $ids A list of media IDs. * @type array $ids A list of media IDs.
@@ -87,7 +88,7 @@ class CustomFolders extends AbstractBulk {
* } * }
* } * }
*/ */
public function get_optimized_media_ids_without_webp() { public function get_optimized_media_ids_without_format( $format ) {
global $wpdb; global $wpdb;
$this->set_no_time_limit(); $this->set_no_time_limit();
@@ -95,9 +96,22 @@ class CustomFolders extends AbstractBulk {
$files_table = Imagify_Files_DB::get_instance()->get_table_name(); $files_table = Imagify_Files_DB::get_instance()->get_table_name();
$folders_table = Imagify_Folders_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 = Imagify_DB::get_mime_types( 'image' );
$mime_types = str_replace( ",'image/webp'", '', $mime_types ); // Remove single quotes and explode string into array.
$webp_suffix = constant( imagify_get_optimization_process_class_name( 'custom-folders' ) . '::WEBP_SUFFIX' ); $mime_types_array = explode( ',', str_replace( "'", '', $mime_types ) );
$files = $wpdb->get_results( $wpdb->prepare( // WPCS: unprepared SQL ok.
// Iterate over array and check if string contains input.
foreach ( $mime_types_array as $item ) {
if ( strpos( $item, $format ) !== false ) {
$mime = $item;
break;
}
}
if ( ! isset( $mime ) && empty( $mime ) ) {
$mime = 'image/webp';
}
$mime_types = str_replace( ",'" . $mime . "'", '', $mime_types );
$nextgen_suffix = constant( imagify_get_optimization_process_class_name( 'custom-folders' ) . '::' . strtoupper( $format ) . '_SUFFIX' );
$files = $wpdb->get_results( $wpdb->prepare( // WPCS: unprepared SQL ok.
" "
SELECT fi.file_id, fi.path SELECT fi.file_id, fi.path
FROM $files_table as fi FROM $files_table as fi
@@ -108,11 +122,11 @@ class CustomFolders extends AbstractBulk {
AND ( fi.status = 'success' OR fi.status = 'already_optimized' ) AND ( fi.status = 'success' OR fi.status = 'already_optimized' )
AND ( fi.data NOT LIKE %s OR fi.data IS NULL ) AND ( fi.data NOT LIKE %s OR fi.data IS NULL )
ORDER BY fi.file_id DESC", ORDER BY fi.file_id DESC",
'%' . $wpdb->esc_like( $webp_suffix . '";a:4:{s:7:"success";b:1;' ) . '%' '%' . $wpdb->esc_like( $nextgen_suffix . '";a:4:{s:7:"success";b:1;' ) . '%'
) ); ) );
$wpdb->flush(); $wpdb->flush();
unset( $mime_types, $files_table, $folders_table, $webp_suffix ); unset( $mime_types, $files_table, $folders_table, $nextgen_suffix, $mime );
$data = [ $data = [
'ids' => [], 'ids' => [],

View File

@@ -20,10 +20,11 @@ class Noop extends AbstractBulk {
} }
/** /**
* Get ids of all optimized media without WebP versions. * * Get ids of all optimized media without Next gen versions.
* *
* @since 1.9 * @since 2.2
* @since 1.9.5 The method doesn't return the IDs directly anymore. *
* @param string $format Format we are looking for. (webp|avif).
* *
* @return array { * @return array {
* @type array $ids A list of media IDs. * @type array $ids A list of media IDs.
@@ -33,7 +34,7 @@ class Noop extends AbstractBulk {
* } * }
* } * }
*/ */
public function get_optimized_media_ids_without_webp() { public function get_optimized_media_ids_without_format( $format ) {
return [ return [
'ids' => [], 'ids' => [],
'errors' => [ 'errors' => [

View File

@@ -165,10 +165,11 @@ class WP extends AbstractBulk {
} }
/** /**
* Get ids of all optimized media without WebP versions. * Get ids of all optimized media without Next gen versions.
* *
* @since 1.9 * @since 2.2
* @since 1.9.5 The method doesn't return the IDs directly anymore. *
* @param string $format Format we are looking for. (webp|avif).
* *
* @return array { * @return array {
* @type array $ids A list of media IDs. * @type array $ids A list of media IDs.
@@ -178,45 +179,61 @@ class WP extends AbstractBulk {
* } * }
* } * }
*/ */
public function get_optimized_media_ids_without_webp() { public function get_optimized_media_ids_without_format( $format ) {
global $wpdb; global $wpdb;
$this->set_no_time_limit(); $this->set_no_time_limit();
$mime_types = Imagify_DB::get_mime_types( 'image' ); $mime_types = Imagify_DB::get_mime_types( 'image' );
$mime_types = str_replace( ",'image/webp'", '', $mime_types );
// Remove single quotes and explode string into array.
$mime_types_array = explode( ',', str_replace( "'", '', $mime_types ) );
// Iterate over array and check if string contains input.
foreach ( $mime_types_array as $item ) {
if ( strpos( $item, $format ) !== false ) {
$mime = $item;
break;
}
}
if ( ! isset( $mime ) && empty( $mime ) ) {
$mime = 'image/webp';
}
$mime_types = str_replace( ",'" . $mime . "'", '', $mime_types );
$statuses = Imagify_DB::get_post_statuses(); $statuses = Imagify_DB::get_post_statuses();
$nodata_join = Imagify_DB::get_required_wp_metadata_join_clause(); $nodata_join = Imagify_DB::get_required_wp_metadata_join_clause();
$nodata_where = Imagify_DB::get_required_wp_metadata_where_clause( [ $nodata_where = Imagify_DB::get_required_wp_metadata_where_clause( [
'prepared' => true, 'prepared' => true,
] ); ] );
$webp_suffix = constant( imagify_get_optimization_process_class_name( 'wp' ) . '::WEBP_SUFFIX' ); $nextgen_suffix = constant( imagify_get_optimization_process_class_name( 'wp' ) . '::' . strtoupper( $format ) . '_SUFFIX' );
$ids = $wpdb->get_col( $wpdb->prepare( // WPCS: unprepared SQL ok. $ids = $wpdb->get_col( $wpdb->prepare( // WPCS: unprepared SQL ok.
" "
SELECT p.ID SELECT p.ID
FROM $wpdb->posts AS p FROM $wpdb->posts AS p
$nodata_join $nodata_join
LEFT JOIN $wpdb->postmeta AS mt1 LEFT JOIN $wpdb->postmeta AS mt1
ON ( p.ID = mt1.post_id AND mt1.meta_key = '_imagify_status' ) ON ( p.ID = mt1.post_id AND mt1.meta_key = '_imagify_status' )
LEFT JOIN $wpdb->postmeta AS mt2 LEFT JOIN $wpdb->postmeta AS mt2
ON ( p.ID = mt2.post_id AND mt2.meta_key = '_imagify_data' ) ON ( p.ID = mt2.post_id AND mt2.meta_key = '_imagify_data' )
WHERE WHERE
p.post_mime_type IN ( $mime_types ) p.post_mime_type IN ( $mime_types )
AND ( mt1.meta_value = 'success' OR mt1.meta_value = 'already_optimized' ) AND (mt1.meta_key IS NULL OR mt1.meta_value = 'success' OR mt1.meta_value = 'already_optimized' )
AND mt2.meta_value NOT LIKE %s AND mt2.meta_value NOT LIKE %s
AND p.post_type = 'attachment' AND p.post_type = 'attachment'
AND p.post_status IN ( $statuses ) AND p.post_status IN ( $statuses )
$nodata_where $nodata_where
ORDER BY p.ID DESC ORDER BY p.ID DESC
LIMIT 0, %d", LIMIT 0, %d",
'%' . $wpdb->esc_like( $webp_suffix . '";a:4:{s:7:"success";b:1;' ) . '%', '%' . $wpdb->esc_like( $nextgen_suffix . '";a:4:{s:7:"success";b:1;' ) . '%',
imagify_get_unoptimized_attachment_limit() imagify_get_unoptimized_attachment_limit()
) ); ) );
$wpdb->flush(); $wpdb->flush();
unset( $mime_types, $statuses, $webp_suffix ); unset( $mime_types, $statuses, $nextgen_suffix, $mime );
$ids = array_filter( array_map( 'absint', $ids ) ); $ids = array_filter( array_map( 'absint', $ids ) );
$data = [ $data = [
'ids' => [], 'ids' => [],
'errors' => [ 'errors' => [
@@ -243,7 +260,7 @@ class WP extends AbstractBulk {
* @param array $metas An array of the data fetched from the database. * @param array $metas An array of the data fetched from the database.
* @param string $context The context. * @param string $context The context.
*/ */
do_action( 'imagify_bulk_generate_webp_before_file_existence_tests', $ids, $metas, 'wp' ); do_action( 'imagify_bulk_generate_nextgen_before_file_existence_tests', $ids, $metas, 'wp' );
foreach ( $ids as $i => $id ) { foreach ( $ids as $i => $id ) {
if ( empty( $metas['filenames'][ $id ] ) ) { if ( empty( $metas['filenames'][ $id ] ) ) {

View File

@@ -0,0 +1,123 @@
<?php
declare(strict_types=1);
namespace Imagify\CDN;
use Imagify\EventManagement\SubscriberInterface;
/**
* CDN subscriber
*/
class CDN implements SubscriberInterface {
/**
* Array of events this subscriber listens to
*
* @return array
*/
public static function get_subscribed_events() {
return [
'imagify_cdn_source_url' => 'get_cdn_source',
];
}
/**
* Get the CDN "source".
*
* @since 1.9.3
*
* @param string $option_url An URL to use instead of the one stored in the option. It is used only if no constant/filter.
*
* @return array {
* @type string $source Where does it come from? Possible values are 'constant', 'filter', or 'option'.
* @type string $name Who? Can be a constant name, a plugin name, or an empty string.
* @type string $url The CDN URL, with a trailing slash. An empty string if no URL is set.
* }
*/
public function get_cdn_source( $option_url = '' ) {
if ( defined( 'IMAGIFY_CDN_URL' ) && IMAGIFY_CDN_URL && is_string( IMAGIFY_CDN_URL ) ) {
// Use a constant.
$source = [
'source' => 'constant',
'name' => 'IMAGIFY_CDN_URL',
'url' => IMAGIFY_CDN_URL,
];
} else {
// Maybe use a filter.
$filter_source = [
'name' => null,
'url' => null,
];
/**
* Provide a custom CDN source.
*
* @since 1.9.3
*
* @param array $filter_source {
* @type $name string The name of which provides the URL (plugin name, etc).
* @type $url string The CDN URL.
* }
*/
$filter_source = apply_filters( 'imagify_cdn_source', $filter_source );
if ( ! empty( $filter_source['url'] ) ) {
$source = [
'source' => 'filter',
'name' => ! empty( $filter_source['name'] ) ? $filter_source['name'] : '',
'url' => $filter_source['url'],
];
}
}
if ( empty( $source['url'] ) ) {
// No constant, no filter: use the option.
$source = [
'source' => 'option',
'name' => '',
'url' => $option_url && is_string( $option_url ) ? $option_url : get_imagify_option( 'cdn_url' ),
];
}
if ( empty( $source['url'] ) ) {
// Nothing set.
return [
'source' => 'option',
'name' => '',
'url' => '',
];
}
$source['url'] = $this->sanitize_cdn_url( $source['url'] );
if ( empty( $source['url'] ) ) {
// Not an URL.
return [
'source' => 'option',
'name' => '',
'url' => '',
];
}
return $source;
}
/**
* Sanitize the CDN URL value.
*
* @since 1.9.3
*
* @param string $url The URL to sanitize.
*
* @return string
*/
public function sanitize_cdn_url( $url ) {
$url = sanitize_text_field( $url );
if ( ! $url || ! preg_match( '@^https?://.+\.[^.]+@i', $url ) ) {
// Not an URL.
return '';
}
return trailingslashit( $url );
}
}

View File

@@ -0,0 +1,47 @@
<?php
declare(strict_types=1);
namespace Imagify\CDN;
use Imagify\Dependencies\League\Container\ServiceProvider\AbstractServiceProvider;
/**
* Service provider for CDN compatibility
*/
class ServiceProvider extends AbstractServiceProvider {
/**
* Services provided by this provider
*
* @var array
*/
protected $provides = [
'cdn',
];
/**
* Subscribers provided by this provider
*
* @var array
*/
public $subscribers = [
'cdn',
];
/**
* Registers the provided classes
*
* @return void
*/
public function register() {
$this->getContainer()->share( 'cdn', CDN::class );
}
/**
* Returns the subscribers array
*
* @return array
*/
public function get_subscribers() {
return $this->subscribers;
}
}

View File

@@ -6,9 +6,9 @@ namespace Imagify\CLI;
use Imagify\Bulk\Bulk; use Imagify\Bulk\Bulk;
/** /**
* Command class for the missing WebP generation * Command class for the missing Nextgen generation
*/ */
class GenerateMissingWebpCommand extends AbstractCommand { class GenerateMissingNextgenCommand extends AbstractCommand {
/** /**
* Executes the command. * Executes the command.
* *
@@ -16,23 +16,23 @@ class GenerateMissingWebpCommand extends AbstractCommand {
* @param array $options Optional arguments. * @param array $options Optional arguments.
*/ */
public function __invoke( $arguments, $options ) { public function __invoke( $arguments, $options ) {
Bulk::get_instance()->run_generate_webp( $arguments ); Bulk::get_instance()->run_generate_nextgen( $arguments );
\WP_CLI::log( 'Imagify missing WebP generation triggered.' ); \WP_CLI::log( 'Imagify missing next-gen images generation triggered.' );
} }
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
protected function get_command_name(): string { protected function get_command_name(): string {
return 'generate-missing-webp'; return 'generate-missing-nextgen';
} }
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function get_description(): string { public function get_description(): string {
return 'Run the generation of the missing WebP versions'; return 'Run the generation of the missing next-gen images versions';
} }
/** /**
@@ -43,7 +43,7 @@ class GenerateMissingWebpCommand extends AbstractCommand {
[ [
'type' => 'positional', 'type' => 'positional',
'name' => 'contexts', 'name' => 'contexts',
'description' => 'The context(s) to run the missing WebP generation for. Possible values are wp and custom-folders.', 'description' => 'The context(s) to run the missing next-gen images generation for. Possible values are wp and custom-folders.',
'optional' => false, 'optional' => false,
'repeating' => true, 'repeating' => true,
], ],

View File

@@ -0,0 +1,28 @@
<?php declare(strict_types=1);
namespace Imagify\Dependencies\League\Container\Argument;
use Imagify\Dependencies\League\Container\ContainerAwareInterface;
use ReflectionFunctionAbstract;
interface ArgumentResolverInterface extends ContainerAwareInterface
{
/**
* Resolve an array of arguments to their concrete implementations.
*
* @param array $arguments
*
* @return array
*/
public function resolveArguments(array $arguments) : array;
/**
* Resolves the correct arguments to be passed to a method.
*
* @param ReflectionFunctionAbstract $method
* @param array $args
*
* @return array
*/
public function reflectArguments(ReflectionFunctionAbstract $method, array $args = []) : array;
}

View File

@@ -0,0 +1,120 @@
<?php declare(strict_types=1);
namespace Imagify\Dependencies\League\Container\Argument;
use Imagify\Dependencies\League\Container\Container;
use Imagify\Dependencies\League\Container\Exception\{ContainerException, NotFoundException};
use Imagify\Dependencies\League\Container\ReflectionContainer;
use Imagify\Dependencies\Psr\Container\ContainerInterface;
use ReflectionFunctionAbstract;
use ReflectionParameter;
trait ArgumentResolverTrait
{
/**
* {@inheritdoc}
*/
public function resolveArguments(array $arguments) : array
{
return array_map(function ($argument) {
$justStringValue = false;
if ($argument instanceof RawArgumentInterface) {
return $argument->getValue();
} elseif ($argument instanceof ClassNameInterface) {
$id = $argument->getClassName();
} elseif (!is_string($argument)) {
return $argument;
} else {
$justStringValue = true;
$id = $argument;
}
$container = null;
try {
$container = $this->getLeagueContainer();
} catch (ContainerException $e) {
if ($this instanceof ReflectionContainer) {
$container = $this;
}
}
if ($container !== null) {
try {
return $container->get($id);
} catch (NotFoundException $exception) {
if ($argument instanceof ClassNameWithOptionalValue) {
return $argument->getOptionalValue();
}
if ($justStringValue) {
return $id;
}
throw $exception;
}
}
if ($argument instanceof ClassNameWithOptionalValue) {
return $argument->getOptionalValue();
}
// Just a string value.
return $id;
}, $arguments);
}
/**
* {@inheritdoc}
*/
public function reflectArguments(ReflectionFunctionAbstract $method, array $args = []) : array
{
$arguments = array_map(function (ReflectionParameter $param) use ($method, $args) {
$name = $param->getName();
$type = $param->getType();
if (array_key_exists($name, $args)) {
return new RawArgument($args[$name]);
}
if ($type) {
if (PHP_VERSION_ID >= 70100) {
$typeName = $type->getName();
} else {
$typeName = (string) $type;
}
$typeName = ltrim($typeName, '?');
if ($param->isDefaultValueAvailable()) {
return new ClassNameWithOptionalValue($typeName, $param->getDefaultValue());
}
return new ClassName($typeName);
}
if ($param->isDefaultValueAvailable()) {
return new RawArgument($param->getDefaultValue());
}
throw new NotFoundException(sprintf(
'Unable to resolve a value for parameter (%s) in the function/method (%s)',
$name,
$method->getName()
));
}, $method->getParameters());
return $this->resolveArguments($arguments);
}
/**
* @return ContainerInterface
*/
abstract public function getContainer() : ContainerInterface;
/**
* @return Container
*/
abstract public function getLeagueContainer() : Container;
}

View File

@@ -0,0 +1,29 @@
<?php declare(strict_types=1);
namespace Imagify\Dependencies\League\Container\Argument;
class ClassName implements ClassNameInterface
{
/**
* @var string
*/
protected $value;
/**
* Construct.
*
* @param string $value
*/
public function __construct(string $value)
{
$this->value = $value;
}
/**
* {@inheritdoc}
*/
public function getClassName() : string
{
return $this->value;
}
}

View File

@@ -0,0 +1,13 @@
<?php declare(strict_types=1);
namespace Imagify\Dependencies\League\Container\Argument;
interface ClassNameInterface
{
/**
* Return the class name.
*
* @return string
*/
public function getClassName() : string;
}

View File

@@ -0,0 +1,39 @@
<?php
namespace Imagify\Dependencies\League\Container\Argument;
class ClassNameWithOptionalValue implements ClassNameInterface
{
/**
* @var string
*/
private $className;
/**
* @var mixed
*/
private $optionalValue;
/**
* @param string $className
* @param mixed $optionalValue
*/
public function __construct(string $className, $optionalValue)
{
$this->className = $className;
$this->optionalValue = $optionalValue;
}
/**
* @inheritDoc
*/
public function getClassName(): string
{
return $this->className;
}
public function getOptionalValue()
{
return $this->optionalValue;
}
}

View File

@@ -0,0 +1,29 @@
<?php declare(strict_types=1);
namespace Imagify\Dependencies\League\Container\Argument;
class RawArgument implements RawArgumentInterface
{
/**
* @var mixed
*/
protected $value;
/**
* Construct.
*
* @param mixed $value
*/
public function __construct($value)
{
$this->value = $value;
}
/**
* {@inheritdoc}
*/
public function getValue()
{
return $this->value;
}
}

View File

@@ -0,0 +1,13 @@
<?php declare(strict_types=1);
namespace Imagify\Dependencies\League\Container\Argument;
interface RawArgumentInterface
{
/**
* Return the value of the raw argument.
*
* @return mixed
*/
public function getValue();
}

View File

@@ -0,0 +1,248 @@
<?php declare(strict_types=1);
namespace Imagify\Dependencies\League\Container;
use Imagify\Dependencies\League\Container\Definition\{DefinitionAggregate, DefinitionInterface, DefinitionAggregateInterface};
use Imagify\Dependencies\League\Container\Exception\{NotFoundException, ContainerException};
use Imagify\Dependencies\League\Container\Inflector\{InflectorAggregate, InflectorInterface, InflectorAggregateInterface};
use Imagify\Dependencies\League\Container\ServiceProvider\{
ServiceProviderAggregate,
ServiceProviderAggregateInterface,
ServiceProviderInterface
};
use Imagify\Dependencies\Psr\Container\ContainerInterface;
class Container implements ContainerInterface
{
/**
* @var boolean
*/
protected $defaultToShared = false;
/**
* @var DefinitionAggregateInterface
*/
protected $definitions;
/**
* @var ServiceProviderAggregateInterface
*/
protected $providers;
/**
* @var InflectorAggregateInterface
*/
protected $inflectors;
/**
* @var ContainerInterface[]
*/
protected $delegates = [];
/**
* Construct.
*
* @param DefinitionAggregateInterface|null $definitions
* @param ServiceProviderAggregateInterface|null $providers
* @param InflectorAggregateInterface|null $inflectors
*/
public function __construct(
DefinitionAggregateInterface $definitions = null,
ServiceProviderAggregateInterface $providers = null,
InflectorAggregateInterface $inflectors = null
) {
$this->definitions = $definitions ?? new DefinitionAggregate;
$this->providers = $providers ?? new ServiceProviderAggregate;
$this->inflectors = $inflectors ?? new InflectorAggregate;
if ($this->definitions instanceof ContainerAwareInterface) {
$this->definitions->setLeagueContainer($this);
}
if ($this->providers instanceof ContainerAwareInterface) {
$this->providers->setLeagueContainer($this);
}
if ($this->inflectors instanceof ContainerAwareInterface) {
$this->inflectors->setLeagueContainer($this);
}
}
/**
* Add an item to the container.
*
* @param string $id
* @param mixed $concrete
* @param boolean $shared
*
* @return DefinitionInterface
*/
public function add(string $id, $concrete = null, bool $shared = null) : DefinitionInterface
{
$concrete = $concrete ?? $id;
$shared = $shared ?? $this->defaultToShared;
return $this->definitions->add($id, $concrete, $shared);
}
/**
* Proxy to add with shared as true.
*
* @param string $id
* @param mixed $concrete
*
* @return DefinitionInterface
*/
public function share(string $id, $concrete = null) : DefinitionInterface
{
return $this->add($id, $concrete, true);
}
/**
* Whether the container should default to defining shared definitions.
*
* @param boolean $shared
*
* @return self
*/
public function defaultToShared(bool $shared = true) : ContainerInterface
{
$this->defaultToShared = $shared;
return $this;
}
/**
* Get a definition to extend.
*
* @param string $id [description]
*
* @return DefinitionInterface
*/
public function extend(string $id) : DefinitionInterface
{
if ($this->providers->provides($id)) {
$this->providers->register($id);
}
if ($this->definitions->has($id)) {
return $this->definitions->getDefinition($id);
}
throw new NotFoundException(
sprintf('Unable to extend alias (%s) as it is not being managed as a definition', $id)
);
}
/**
* Add a service provider.
*
* @param ServiceProviderInterface|string $provider
*
* @return self
*/
public function addServiceProvider($provider) : self
{
$this->providers->add($provider);
return $this;
}
/**
* {@inheritdoc}
*/
public function get($id, bool $new = false)
{
if ($this->definitions->has($id)) {
$resolved = $this->definitions->resolve($id, $new);
return $this->inflectors->inflect($resolved);
}
if ($this->definitions->hasTag($id)) {
$arrayOf = $this->definitions->resolveTagged($id, $new);
array_walk($arrayOf, function (&$resolved) {
$resolved = $this->inflectors->inflect($resolved);
});
return $arrayOf;
}
if ($this->providers->provides($id)) {
$this->providers->register($id);
if (!$this->definitions->has($id) && !$this->definitions->hasTag($id)) {
throw new ContainerException(sprintf('Service provider lied about providing (%s) service', $id));
}
return $this->get($id, $new);
}
foreach ($this->delegates as $delegate) {
if ($delegate->has($id)) {
$resolved = $delegate->get($id);
return $this->inflectors->inflect($resolved);
}
}
throw new NotFoundException(sprintf('Alias (%s) is not being managed by the container or delegates', $id));
}
/**
* {@inheritdoc}
*/
public function has($id)
{
if ($this->definitions->has($id)) {
return true;
}
if ($this->definitions->hasTag($id)) {
return true;
}
if ($this->providers->provides($id)) {
return true;
}
foreach ($this->delegates as $delegate) {
if ($delegate->has($id)) {
return true;
}
}
return false;
}
/**
* Allows for manipulation of specific types on resolution.
*
* @param string $type
* @param callable|null $callback
*
* @return InflectorInterface
*/
public function inflector(string $type, callable $callback = null) : InflectorInterface
{
return $this->inflectors->add($type, $callback);
}
/**
* Delegate a backup container to be checked for services if it
* cannot be resolved via this container.
*
* @param ContainerInterface $container
*
* @return self
*/
public function delegate(ContainerInterface $container) : self
{
$this->delegates[] = $container;
if ($container instanceof ContainerAwareInterface) {
$container->setLeagueContainer($this);
}
return $this;
}
}

View File

@@ -0,0 +1,40 @@
<?php declare(strict_types=1);
namespace Imagify\Dependencies\League\Container;
use Imagify\Dependencies\Psr\Container\ContainerInterface;
interface ContainerAwareInterface
{
/**
* Set a container
*
* @param ContainerInterface $container
*
* @return self
*/
public function setContainer(ContainerInterface $container) : ContainerAwareInterface;
/**
* Get the container
*
* @return ContainerInterface
*/
public function getContainer() : ContainerInterface;
/**
* Set a container. This will be removed in favour of setContainer receiving Container in next major release.
*
* @param Container $container
*
* @return self
*/
public function setLeagueContainer(Container $container) : self;
/**
* Get the container. This will be removed in favour of getContainer returning Container in next major release.
*
* @return Container
*/
public function getLeagueContainer() : Container;
}

View File

@@ -0,0 +1,76 @@
<?php declare(strict_types=1);
namespace Imagify\Dependencies\League\Container;
use Imagify\Dependencies\League\Container\Exception\ContainerException;
use Imagify\Dependencies\Psr\Container\ContainerInterface;
trait ContainerAwareTrait
{
/**
* @var ContainerInterface
*/
protected $container;
/**
* @var Container
*/
protected $leagueContainer;
/**
* Set a container.
*
* @param ContainerInterface $container
*
* @return ContainerAwareInterface
*/
public function setContainer(ContainerInterface $container) : ContainerAwareInterface
{
$this->container = $container;
return $this;
}
/**
* Get the container.
*
* @return ContainerInterface
*/
public function getContainer() : ContainerInterface
{
if ($this->container instanceof ContainerInterface) {
return $this->container;
}
throw new ContainerException('No container implementation has been set.');
}
/**
* Set a container.
*
* @param Container $container
*
* @return self
*/
public function setLeagueContainer(Container $container) : ContainerAwareInterface
{
$this->container = $container;
$this->leagueContainer = $container;
return $this;
}
/**
* Get the container.
*
* @return Container
*/
public function getLeagueContainer() : Container
{
if ($this->leagueContainer instanceof Container) {
return $this->leagueContainer;
}
throw new ContainerException('No container implementation has been set.');
}
}

View File

@@ -0,0 +1,278 @@
<?php declare(strict_types=1);
namespace Imagify\Dependencies\League\Container\Definition;
use Imagify\Dependencies\League\Container\Argument\{
ArgumentResolverInterface, ArgumentResolverTrait, ClassNameInterface, RawArgumentInterface
};
use Imagify\Dependencies\League\Container\ContainerAwareTrait;
use ReflectionClass;
use ReflectionException;
class Definition implements ArgumentResolverInterface, DefinitionInterface
{
use ArgumentResolverTrait;
use ContainerAwareTrait;
/**
* @var string
*/
protected $alias;
/**
* @var mixed
*/
protected $concrete;
/**
* @var boolean
*/
protected $shared = false;
/**
* @var array
*/
protected $tags = [];
/**
* @var array
*/
protected $arguments = [];
/**
* @var array
*/
protected $methods = [];
/**
* @var mixed
*/
protected $resolved;
/**
* Constructor.
*
* @param string $id
* @param mixed $concrete
*/
public function __construct(string $id, $concrete = null)
{
$concrete = $concrete ?? $id;
$this->alias = $id;
$this->concrete = $concrete;
}
/**
* {@inheritdoc}
*/
public function addTag(string $tag) : DefinitionInterface
{
$this->tags[$tag] = true;
return $this;
}
/**
* {@inheritdoc}
*/
public function hasTag(string $tag) : bool
{
return isset($this->tags[$tag]);
}
/**
* {@inheritdoc}
*/
public function setAlias(string $id) : DefinitionInterface
{
$this->alias = $id;
return $this;
}
/**
* {@inheritdoc}
*/
public function getAlias() : string
{
return $this->alias;
}
/**
* {@inheritdoc}
*/
public function setShared(bool $shared = true) : DefinitionInterface
{
$this->shared = $shared;
return $this;
}
/**
* {@inheritdoc}
*/
public function isShared() : bool
{
return $this->shared;
}
/**
* {@inheritdoc}
*/
public function getConcrete()
{
return $this->concrete;
}
/**
* {@inheritdoc}
*/
public function setConcrete($concrete) : DefinitionInterface
{
$this->concrete = $concrete;
$this->resolved = null;
return $this;
}
/**
* {@inheritdoc}
*/
public function addArgument($arg) : DefinitionInterface
{
$this->arguments[] = $arg;
return $this;
}
/**
* {@inheritdoc}
*/
public function addArguments(array $args) : DefinitionInterface
{
foreach ($args as $arg) {
$this->addArgument($arg);
}
return $this;
}
/**
* {@inheritdoc}
*/
public function addMethodCall(string $method, array $args = []) : DefinitionInterface
{
$this->methods[] = [
'method' => $method,
'arguments' => $args
];
return $this;
}
/**
* {@inheritdoc}
*/
public function addMethodCalls(array $methods = []) : DefinitionInterface
{
foreach ($methods as $method => $args) {
$this->addMethodCall($method, $args);
}
return $this;
}
/**
* {@inheritdoc}
*/
public function resolve(bool $new = false)
{
$concrete = $this->concrete;
if ($this->isShared() && $this->resolved !== null && $new === false) {
return $this->resolved;
}
if (is_callable($concrete)) {
$concrete = $this->resolveCallable($concrete);
}
if ($concrete instanceof RawArgumentInterface) {
$this->resolved = $concrete->getValue();
return $concrete->getValue();
}
if ($concrete instanceof ClassNameInterface) {
$concrete = $concrete->getClassName();
}
if (is_string($concrete) && class_exists($concrete)) {
$concrete = $this->resolveClass($concrete);
}
if (is_object($concrete)) {
$concrete = $this->invokeMethods($concrete);
}
if (is_string($concrete) && $this->getContainer()->has($concrete)) {
$concrete = $this->getContainer()->get($concrete);
}
$this->resolved = $concrete;
return $concrete;
}
/**
* Resolve a callable.
*
* @param callable $concrete
*
* @return mixed
*/
protected function resolveCallable(callable $concrete)
{
$resolved = $this->resolveArguments($this->arguments);
return call_user_func_array($concrete, $resolved);
}
/**
* Resolve a class.
*
* @param string $concrete
*
* @return object
*
* @throws ReflectionException
*/
protected function resolveClass(string $concrete)
{
$resolved = $this->resolveArguments($this->arguments);
$reflection = new ReflectionClass($concrete);
return $reflection->newInstanceArgs($resolved);
}
/**
* Invoke methods on resolved instance.
*
* @param object $instance
*
* @return object
*/
protected function invokeMethods($instance)
{
foreach ($this->methods as $method) {
$args = $this->resolveArguments($method['arguments']);
/** @var callable $callable */
$callable = [$instance, $method['method']];
call_user_func_array($callable, $args);
}
return $instance;
}
}

View File

@@ -0,0 +1,124 @@
<?php declare(strict_types=1);
namespace Imagify\Dependencies\League\Container\Definition;
use Generator;
use Imagify\Dependencies\League\Container\ContainerAwareTrait;
use Imagify\Dependencies\League\Container\Exception\NotFoundException;
class DefinitionAggregate implements DefinitionAggregateInterface
{
use ContainerAwareTrait;
/**
* @var DefinitionInterface[]
*/
protected $definitions = [];
/**
* Construct.
*
* @param DefinitionInterface[] $definitions
*/
public function __construct(array $definitions = [])
{
$this->definitions = array_filter($definitions, function ($definition) {
return ($definition instanceof DefinitionInterface);
});
}
/**
* {@inheritdoc}
*/
public function add(string $id, $definition, bool $shared = false) : DefinitionInterface
{
if (!$definition instanceof DefinitionInterface) {
$definition = new Definition($id, $definition);
}
$this->definitions[] = $definition
->setAlias($id)
->setShared($shared)
;
return $definition;
}
/**
* {@inheritdoc}
*/
public function has(string $id) : bool
{
foreach ($this->getIterator() as $definition) {
if ($id === $definition->getAlias()) {
return true;
}
}
return false;
}
/**
* {@inheritdoc}
*/
public function hasTag(string $tag) : bool
{
foreach ($this->getIterator() as $definition) {
if ($definition->hasTag($tag)) {
return true;
}
}
return false;
}
/**
* {@inheritdoc}
*/
public function getDefinition(string $id) : DefinitionInterface
{
foreach ($this->getIterator() as $definition) {
if ($id === $definition->getAlias()) {
return $definition->setLeagueContainer($this->getLeagueContainer());
}
}
throw new NotFoundException(sprintf('Alias (%s) is not being handled as a definition.', $id));
}
/**
* {@inheritdoc}
*/
public function resolve(string $id, bool $new = false)
{
return $this->getDefinition($id)->resolve($new);
}
/**
* {@inheritdoc}
*/
public function resolveTagged(string $tag, bool $new = false) : array
{
$arrayOf = [];
foreach ($this->getIterator() as $definition) {
if ($definition->hasTag($tag)) {
$arrayOf[] = $definition->setLeagueContainer($this->getLeagueContainer())->resolve($new);
}
}
return $arrayOf;
}
/**
* {@inheritdoc}
*/
public function getIterator() : Generator
{
$count = count($this->definitions);
for ($i = 0; $i < $count; $i++) {
yield $this->definitions[$i];
}
}
}

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