Merged in feature/Plugin-updates (pull request #19)

Updated plugins

* Updated plugins
This commit is contained in:
Tony Volpe
2024-01-10 21:13:38 +00:00
parent 768ebbdc75
commit 14f187dd80
168 changed files with 9219 additions and 9861 deletions

View File

@@ -1,5 +1,9 @@
<?php
// Include the Settings Page & Update Check
include_once( 'admin/settings.php' );
include_once( 'admin/update-check.php' );
/**
* Admin Scripts and Styles
*

View File

@@ -0,0 +1,770 @@
<?php
/**
* Class CF7DTX_Plugin_Settings
*
* Configure the plugin settings page.
*/
class CF7DTX_Plugin_Settings {
/**
* Capability required by the user to access the My Plugin menu entry.
*
* @var string $capability
*/
private $capability = 'manage_options';
private $sections;
private $fields;
private $num_forms_to_scan = 20;
/**
* The Plugin Settings constructor.
*/
function __construct($sections, $fields) {
add_action( 'admin_init', [$this, 'settings_init'] );
add_action( 'admin_menu', [$this, 'options_page'] );
$this->sections = $sections;
$this->fields = $fields;
}
/**
* Register the settings and all fields.
*/
function settings_init() : void {
// Register a new setting this page.
register_setting( 'cf7dtx_settings', 'cf7dtx_settings' );
foreach( $this->sections as $section_id => $section ){
// Register a new section.
add_settings_section(
$section_id,
$section['title'],
[$this, 'render_section'],
'cf7dtx_settings',
);
}
/* Register All The Fields. */
foreach( $this->fields as $field ) {
// Register a new field in the main section.
add_settings_field(
$field['id'], /* ID for the field. Only used internally. To set the HTML ID attribute, use $args['label_for']. */
$field['label'], /* Label for the field. */
[$this, 'render_field'], /* The name of the callback function. */
'cf7dtx_settings', /* The menu page on which to display this field. */
$field['section'], /* The section of the settings page in which to show the box. */
[
'label_for' => $field['id'], /* The ID of the field. */
'class' => 'cf7dtx_row', /* The class of the field. */
'field' => $field, /* Custom data for the field. */
]
);
}
}
/**
* Add a subpage to the WordPress Settings menu.
*/
function options_page() : void {
add_submenu_page(
'wpcf7', /* Parent Menu Slug */
'Contact Form 7 - Dynamic Text Extension', /* Page Title */
'Dynamic Text Extension', /* Menu Title */
$this->capability, /* Capability */
'cf7dtx_settings', /* Menu Slug */
[$this, 'render_options_page'], /* Callback */
);
}
/**
* Render the settings page.
*/
function render_options_page() : void {
// check user capabilities
if ( ! current_user_can( $this->capability ) ) {
return;
}
if( isset( $_GET['dismiss-access-keys-notice'] )){
wpcf7dtx_set_update_access_scan_check_status('notice_dismissed');
?>
<div class="notice notice-success dtx-notice">
<p><?php _e('Notice Dismissed. You can run the scan any time from the CF7 DTX settings page', 'contact-form-7-dynamic-text-extension'); ?></p>
<p><?php $this->render_back_to_settings_button(); ?></p>
</div>
<?php
return;
}
if( isset( $_GET['scan-meta-keys'] )){
if( isset( $_POST['save-allows'] )){
$r = $this->handle_save_allows();
?>
<div class="wrap">
<h1><?php _e('DTX: Keys Added To Allow List', 'contact-form-7-dynamic-text-extension'); ?></h1>
<?php $this->render_allow_keys_submission($r); ?>
</div>
<?php
}
else{
$offset = isset( $_GET['offset'] ) ? $_GET['offset'] : 0;
$results = wpcf7dtx_scan_forms_for_access_keys( $this->num_forms_to_scan, $offset );
?>
<div class="wrap">
<h1><?php _e('DTX: Form Shortcode Scan Results', 'contact-form-7-dynamic-text-extension'); ?></h1>
<?php $this->render_scan_results($results); ?>
</div>
<?php
}
}
else{
// add error/update messages
// check if the user have submitted the settings
// WordPress will add the "settings-updated" $_GET parameter to the url
if ( isset( $_GET['settings-updated'] ) ) {
// add settings saved message with the class of "updated"
add_settings_error( 'cf7dtx_messages', 'cf7dtx_message', __( 'Settings Saved', 'contact-form-7-dynamic-text-extension' ), 'updated' );
}
// show error/update messages
settings_errors( 'cf7dtx_messages' );
?>
<div class="wrap">
<h1><?php echo esc_html( get_admin_page_title() ); ?></h1>
<form action="options.php" method="post">
<?php
/* output security fields for the registered setting "cf7dtx" */
settings_fields( 'cf7dtx_settings' );
/* output setting sections and their fields */
/* (sections are registered for "cf7dtx", each field is registered to a specific section) */
do_settings_sections( 'cf7dtx_settings' );
/* output save settings button */
submit_button( 'Save Settings' );
?>
</form>
<a href="<?php echo wpcf7dtx_get_admin_scan_screen_url(); ?>">Scan Forms for Post Meta and User Data Keys</a>
</div>
<?php
}
}
/**
* Render a settings field.
*
* @param array $args Args to configure the field.
*/
function render_field( array $args ) : void {
$field = $args['field'];
// Get the value of the setting we've registered with register_setting()
$options = get_option( 'cf7dtx_settings' );
switch ( $field['type'] ) {
case "text": {
?>
<input
type="text"
id="<?php echo esc_attr( $field['id'] ); ?>"
name="cf7dtx_settings[<?php echo esc_attr( $field['id'] ); ?>]"
value="<?php echo isset( $options[ $field['id'] ] ) ? esc_attr( $options[ $field['id'] ] ) : ''; ?>"
>
<p class="description">
<?php esc_html_e( $field['description'], 'cf7dtx_settings' ); ?>
</p>
<?php
break;
}
case "checkbox": {
?>
<input
type="checkbox"
id="<?php echo esc_attr( $field['id'] ); ?>"
name="cf7dtx_settings[<?php echo esc_attr( $field['id'] ); ?>]"
value="1"
<?php echo isset( $options[ $field['id'] ] ) ? ( checked( $options[ $field['id'] ], 1, false ) ) : ( '' ); ?>
>
<p class="description">
<?php esc_html_e( $field['description'], 'cf7dtx_settings' ); ?>
</p>
<?php
break;
}
case "textarea": {
?>
<textarea
id="<?php echo esc_attr( $field['id'] ); ?>"
name="cf7dtx_settings[<?php echo esc_attr( $field['id'] ); ?>]"
style="width:400px; height:200px;"
><?php echo isset( $options[ $field['id'] ] ) ? esc_attr( $options[ $field['id'] ] ) : ''; ?></textarea>
<p class="description">
<?php esc_html_e( $field['description'], 'cf7dtx_settings' ); ?>
</p>
<?php
break;
}
case "select": {
?>
<select
id="<?php echo esc_attr( $field['id'] ); ?>"
name="cf7dtx_settings[<?php echo esc_attr( $field['id'] ); ?>]"
>
<?php foreach( $field['options'] as $key => $option ) { ?>
<option value="<?php echo $key; ?>"
<?php echo isset( $options[ $field['id'] ] ) ? ( selected( $options[ $field['id'] ], $key, false ) ) : ( '' ); ?>
>
<?php echo $option; ?>
</option>
<?php } ?>
</select>
<p class="description">
<?php esc_html_e( $field['description'], 'cf7dtx_settings' ); ?>
</p>
<?php
break;
}
case "password": {
?>
<input
type="password"
id="<?php echo esc_attr( $field['id'] ); ?>"
name="cf7dtx_settings[<?php echo esc_attr( $field['id'] ); ?>]"
value="<?php echo isset( $options[ $field['id'] ] ) ? esc_attr( $options[ $field['id'] ] ) : ''; ?>"
>
<p class="description">
<?php esc_html_e( $field['description'], 'cf7dtx_settings' ); ?>
</p>
<?php
break;
}
case "wysiwyg": {
wp_editor(
isset( $options[ $field['id'] ] ) ? $options[ $field['id'] ] : '',
$field['id'],
array(
'textarea_name' => 'cf7dtx_settings[' . $field['id'] . ']',
'textarea_rows' => 5,
)
);
break;
}
case "email": {
?>
<input
type="email"
id="<?php echo esc_attr( $field['id'] ); ?>"
name="cf7dtx_settings[<?php echo esc_attr( $field['id'] ); ?>]"
value="<?php echo isset( $options[ $field['id'] ] ) ? esc_attr( $options[ $field['id'] ] ) : ''; ?>"
>
<p class="description">
<?php esc_html_e( $field['description'], 'cf7dtx_settings' ); ?>
</p>
<?php
break;
}
case "url": {
?>
<input
type="url"
id="<?php echo esc_attr( $field['id'] ); ?>"
name="cf7dtx_settings[<?php echo esc_attr( $field['id'] ); ?>]"
value="<?php echo isset( $options[ $field['id'] ] ) ? esc_attr( $options[ $field['id'] ] ) : ''; ?>"
>
<p class="description">
<?php esc_html_e( $field['description'], 'cf7dtx_settings' ); ?>
</p>
<?php
break;
}
case "color": {
?>
<input
type="color"
id="<?php echo esc_attr( $field['id'] ); ?>"
name="cf7dtx_settings[<?php echo esc_attr( $field['id'] ); ?>]"
value="<?php echo isset( $options[ $field['id'] ] ) ? esc_attr( $options[ $field['id'] ] ) : ''; ?>"
>
<p class="description">
<?php esc_html_e( $field['description'], 'cf7dtx_settings' ); ?>
</p>
<?php
break;
}
case "date": {
?>
<input
type="date"
id="<?php echo esc_attr( $field['id'] ); ?>"
name="cf7dtx_settings[<?php echo esc_attr( $field['id'] ); ?>]"
value="<?php echo isset( $options[ $field['id'] ] ) ? esc_attr( $options[ $field['id'] ] ) : ''; ?>"
>
<p class="description">
<?php esc_html_e( $field['description'], 'cf7dtx_settings' ); ?>
</p>
<?php
break;
}
}
}
/**
* Render a section on a page, with an ID and a text label.
*
* @since 1.0.0
*
* @param array $args {
* An array of parameters for the section.
*
* @type string $id The ID of the section.
* }
*/
function render_section( array $args ) : void {
?>
<p id="<?php echo esc_attr( $args['id'] ); ?>"><?php echo $this->sections[$args['id']]['description']; ?></p>
<?php
}
function render_scan_results( $results ){
// No forms are using the shortcodes in question
if( !count($results['forms']) ){
wpcf7dtx_set_update_access_scan_check_status( 'intervention_not_required' );
echo '<div class="notice notice-success dtx-notice"><p>'.__('Scan complete. No keys detected.', 'contact-form-7-dynamic-text-extension').'</p></div>';
$this->render_back_to_settings_button();
return;
}
// Check if we need to scan another batch
if( $results['forms_scanned'] === $this->num_forms_to_scan ){
$offset = isset( $_GET['offset'] ) ? $_GET['offset'] : 0;
$next_offset = $offset + $this->num_forms_to_scan;
echo '<div class="notice notice-warning dtx-notice"><p>';
echo sprintf(
__( '%1$s forms scanned. There may be more forms to scan.', 'contact-form-7-dynamic-text-extension' ),
$results['forms_scanned'],
);
echo ' ';
echo '<a href="'.wpcf7dtx_get_admin_scan_screen_url($next_offset).'">'.sprintf(
__( 'Scan %1$s more forms', 'contact-form-7-dynamic-text-extension' ),
$this->num_forms_to_scan
).'</a>';
echo '</p></div>';
}
$settings = wpcf7dtx_get_settings();
$already_allowed_meta_keys = wpcf7dtx_parse_allowed_keys(wpcf7dtx_array_has_key('post_meta_allow_keys', $settings));
$already_allowed_user_keys = wpcf7dtx_parse_allowed_keys(wpcf7dtx_array_has_key('user_data_allow_keys', $settings));
// Check the results ahead of time to see if all of the keys are already in the allow list - if so, nothing to do
$forms = $results['forms'];
$all_keys_allowed = true;
foreach( $forms as $form_id => $r ){
if( count($r['meta_keys'])){
foreach( $r['meta_keys'] as $key ){
if( !in_array( $key, $already_allowed_meta_keys ) ){
$all_keys_allowed = false;
break;
}
}
if( $all_keys_allowed === false ) break;
}
if( count($r['user_keys'])){
foreach( $r['user_keys'] as $key ){
if( !in_array( $key, $already_allowed_user_keys ) ){
$all_keys_allowed = false;
break;
}
}
if( $all_keys_allowed === false ) break;
}
}
if( $all_keys_allowed ){
wpcf7dtx_set_update_access_scan_check_status( 'intervention_completed' );
}
?>
<style>
.postbox,
.dtx-notice{
max-width:600px;
box-sizing:border-box;
}
.postbox-header{
padding:1em;
}
.postbox-header h2{
font-size:14px;
margin:0;
}
.key-disabled{
opacity:.8;
}
</style>
<div>
<?php if( $all_keys_allowed ): ?>
<div class="notice notice-success dtx-notice">
<p><?php
echo sprintf(
__('Scan of %1$s forms complete. All keys detected are already on allow list. No action necessary for these forms.', 'contact-form-7-dynamic-text-extension'),
$results['forms_scanned'],
); ?></p>
</div>
<?php else: ?>
<div class="notice notice-error dtx-notice" style="width:600px; box-sizing:border-box;">
<p><strong><?php _e('Shortcodes accessing potentially sensitive Post Meta or User Data were detected in the forms listed below.', 'contact-form-7-dynamic-text-extension'); ?></strong></p>
<p><?php _e('Only keys on the allow list will return their value when accessed. Attempting to access keys that are not on the allow list via DTX shortcodes will return an empty string and throw a warning message.', 'contact-form-7-dynamic-text-extension'); ?></p>
<p><?php _e('Review the keys below and confirm that you want to allow access, then select meta and/or user keys to add them to the relevant allow list. Any keys for sensitive data should be removed by editing your contact form.', 'contact-form-7-dynamic-text-extension'); ?></p>
<p><?php _e('Note that keys which are already in the allow list are displayed but marked as already selected.', 'contact-form-7-dynamic-text-extension'); ?></p>
<p><a href="<?php echo WPCF7DTX_DATA_ACCESS_KB_URL; ?>" target="_blank"><?php _e('More Information', 'contact-form-7-dynamic-text-extension' ); ?></a></p>
</div>
<?php endif; ?>
<form action="admin.php?page=cf7dtx_settings&scan-meta-keys" method="post">
<?php
settings_fields( 'cf7dtx_settings' );
foreach( $results['forms'] as $form_id => $r ){
?>
<div class="postbox" >
<div class="postbox-header">
<h2><?php echo $r['title']; ?></h2>
<a href="<?php echo $r['admin_url'];?>" target="_blank">View form</a>
</div>
<div class="inside">
<?php if( count($r['meta_keys']) ): ?>
<h4>Meta Keys</h3>
<div>
<?php foreach( $r['meta_keys'] as $key ){
$already_allowed = in_array( $key, $already_allowed_meta_keys );
$name = "dtx_meta_key/$key";
?>
<div>
<label <?php if( $already_allowed ) echo 'class="key-disabled" title="Already in Allow List"'; ?>>
<input
name="<?php echo $name;?>"
id="<?php echo $name;?>"
type="checkbox"
value="1"
<?php if( $already_allowed ) echo 'checked="checked" disabled'; ?> />
<?php echo $key; ?>
</label>
</div>
<?php
}
?>
</div>
<?php endif; ?>
<?php if( count($r['user_keys']) ): ?>
<h4>User Data Keys</h3>
<div>
<?php foreach( $r['user_keys'] as $key ){
$name = "dtx_user_key/$key";
$already_allowed = in_array( $key, $already_allowed_user_keys );
?>
<div>
<label <?php if( $already_allowed ) echo 'class="key-disabled" title="Already in Allow List"'; ?>>
<input
name="<?php echo $name; ?>"
id="<?php echo $name; ?>"
type="checkbox"
value="1"
<?php if( $already_allowed ) echo 'checked="checked" disabled'; ?> />
<?php echo $key; ?>
</label>
</div>
<?php
}
?>
</div>
<?php endif; ?>
</div>
</div>
<?php
}
?>
<?php if( !$all_keys_allowed ) submit_button( __('Add Selected Keys to Allow Lists', 'contact-form-7-dynamic-text-extension'), 'primary', 'save-allows' ); ?>
</form>
<?php $this->render_back_to_settings_button(); ?>
</div>
<?php
}
function handle_save_allows(){
$user_keys = [];
$meta_keys = [];
// Find saved keys
foreach( $_POST as $key => $val ){
if( str_starts_with( $key, 'dtx_meta_key' ) ){
$parts = explode( '/', $key);
$meta_keys[] = $parts[1];
}
else if( str_starts_with( $key, 'dtx_user_key' )){
$parts = explode( '/', $key);
$user_keys[] = $parts[1];
}
}
// Add those keys in options
$settings = wpcf7dtx_get_settings();
// Meta Data
if( count( $meta_keys ) ){
// Get already saved values
$post_meta_allow_keys = isset( $settings['post_meta_allow_keys'] ) ? wpcf7dtx_parse_allowed_keys($settings['post_meta_allow_keys']) : [];
// Merge with new values
$new = array_unique( array_merge( $post_meta_allow_keys, $meta_keys ));
$settings['post_meta_allow_keys'] = implode( PHP_EOL, $new );
}
// User Data
if( count( $user_keys ) ){
// Get already saved values
$user_data_allow_keys = isset( $settings['user_data_allow_keys'] ) ? wpcf7dtx_parse_allowed_keys($settings['user_data_allow_keys']) : [];
// Merge with new values
$new = array_unique( array_merge( $user_data_allow_keys, $user_keys ));
$settings['user_data_allow_keys'] = implode( PHP_EOL, $new );
}
// Update with new settings
wpcf7dtx_update_settings( $settings );
// Mark as intervention complete
wpcf7dtx_set_update_access_scan_check_status( 'intervention_completed' );
return [
'user' => $user_keys,
'meta' => $meta_keys,
];
}
function render_allow_keys_submission( $r ){
?>
<?php if( count($r['meta'])): ?>
<p><?php _e('Meta Keys Added','contact-form-7-dynamic-text-extension'); ?>: <?php echo implode(', ',$r['meta']); ?></p>
<?php endif; ?>
<?php if( count( $r['user'])): ?>
<p><?php _e('User Data Keys Added','contact-form-7-dynamic-text-extension'); ?>: <?php echo implode(', ', $r['user']); ?></p>
<?php endif; ?>
<?php if( !count($r['meta']) && !count($r['user'])): ?>
<p><?php _e('No Keys Selected', 'contact-form-7-dynamic-text-extension'); ?></p>
<?php endif; ?>
<?php
$this->render_back_to_settings_button();
}
function render_back_to_settings_button(){
?>
<a href="<?php echo wpcf7dtx_get_admin_settings_screen_url(); ?>">&laquo; <?php _e('Back to Settings', 'contact-form-7-dynamic-text-extension'); ?></a>
<?php
}
}
$sections = [
'post_meta_access' => [
'title' => __('Post Meta Access', 'contact-form-7-dynamic-text-extension'),
'description' => __('Control which post metadata the CF7 DTX shortcodes (CF7_get_custom_field) can access. By default, all metadata is protected, so you can open up access through these settings. Keep in mind that users with Contributor+ credentials can add shortcodes and therefore access this data, so make sure not to expose anything sensitive.').
' <a href="'.WPCF7DTX_DATA_ACCESS_KB_URL.'" target="_blank">'. __('More Information', 'contact-form-7-dynamic-text-extension' ).'</a>',
],
'user_data_access' => [
'title' => __('User Data Access', 'contact-form-7-dynamic-text-extension'),
'description' => __('Control which user data the CF7 DTX shortcodes (CF7_get_current_user) can access. By default, all user data is protected, so you can open up access through these settings. Keep in mind that users with Contributor+ credentials can add shortcodes and therefore access this data, so make sure not to expose anything sensitive.').
' <a href="'.WPCF7DTX_DATA_ACCESS_KB_URL.'" target="_blank">'. __('More Information', 'contact-form-7-dynamic-text-extension' ).'</a>',
],
];
/**
* Array of fields that should be displayed in the settings page.
*
* @var array $fields
*/
$fields = [
[
'id' => 'post_meta_allow_keys',
'label' => __('Meta Key Allow List', 'contact-form-7-dynamic-text-extension'),
'description' => __('Allow access to these specific post metadata keys. Enter one per line.', 'contact-form-7-dynamic-text-extension'),
'type' => 'textarea',
'section' => 'post_meta_access',
],
[
'id' => 'post_meta_allow_all',
'label' => __('Allow Access to All Post Metadata', 'contact-form-7-dynamic-text-extension'),
'description' => __('**Use with caution.** Should only be enabled if all authorized users with editor privileges (Contributor+) are trusted and should have access to this data. All metadata from any post (including custom post types) will be accessible via the CF7_get_custom_field shortcode. If in doubt, use the Allow List to allow only specific keys.', 'contact-form-7-dynamic-text-extension'),
'type' => 'select',
'options' => [
'disabled' => __( 'Disabled - Only Allow Access to Meta Key Allow List', 'contact-form-7-dynamic-text-extension' ),
'enabled' => __( 'Enabled - Allow Access to All Post Metadata', 'contact-form-7-dynamic-text-extension' ),
],
'section' => 'post_meta_access',
],
[
'id' => 'user_data_allow_keys',
'label' => __('User Data Key Allow List', 'contact-form-7-dynamic-text-extension'),
'description' => __('Allow access to these specific user data keys. Enter one per line.', 'contact-form-7-dynamic-text-extension'),
'type' => 'textarea',
'section' => 'user_data_access',
],
[
'id' => 'user_data_allow_all',
'label' => __('Allow Access to All User Data', 'contact-form-7-dynamic-text-extension'),
'description' => __('**Use with caution.** Should only be enabled if all authorized users with editor privileges (Contributor+) are trusted and should have access to this data. All of the current user\'s data fields will be accessible via the CF7_get_current_user shortcode. If in doubt, use the Allow List to allow only specific keys.', 'contact-form-7-dynamic-text-extension'),
'type' => 'select',
'options' => [
'disabled' => __( 'Disabled - Only Allow Access to User Data Key Allow List', 'contact-form-7-dynamic-text-extension' ),
'enabled' => __( 'Enabled - Allow Access to User Data', 'contact-form-7-dynamic-text-extension' ),
],
'section' => 'user_data_access',
],
];
new CF7DTX_Plugin_Settings($sections, $fields);
function wpcf7dtx_get_admin_scan_screen_url($offset=0){
$path = 'admin.php?page=cf7dtx_settings&scan-meta-keys';
if( $offset ){
$path.= '&offset='.$offset;
}
return admin_url($path);
}
function wpcf7dtx_get_admin_settings_screen_url(){
return admin_url('admin.php?page=cf7dtx_settings');
}
/**
* Search all CF7 forms for
*/
function wpcf7dtx_scan_forms_for_access_keys( $num, $offset=0){
$found = [
'forms' => [],
];
$forms = [];
if( function_exists('wpcf7_contact_form') ){
$cf7forms = get_posts([
'post_type' => 'wpcf7_contact_form',
// 'numberposts' => $numposts, // sanity check
'posts_per_page' => $num,
'offset' => $offset,
]);
$found['forms_scanned'] = count($cf7forms);
// Loop through forms
foreach( $cf7forms as $form ){
// Search for the custom fields shortcode
if( str_contains($form->post_content, 'CF7_get_custom_field') ||
str_contains($form->post_content, 'CF7_get_current_user')
){
$cf7 = wpcf7_contact_form( $form->ID );
$forms[$form->ID] = [
'title' => $cf7->title(),
'meta_keys' => [],
'user_keys' => [],
'admin_url' => admin_url( "admin.php?page=wpcf7&post={$form->ID}&action=edit" ),
];
$tags = $cf7->scan_form_tags();
// Check each tag
foreach( $tags as $tag ){
// Find dynamic tags
if( str_starts_with( $tag->type, 'dynamic' ) ){
// Check each value
foreach( $tag->values as $val ){
// Find CF7_get_custom_field
if( str_starts_with( $val, 'CF7_get_custom_field' )){
// Parse out the shortcode atts
$atts = shortcode_parse_atts($val);
if( $atts ){
// Grab the meta key
$meta_key = $atts['key'];
// Add meta key to the list
if( $meta_key ){
$forms[$form->ID]['meta_keys'][] = $meta_key;
}
}
}
// Find CF7_get_current_user
if( str_starts_with( $val, 'CF7_get_current_user' )){
// Parse out the shortcode atts
$atts = shortcode_parse_atts($val);
if( $atts ){
// Grab user data key
$key = $atts['key'];
if( $key ){
$forms[$form->ID]['user_keys'][] = $key;
}
}
}
}
}
}
}
}
}
$found['forms'] = $forms;
return $found;
}

View File

@@ -0,0 +1,97 @@
<?php
add_action( 'plugins_loaded', 'wpcf7dtx_update_check' );
function wpcf7dtx_update_check(){
if( WPCF7DTX_VERSION !== get_option( 'cf7dtx_version', '' ) ){
// Update the database version with the current plugin version
update_option( 'cf7dtx_version', WPCF7DTX_VERSION );
// Run the update handler
add_action('admin_init', 'wpcf7dtx_update');
}
}
function wpcf7dtx_update(){
// v4.2.0 will scan for meta and user keys that should be allow-listed and display an admin alert
wpcf7dtx_v4_2_0_access_scan_check();
// Future update processes would go here
}
/*** 4.2.0 - Security Access ***/
function wpcf7dtx_v4_2_0_access_scan_check(){
$op = 'cf7dtx_v4_2_0_access_scan_check_status';
$status = get_option( $op, '' );
// Status values:
// intervention_required - show a notice to the admin
// intervention_not_required - we can ignore
// intervention_completed - no need to show notice any longer
// notice_dismissed - alert was dismissed by user
// If we've never checked before
if( $status === '' ){
// Run a scan - 20 by default. If they have more than 20 forms, we'll alert regardless.
// For less than 20 forms, we'll only alert if we detect an issue
$num_to_scan = 20;
$r = wpcf7dtx_scan_forms_for_access_keys( $num_to_scan );
$found = count($r['forms']);
$scanned = $r['forms_scanned'];
// If keys were found, or if we scanned the max number (so there are likely more to be scanned)
if( $found || $scanned === $num_to_scan ){
// We'll show a notice to the user
$status = 'intervention_required';
}
else{
// No keys need to be allow-listed, no need to show the user a list
$status = 'intervention_not_required';
}
wpcf7dtx_set_update_access_scan_check_status( $status );
}
}
add_action('admin_notices', 'wpcf7dtx_access_keys_notice');
/**
* Display an admin notice if there are unresolved issues with accessing disallowed keys via DTX shortcodes
*/
function wpcf7dtx_access_keys_notice(){
// Don't show this notice on the Scan Results screen to avoid confusion
if( isset($_GET['page']) && $_GET['page'] === 'cf7dtx_settings' && ( isset( $_GET['scan-meta-keys']) || isset($_GET['dismiss-access-keys-notice']))) return;
// If this user is not an administrator, don't do anything. Only admins should see this.
$user = wp_get_current_user();
if ( !in_array( 'administrator', (array) $user->roles ) ) return;
// If the status doesn't require intervention, don't do anything
$status = get_option( 'cf7dtx_v4_2_0_access_scan_check_status', '' );
if( $status !== 'intervention_required' ){
return;
}
?>
<div class="notice notice-error">
<p>
<?php _e('CF7 DTX: Shortcode data access requires allow-listing.', 'contact-form-7-dynamic-text-extension'); ?>
<a href="<?php echo wpcf7dtx_get_admin_settings_screen_url(); ?>"><?php _e('Edit Settings', 'contact-form-7-dynamic-text-extension' ); ?></a>
|
<a href="<?php echo wpcf7dtx_get_admin_scan_screen_url(); ?>"><?php _e('Scan &amp; Resolve', 'contact-form-7-dynamic-text-extension' ); ?></a>
|
<a href="<?php echo WPCF7DTX_DATA_ACCESS_KB_URL; ?>" target="_blank"><?php _e('More Information', 'contact-form-7-dynamic-text-extension' ); ?></a>
<?php if( isset($_GET['page']) && $_GET['page'] === 'cf7dtx_settings' ): ?>
| <a href="<?php echo admin_url('admin.php?page=cf7dtx_settings&dismiss-access-keys-notice'); ?>"><?php _e('Dismiss', 'contact-form-7-dynamic-text-extension' ); ?></a>
<?php endif; ?>
</p>
</div>
<?php
}
function wpcf7dtx_set_update_access_scan_check_status( $status ){
update_option( 'cf7dtx_v4_2_0_access_scan_check_status', $status );
}

View File

@@ -214,6 +214,14 @@ function wpcf7dtx_get_custom_field($atts = array())
'post_id' => '',
'obfuscate' => ''
), array_change_key_case((array)$atts, CASE_LOWER)));
// If this key can't be accessed
if( !wpcf7dtx_post_meta_key_access_is_allowed( $key ) ){
// Trigger a warning if a denied key is in use
wpcf7dtx_access_denied_alert( $key, 'post_meta' );
return '';
}
$post_id = wpcf7dtx_get_post_id($post_id);
$key = apply_filters('wpcf7dtx_sanitize', $key, 'text');
if ($post_id && $key) {
@@ -343,6 +351,14 @@ function wpcf7dtx_get_current_user($atts = array())
'obfuscate' => ''
), array_change_key_case((array)$atts, CASE_LOWER)));
if (is_user_logged_in()) {
// If this key can't be accessed
if( !wpcf7dtx_user_data_access_is_allowed( $key ) ){
// Trigger a warning if a denied key is in use
wpcf7dtx_access_denied_alert( $key, 'user_data' );
return '';
}
$user = wp_get_current_user();
return apply_filters('wpcf7dtx_escape', $user->get($key), $obfuscate);
}

View File

@@ -661,3 +661,216 @@ function wpcf7dtx_array_has_key($key, $array = array(), $default = '')
}
return $default;
}
/**
* Check if admin has allowed access to a specific post meta key
*
* @since 4.2.0
*
* @param string $meta_key The post meta key to test
*
* @return bool True if this key can be accessed, false otherwise
*/
function wpcf7dtx_post_meta_key_access_is_allowed($meta_key)
{
// Get the DTX Settings
$settings = wpcf7dtx_get_settings();get_option('cf7dtx_settings', []);
// Has access to all metadata been enabled?
if( isset($settings['post_meta_allow_all']) && $settings['post_meta_allow_all'] === 'enabled' ){
return true;
}
// If not, check the Allow List
$allowed_keys;
// No key list from settings
if( !isset($settings['post_meta_allow_keys'] ) || !is_string($settings['post_meta_allow_keys'])){
$allowed_keys = [];
}
// Extract allowed keys from setting text area
else{
// $allowed_keys = preg_split('/\r\n|\r|\n/', $settings['post_meta_allow_keys']);
$allowed_keys = wpcf7dtx_parse_allowed_keys( $settings['post_meta_allow_keys'] );
}
// Allow custom filters
$allowed_keys = apply_filters( 'wpcf7dtx_post_meta_key_allow_list', $allowed_keys );
// Check if the key is in the allow list
if( in_array( $meta_key, $allowed_keys ) ){
return true; // The key is allowed
}
// Everything is disallowed by default
return false;
}
/**
* Check if admin has allowed access to a specific user data
*
* @since 4.2.0
*
* @param string $key The user data key to test
*
* @return bool True if this key can be accessed, false otherwise
*/
function wpcf7dtx_user_data_access_is_allowed( $key )
{
// Get the DTX Settings
$settings = wpcf7dtx_get_settings(); //get_option('cf7dtx_settings', []);
// Has access to all metadata been enabled?
if( isset($settings['user_data_allow_all']) && $settings['user_data_allow_all'] === 'enabled' ){
return true;
}
// If not, check the Allow List
$allowed_keys;
// No key list from settings
if( !isset($settings['user_data_allow_keys'] ) || !is_string($settings['user_data_allow_keys'])){
$allowed_keys = [];
}
// Extract allowed keys from setting text area
else{
// $allowed_keys = preg_split('/\r\n|\r|\n/', $settings['user_data_allow_keys']);
$allowed_keys = wpcf7dtx_parse_allowed_keys($settings['user_data_allow_keys']);
}
// Allow custom filters
$allowed_keys = apply_filters( 'wpcf7dtx_user_data_key_allow_list', $allowed_keys );
// Check if the key is in the allow list
if( in_array( $key, $allowed_keys ) ){
return true; // The key is allowed
}
// Everything is disallowed by default
return false;
}
/**
* Take the string saved in the options array from the allow list textarea and parse it into an array by newlines.
* Also strip whitespace
*
* @param string $allowlist The string of allowed keys stored in the DB
*
* @return array Array of allowed keys
*/
function wpcf7dtx_parse_allowed_keys( $allowlist ){
// Split by newlines
$keys = wpcf7dtx_split_newlines( $allowlist );
// Trim whitespace
$keys = array_map( 'trim' , $keys );
return $keys;
}
/**
* Used to parse strings stored in the database that are from text areas with one element per line into an array of strings
*
* @param string $str The multi-line string to be parsed into an array
*
* @return array Array of parsed strings
*/
function wpcf7dtx_split_newlines( $str ){
return preg_split('/\r\n|\r|\n/', $str);
}
/**
* Gets the CF7 DTX settings field from the WP options table. Returns an empty array if option has not previously been set
*
* @return array The settings array
*/
function wpcf7dtx_get_settings(){
return get_option('cf7dtx_settings', []);
}
/**
* Updates the CF7 DTX settings in the WP options table
*
* @param array $settings The settings array
*
*/
function wpcf7dtx_update_settings($settings){
update_option( 'cf7dtx_settings', $settings );
}
/**
* Outputs a useful PHP Warning message to users on how to allow-list denied meta and user keys
*
* @param string $key The post meta or user key to which access is currently denied
* @param string $type Either 'post_meta' or 'user_data', used to display an appropriate message to the user
*/
function wpcf7dtx_access_denied_alert( $key, $type ){
// Only check on the front end
if( is_admin() || wp_doing_ajax() || wp_is_json_request() ) return;
$shortcode = '';
$list_name = '';
switch( $type ){
case 'post_meta':
$shortcode = 'CF7_get_custom_field';
$list_name = __('Meta Key Allow List', 'contact-form-7-dynamic-text-extension');
break;
case 'user_data':
$shortcode = 'CF7_get_current_user';
$list_name = __('User Data Key Allow List', 'contact-form-7-dynamic-text-extension');
break;
default:
}
$settings_page_url = admin_url('admin.php?page=cf7dtx_settings');
$msg = sprintf(
__('CF7 DTX: Access denied to key: "%1$s" in dynamic contact form shortcode: [%2$s]. Please add this key to the %3$s at %4$s','contact-form-7-dynamic-text-extension'),
$key,
$shortcode,
$list_name,
$settings_page_url
);
trigger_error( $msg, E_USER_WARNING );
}
/**
* Helper function to output array and object data
*/
/*
function dtxpretty ($var, $print=true, $privobj=false) {
$type = gettype($var);
if( $privobj && $type === 'object' ){
$p = '<pre>'.print_r($var, true).'</pre>';
}
else {
$p = '<pre>'.$type . ' ' . json_encode(
$var,
JSON_UNESCAPED_SLASHES |
JSON_UNESCAPED_UNICODE |
JSON_PRETTY_PRINT |
JSON_PARTIAL_OUTPUT_ON_ERROR |
JSON_INVALID_UTF8_SUBSTITUTE
).'</pre>';
}
if( $print ) {
echo $p;
}
return $p;
}
*/

View File

@@ -1,233 +1,369 @@
<?php
/**
* Add Frontend Validation Messages
*
* @since 4.0.0
*
* @param array An associative array of messages
*
* @return array A modified associative array of messages
*/
function wpcf7dtx_messages($messages)
{
return array_merge($messages, array(
'dtx_invalid_email' => array(
'description' => __('There is a field with an invalid email address', 'contact-form-7-dynamic-text-extension'),
'default' => __('Please enter a valid email address.', 'contact-form-7-dynamic-text-extension')
),
'dtx_invalid_tel' => array(
'description' => __('There is a field with an invalid phone number', 'contact-form-7-dynamic-text-extension'),
'default' => __('Please enter a valid phone number.', 'contact-form-7-dynamic-text-extension')
),
'dtx_invalid_number' => array(
'description' => __('There is a field with an invalid number', 'contact-form-7-dynamic-text-extension'),
'default' => __('Please enter a valid number.', 'contact-form-7-dynamic-text-extension')
),
'dtx_invalid_date' => array(
'description' => __('There is a field with an invalid date', 'contact-form-7-dynamic-text-extension'),
'default' => __('Please enter a valid date.', 'contact-form-7-dynamic-text-extension')
),
));
}
add_filter('wpcf7_messages', 'wpcf7dtx_messages');
/**
* Validate DTX Form Fields
*
* Frontend validation for DTX form tags
*
* @param WPCF7_Validation $result the current validation result object
* @param WPCF7_FormTag $tag the current form tag being filtered for validation
*
* @return WPCF7_Validation a possibly modified validation result object
*/
function wpcf7dtx_validation_filter($result, $tag)
{
$type = str_replace(array('dynamic_', 'dynamic'), '', $tag->basetype);
if (empty($tag->name) || in_array($type, array('hidden', 'submit', 'reset'))) {
return $result; // Bail early for tags without names or if a specific type
}
// Get the value
$user_value = wpcf7dtx_array_has_key($tag->name, $_POST);
if (is_array($user_value)) {
$selection_count = count($user_value);
if (!wpcf7_form_tag_supports($tag->type, 'selectable-values')) {
// Field passed selectable values when it's doesn't support them
$result->invalidate($tag, wpcf7_get_message('validation_error'));
return $result;
} elseif ($selection_count > 1) {
if (!wpcf7_form_tag_supports($tag->type, 'multiple-controls-container')) {
// Field passed multiple values when it's doesn't support them
$result->invalidate($tag, wpcf7_get_message('validation_error'));
return $result;
}
foreach ($user_value as $selection) {
// Validate each selected choice
$result = wpcf7dtx_validate_value($result, sanitize_textarea_field(strval($selection)), $tag, $type);
if (!$result->is_valid($tag->name)) {
return $result; // Return early if any are invalid
}
}
return $result;
}
$user_value = sanitize_text_field(strval(implode(' ', $user_value)));
} elseif ($type == 'textarea') {
$user_value = sanitize_textarea_field(strval($user_value));
} else {
$user_value = sanitize_text_field(strval($user_value));
}
// Validate and return
return wpcf7dtx_validate_value($result, $user_value, $tag, $type);
}
/**
* Validate Single Value
*
* @param WPCF7_Validation $result the current validation result object
* @param string $value the current value being validated, sanitized
* @param WPCF7_FormTag $tag the current form tag being filtered for validation
* @param string $type Optional. The type of the current form tag. Default is blank for lookup.
*
* @return WPCF7_Validation a possibly modified validation result object
*/
function wpcf7dtx_validate_value($result, $value, $tag, $type = '')
{
$type = $type ? $type : str_replace(array('dynamic_', 'dynamic'), '', $tag->basetype);
// Validate required fields for value
if ($tag->is_required() && empty($value)) {
$result->invalidate($tag, wpcf7_get_message('invalid_required'));
return $result;
}
// Validate value by type
if (!empty($value)) {
switch ($type) {
case 'email':
if (!wpcf7_is_email($value)) {
$result->invalidate($tag, wpcf7_get_message('dtx_invalid_email'));
return $result;
}
break;
case 'tel':
if (!wpcf7_is_tel($value)) {
$result->invalidate($tag, wpcf7_get_message('dtx_invalid_tel'));
return $result;
}
break;
case 'number':
case 'range':
if (!wpcf7_is_number($value)) {
$result->invalidate($tag, wpcf7_get_message('dtx_invalid_number'));
return $result;
}
break;
case 'date':
if (!wpcf7_is_date($value)) {
$result->invalidate($tag, wpcf7_get_message('dtx_invalid_date'));
return $result;
}
break;
}
// Finish validating text-based inputs
$maxlength = $tag->get_maxlength_option();
$minlength = $tag->get_minlength_option();
if ($maxlength && $minlength && $maxlength < $minlength) {
$maxlength = $minlength = null;
}
$code_units = wpcf7_count_code_units($value);
if (false !== $code_units) {
if ($maxlength && $maxlength < $code_units) {
$result->invalidate($tag, wpcf7_get_message('invalid_too_long'));
return $result;
} elseif ($minlength && $code_units < $minlength) {
$result->invalidate($tag, wpcf7_get_message('invalid_too_short'));
return $result;
}
}
}
return $result;
}
/**
* Backend Mail Configuration Validation
*
* Validate dynamic form tags used in mail configuration.
*
* @since 4.0.0
*
* @param WPCF7_ConfigValidator
*
* @return void
*/
function wpcf7dtx_validate($validator)
{
if (!$validator->is_valid()) {
$contact_form = null;
$form_tags = null;
foreach ($validator->collect_error_messages() as $component => $errors) {
$components = explode('.', $component);
if (count($components) === 2 && strpos($components[0], 'mail') === 0 && in_array($components[1], array('sender', 'recipient', 'additional_headers'))) {
foreach ($errors as $error) {
// Focus on email fields that flag the invalid mailbox syntax warning, have to test link because code isn't sent and message could be in any language
if (strpos(wpcf7dtx_array_has_key('link', $error), 'invalid-mailbox-syntax') !== false) {
if (is_null($contact_form)) {
$contact_form = $validator->contact_form();
}
if (is_null($form_tags)) {
$form_tags = wpcf7_scan_form_tags();
}
$raw_value = $contact_form->prop($components[0])[$components[1]];
foreach ($form_tags as $tag) {
if (!empty($tag->name)) {
// Check if this form tag is in the raw value
$form_tag = '[' . $tag->name . ']';
if (strpos($raw_value, $form_tag) !== false && in_array($tag->basetype, array_keys(wpcf7dtx_config()))) {
$validator->remove_error($component, 'invalid_mailbox_syntax'); // Remove error, this is ours to handle now
$utm_source = urlencode(home_url());
if (!in_array($tag->basetype, array('dynamic_hidden', 'dynamic_email'))) {
$validator->add_error($component, 'invalid_mailbox_syntax', array(
'message' => __('Only email, dynamic email, hidden, or dynamic hidden form tags can be used for email addresses.', 'contact-form-7-dynamic-text-extension'),
'link' => esc_url(sprintf('https://aurisecreative.com/docs/contact-form-7-dynamic-text-extension/configuration-errors/?utm_source=%s&utm_medium=link&utm_campaign=contact-form-7-dynamic-text-extension&utm_content=config-error-invalid_mailbox_syntax#valid-form-tags', $utm_source))
));
} else {
$dynamic_value = wpcf7dtx_get_dynamic(false, $tag); // Get the dynamic value of this tag
if (empty($dynamic_value) && $tag->basetype == 'dynamic_hidden') {
$validator->add_error($component, 'maybe_empty', array(
'message' => __('The dynamic hidden form tag must have a default value.', 'contact-form-7-dynamic-text-extension'),
'link' => esc_url(sprintf('https://aurisecreative.com/docs/contact-form-7-dynamic-text-extension/configuration-errors/?utm_source=%s&utm_medium=link&utm_campaign=contact-form-7-dynamic-text-extension&utm_content=config-error-maybe_empty#maybe-empty', $utm_source))
));
} elseif (empty($dynamic_value) && !$tag->is_required()) {
$validator->add_error($component, 'maybe_empty', array(
'message' => __('The dynamic form tag must be required or have a default value.', 'contact-form-7-dynamic-text-extension'),
'link' => esc_url(sprintf('https://aurisecreative.com/docs/contact-form-7-dynamic-text-extension/configuration-errors/?utm_source=%s&utm_medium=link&utm_campaign=contact-form-7-dynamic-text-extension&utm_content=config-error-maybe_empty#maybe-empty', $utm_source))
));
} elseif (!empty($dynamic_value)) {
if (!wpcf7_is_email($dynamic_value)) {
$validator->add_error($component, 'invalid_mailbox_syntax', array(
'message' => __('The default dynamic value does not result in a valid email address.', 'contact-form-7-dynamic-text-extension'),
'link' => esc_url(sprintf('https://aurisecreative.com/docs/contact-form-7-dynamic-text-extension/configuration-errors/?utm_source=%s&utm_medium=link&utm_campaign=contact-form-7-dynamic-text-extension&utm_content=config-error-invalid_mailbox_syntax#invalid-email-address', $utm_source))
));
} elseif ($component[1] == 'sender' && !wpcf7_is_email_in_site_domain($dynamic_value)) {
$validator->add_error($component, 'email_not_in_site_domain', array(
'message' => __('The dynamic email address for the sender does not belong to the site domain.', 'contact-form-7-dynamic-text-extension'),
'link' => esc_url(sprintf('https://aurisecreative.com/docs/contact-form-7-dynamic-text-extension/configuration-errors/?utm_source=%s&utm_medium=link&utm_campaign=contact-form-7-dynamic-text-extension&utm_content=config-error-email_not_in_site_domain#invalid-site-domain', $utm_source))
));
}
}
}
}
}
}
}
}
}
}
}
}
add_action('wpcf7_config_validator_validate', 'wpcf7dtx_validate');
<?php
/**
* Add Frontend Validation Messages
*
* @since 4.0.0
*
* @param array An associative array of messages
*
* @return array A modified associative array of messages
*/
function wpcf7dtx_messages($messages)
{
return array_merge($messages, array(
'dtx_invalid_email' => array(
'description' => __('There is a field with an invalid email address', 'contact-form-7-dynamic-text-extension'),
'default' => __('Please enter a valid email address.', 'contact-form-7-dynamic-text-extension')
),
'dtx_invalid_tel' => array(
'description' => __('There is a field with an invalid phone number', 'contact-form-7-dynamic-text-extension'),
'default' => __('Please enter a valid phone number.', 'contact-form-7-dynamic-text-extension')
),
'dtx_invalid_number' => array(
'description' => __('There is a field with an invalid number', 'contact-form-7-dynamic-text-extension'),
'default' => __('Please enter a valid number.', 'contact-form-7-dynamic-text-extension')
),
'dtx_invalid_date' => array(
'description' => __('There is a field with an invalid date', 'contact-form-7-dynamic-text-extension'),
'default' => __('Please enter a valid date.', 'contact-form-7-dynamic-text-extension')
),
));
}
add_filter('wpcf7_messages', 'wpcf7dtx_messages');
/**
* Add DTX Error Code to Config Validator
*
* @since 5.0.0
*
* @param array $error_codes A sequential array of available error codes in Contact Form 7.
*
* @return array A modified sequential array of available error codes in Contact Form 7.
*/
function wpcf7dtx_config_validator_available_error_codes($error_codes)
{
$dtx_errors = array('dtx_disallowed');
return array_merge($error_codes, $dtx_errors);
}
add_filter('wpcf7_config_validator_available_error_codes', 'wpcf7dtx_config_validator_available_error_codes');
/**
* Validate DTX Form Fields
*
* Frontend validation for DTX form tags
*
* @param WPCF7_Validation $result the current validation result object
* @param WPCF7_FormTag $tag the current form tag being filtered for validation
*
* @return WPCF7_Validation a possibly modified validation result object
*/
function wpcf7dtx_validation_filter($result, $tag)
{
$type = str_replace(array('dynamic_', 'dynamic'), '', $tag->basetype);
if (empty($tag->name) || in_array($type, array('hidden', 'submit', 'reset'))) {
return $result; // Bail early for tags without names or if a specific type
}
// Get the value
$user_value = wpcf7dtx_array_has_key($tag->name, $_POST);
if (is_array($user_value)) {
$selection_count = count($user_value);
if (!wpcf7_form_tag_supports($tag->type, 'selectable-values')) {
// Field passed selectable values when it's doesn't support them
$result->invalidate($tag, wpcf7_get_message('validation_error'));
return $result;
} elseif ($selection_count > 1) {
if (!wpcf7_form_tag_supports($tag->type, 'multiple-controls-container')) {
// Field passed multiple values when it's doesn't support them
$result->invalidate($tag, wpcf7_get_message('validation_error'));
return $result;
}
foreach ($user_value as $selection) {
// Validate each selected choice
$result = wpcf7dtx_validate_value($result, sanitize_textarea_field(strval($selection)), $tag, $type);
if (!$result->is_valid($tag->name)) {
return $result; // Return early if any are invalid
}
}
return $result;
}
$user_value = sanitize_text_field(strval(implode(' ', $user_value)));
} elseif ($type == 'textarea') {
$user_value = sanitize_textarea_field(strval($user_value));
} else {
$user_value = sanitize_text_field(strval($user_value));
}
// Validate and return
return wpcf7dtx_validate_value($result, $user_value, $tag, $type);
}
/**
* Validate Single Value
*
* @param WPCF7_Validation $result the current validation result object
* @param string $value the current value being validated, sanitized
* @param WPCF7_FormTag $tag the current form tag being filtered for validation
* @param string $type Optional. The type of the current form tag. Default is blank for lookup.
*
* @return WPCF7_Validation a possibly modified validation result object
*/
function wpcf7dtx_validate_value($result, $value, $tag, $type = '')
{
$type = $type ? $type : str_replace(array('dynamic_', 'dynamic'), '', $tag->basetype);
// Validate required fields for value
if ($tag->is_required() && empty($value)) {
$result->invalidate($tag, wpcf7_get_message('invalid_required'));
return $result;
}
// Validate value by type
if (!empty($value)) {
switch ($type) {
case 'email':
if (!wpcf7_is_email($value)) {
$result->invalidate($tag, wpcf7_get_message('dtx_invalid_email'));
return $result;
}
break;
case 'tel':
if (!wpcf7_is_tel($value)) {
$result->invalidate($tag, wpcf7_get_message('dtx_invalid_tel'));
return $result;
}
break;
case 'number':
case 'range':
if (!wpcf7_is_number($value)) {
$result->invalidate($tag, wpcf7_get_message('dtx_invalid_number'));
return $result;
}
break;
case 'date':
if (!wpcf7_is_date($value)) {
$result->invalidate($tag, wpcf7_get_message('dtx_invalid_date'));
return $result;
}
break;
}
// Finish validating text-based inputs
$maxlength = $tag->get_maxlength_option();
$minlength = $tag->get_minlength_option();
if ($maxlength && $minlength && $maxlength < $minlength) {
$maxlength = $minlength = null;
}
$code_units = wpcf7_count_code_units($value);
if (false !== $code_units) {
if ($maxlength && $maxlength < $code_units) {
$result->invalidate($tag, wpcf7_get_message('invalid_too_long'));
return $result;
} elseif ($minlength && $code_units < $minlength) {
$result->invalidate($tag, wpcf7_get_message('invalid_too_short'));
return $result;
}
}
}
return $result;
}
/**
* Backend Mail Configuration Validation
*
* Validate dynamic form tags used in mail configuration.
*
* @since 4.0.0
*
* @param WPCF7_ConfigValidator
*
* @return void
*/
function wpcf7dtx_validate($validator)
{
// Check for sensitive form tags
$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
if (in_array($tag->basetype, array_merge(
array('dynamictext', 'dynamichidden'), // Deprecated DTX form tags
array_keys(wpcf7dtx_config()) // DTX form tags
))) {
// Check value for sensitive data
$default = $tag->get_option('defaultvalue', '', true);
if (!$default) {
$default = $tag->get_default_option(strval(reset($tag->values)));
}
if (
!empty($value = trim(wpcf7_get_hangover($tag->name, $default))) && // Has value
($result = wpcf7dtx_validate_sensitive_value($value))['status'] // Has sensitive data
) {
$validator->add_error('form.body', 'dtx_disallowed', array(
'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'),
esc_html($tag->basetype),
esc_html($tag->name),
esc_html($result['key']),
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'))
),
'link' => wpcf7dtx_get_admin_settings_screen_url()
));
}
// Check placeholder for sensitive data
if (
($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
($result = wpcf7dtx_validate_sensitive_value($placeholder))['status'] // Has sensitive data
) {
$validator->add_error('form.body', 'dtx_disallowed', array(
'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'),
esc_html($tag->basetype),
esc_html($tag->name),
esc_html($result['key']),
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'))
),
'link' => wpcf7dtx_get_admin_settings_screen_url()
));
}
}
}
// Validate email address
if (!$validator->is_valid()) {
$contact_form = null;
$form_tags = null;
foreach ($validator->collect_error_messages() as $component => $errors) {
$components = explode('.', $component);
if (count($components) === 2 && strpos($components[0], 'mail') === 0 && in_array($components[1], array('sender', 'recipient', 'additional_headers'))) {
foreach ($errors as $error) {
// Focus on email fields that flag the invalid mailbox syntax warning, have to test link because code isn't sent and message could be in any language
if (strpos(wpcf7dtx_array_has_key('link', $error), 'invalid-mailbox-syntax') !== false) {
if (is_null($contact_form)) {
$contact_form = $validator->contact_form();
}
if (is_null($form_tags)) {
$form_tags = wpcf7_scan_form_tags();
}
$raw_value = $contact_form->prop($components[0])[$components[1]];
foreach ($form_tags as $tag) {
if (!empty($tag->name)) {
// Check if this form tag is in the raw value
$form_tag = '[' . $tag->name . ']';
if (strpos($raw_value, $form_tag) !== false && in_array($tag->basetype, array_keys(wpcf7dtx_config()))) {
$validator->remove_error($component, 'invalid_mailbox_syntax'); // Remove error, this is ours to handle now
$utm_source = urlencode(home_url());
if (!in_array($tag->basetype, array('dynamic_hidden', 'dynamic_email'))) {
$validator->add_error($component, 'invalid_mailbox_syntax', array(
'message' => __('Only email, dynamic email, hidden, or dynamic hidden form tags can be used for email addresses.', 'contact-form-7-dynamic-text-extension'),
'link' => esc_url(sprintf('https://aurisecreative.com/docs/contact-form-7-dynamic-text-extension/configuration-errors/?utm_source=%s&utm_medium=link&utm_campaign=contact-form-7-dynamic-text-extension&utm_content=config-error-invalid_mailbox_syntax#valid-form-tags', $utm_source))
));
} else {
$dynamic_value = wpcf7dtx_get_dynamic(false, $tag); // Get the dynamic value of this tag
if (empty($dynamic_value) && $tag->basetype == 'dynamic_hidden') {
$validator->add_error($component, 'maybe_empty', array(
'message' => __('The dynamic hidden form tag must have a default value.', 'contact-form-7-dynamic-text-extension'),
'link' => esc_url(sprintf('https://aurisecreative.com/docs/contact-form-7-dynamic-text-extension/configuration-errors/?utm_source=%s&utm_medium=link&utm_campaign=contact-form-7-dynamic-text-extension&utm_content=config-error-maybe_empty#maybe-empty', $utm_source))
));
} elseif (empty($dynamic_value) && !$tag->is_required()) {
$validator->add_error($component, 'maybe_empty', array(
'message' => __('The dynamic form tag must be required or have a default value.', 'contact-form-7-dynamic-text-extension'),
'link' => esc_url(sprintf('https://aurisecreative.com/docs/contact-form-7-dynamic-text-extension/configuration-errors/?utm_source=%s&utm_medium=link&utm_campaign=contact-form-7-dynamic-text-extension&utm_content=config-error-maybe_empty#maybe-empty', $utm_source))
));
} elseif (!empty($dynamic_value)) {
if (!wpcf7_is_email($dynamic_value)) {
$validator->add_error($component, 'invalid_mailbox_syntax', array(
'message' => __('The default dynamic value does not result in a valid email address.', 'contact-form-7-dynamic-text-extension'),
'link' => esc_url(sprintf('https://aurisecreative.com/docs/contact-form-7-dynamic-text-extension/configuration-errors/?utm_source=%s&utm_medium=link&utm_campaign=contact-form-7-dynamic-text-extension&utm_content=config-error-invalid_mailbox_syntax#invalid-email-address', $utm_source))
));
} elseif ($component[1] == 'sender' && !wpcf7_is_email_in_site_domain($dynamic_value)) {
$validator->add_error($component, 'email_not_in_site_domain', array(
'message' => __('The dynamic email address for the sender does not belong to the site domain.', 'contact-form-7-dynamic-text-extension'),
'link' => esc_url(sprintf('https://aurisecreative.com/docs/contact-form-7-dynamic-text-extension/configuration-errors/?utm_source=%s&utm_medium=link&utm_campaign=contact-form-7-dynamic-text-extension&utm_content=config-error-email_not_in_site_domain#invalid-site-domain', $utm_source))
));
}
}
}
}
}
}
}
}
}
}
}
}
add_action('wpcf7_config_validator_validate', 'wpcf7dtx_validate');
/**
* Validate Field Value for Sensitive Data
*
* @since 5.0.0
*
* @see https://developer.wordpress.org/reference/functions/get_bloginfo/#description
*
* @param string $content The string to validate.
*
* @return array An associative array with keys `status` (bool), `shortcode` (string), and `key` (string).
* The value of `status` is true if the content is a shortcode that is attempting to access sensitive data. False
* otherwise. The value of `shortcode` is the the shortcode that is making the attempt if `status` is true. The
* value of `key` is the shortcode's `key` attribute of the attempt being made if `status` is true.
*/
function wpcf7dtx_validate_sensitive_value($content)
{
$r = array(
'status' => false,
'shortcode' => '',
'key' => ''
);
// Parse the attributes. [0] is the shortcode name. ['key'] is the key attribute
$atts = shortcode_parse_atts($content);
// If we can't extract the atts, or the shortcode or `key` is not an att, don't validate
if( !is_array($atts) || !array_key_exists('key', $atts) || !array_key_exists('0', $atts) ) return $r;
// Find the key and shortcode in question
$key = sanitize_text_field($atts['key']);
$shortcode = sanitize_text_field($atts['0']);
// If the shortcode or key value does not exist, don't validate
if( empty($shortcode) || empty($key) ) return $r;
$allowed = true;
switch( $shortcode ){
case 'CF7_get_custom_field':
$allowed = wpcf7dtx_post_meta_key_access_is_allowed( $key );
break;
case 'CF7_get_current_user':
$allowed = wpcf7dtx_user_data_access_is_allowed( $key );
break;
default:
}
if( !$allowed ){
$r['status'] = true;
$r['shortcode'] = $shortcode;
$r['key'] = $key;
}
return $r;
}