2499 lines
82 KiB
PHP
2499 lines
82 KiB
PHP
<?php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName -- File name follows plugin slug convention; class name cannot be changed without breaking the codebase.
|
|
// 100% match.
|
|
|
|
/**
|
|
* Field rendering and persistence layer for the Complianz T&C wizard.
|
|
*
|
|
* Defines the cmplz_tc_field singleton class, which is responsible for
|
|
* rendering every input type used in the setup wizard (text, checkbox,
|
|
* radio, select, editor, multicheckbox, and more), saving submitted
|
|
* wizard form data with per-type sanitisation, managing "multiple" rows
|
|
* (add/remove repeatable entries), evaluating field conditions and
|
|
* callback conditions, and registering translatable field values with
|
|
* Polylang and WPML.
|
|
*
|
|
* @package Complianz_Terms_Conditions
|
|
* @subpackage Field
|
|
* @author Complianz
|
|
* @copyright 2023 Complianz.io
|
|
* @license GPL-2.0-or-later
|
|
* @link https://complianz.io
|
|
*
|
|
* @since 1.0.0
|
|
*/
|
|
|
|
defined( 'ABSPATH' ) || die( 'you do not have acces to this page!' );
|
|
|
|
if ( ! class_exists( 'cmplz_tc_field' ) ) {
|
|
// phpcs:disable PEAR.NamingConventions.ValidClassName.StartWithCapital, PEAR.NamingConventions.ValidClassName.Invalid, WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedClassFound -- Established public API; class name cannot be changed without breaking all callers.
|
|
/**
|
|
* Renders wizard form fields and persists their values to WordPress options.
|
|
*
|
|
* Implemented as a singleton. Hooks into WordPress init (priority 5) to
|
|
* process form saves before the page renders, and into the plugin's own
|
|
* complianz_tc_before_label / complianz_tc_label_html / complianz_tc_after_label /
|
|
* complianz_tc_after_field action sequence that drives the field layout in the
|
|
* wizard template. Every public field-type method (text, radio, checkbox, etc.)
|
|
* follows the same pattern: check show_field(), fire the label hooks, render the
|
|
* input, fire after_field.
|
|
*
|
|
* @package Complianz_Terms_Conditions
|
|
* @subpackage Field
|
|
*
|
|
* @since 1.0.0
|
|
*/
|
|
class cmplz_tc_field {
|
|
// phpcs:enable PEAR.NamingConventions.ValidClassName.StartWithCapital, PEAR.NamingConventions.ValidClassName.Invalid, WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedClassFound
|
|
/**
|
|
* Holds the single instance of this class (singleton).
|
|
*
|
|
* @since 1.0.0
|
|
* @access private
|
|
* @var cmplz_tc_field|null
|
|
*/
|
|
private static $_this; // phpcs:ignore PSR2.Classes.PropertyDeclaration.Underscore -- Underscore prefix is part of the established singleton accessor pattern used throughout this codebase.
|
|
|
|
/**
|
|
* Current wizard step position counter used during field rendering.
|
|
*
|
|
* @since 1.0.0
|
|
* @access public
|
|
* @var int|null
|
|
*/
|
|
public $position;
|
|
|
|
/**
|
|
* Cached field definitions array returned by cmplz_tc_config::fields().
|
|
*
|
|
* @since 1.0.0
|
|
* @access public
|
|
* @var array|null
|
|
*/
|
|
public $fields;
|
|
|
|
/**
|
|
* Default argument map merged with every field's args via wp_parse_args().
|
|
*
|
|
* Populated by load(). Keys mirror the field configuration schema:
|
|
* fieldname, type, required, default, label, table, callback_condition,
|
|
* condition, callback, placeholder, optional, disabled, hidden, region,
|
|
* media, first, warn, cols, minimum.
|
|
*
|
|
* @since 1.0.0
|
|
* @access public
|
|
* @var array
|
|
*/
|
|
public $default_args;
|
|
|
|
/**
|
|
* Collects fieldnames of required fields that were empty on save.
|
|
*
|
|
* Populated during save_field() and read by show_errors() to render
|
|
* inline validation messages beneath the corresponding field label.
|
|
*
|
|
* @since 1.0.0
|
|
* @access public
|
|
* @var string[] List of fieldname strings that failed required validation.
|
|
*/
|
|
public $form_errors = array();
|
|
|
|
/**
|
|
* Initialises the field system, registers hooks, and loads default args.
|
|
*
|
|
* Guards against a second instantiation (singleton). Registers process_save()
|
|
* on init at priority 5 so the form is saved before the wizard template
|
|
* renders. Hooks label-rendering callbacks to the complianz_tc_* action
|
|
* sequence used by every field-type method. Calls load() to populate the
|
|
* default_args map.
|
|
*
|
|
* @since 1.0.0
|
|
* @access public
|
|
*/
|
|
public function __construct() {
|
|
if ( isset( self::$_this ) ) {
|
|
wp_die(
|
|
esc_html(
|
|
sprintf(
|
|
'%s is a singleton class and you cannot create a second instance.',
|
|
get_class( $this )
|
|
)
|
|
)
|
|
);
|
|
}
|
|
|
|
self::$_this = $this;
|
|
add_action( 'init', array( $this, 'process_save' ), 5 );
|
|
add_action( 'cmplz_tc_register_translation', array( $this, 'register_translation' ), 10, 2 );
|
|
|
|
add_action( 'complianz_tc_before_label', array( $this, 'before_label' ), 10, 1 );
|
|
add_action( 'complianz_tc_before_label', array( $this, 'show_errors' ), 10, 1 );
|
|
add_action( 'complianz_tc_label_html', array( $this, 'label_html' ), 10, 1 );
|
|
add_action( 'complianz_tc_after_label', array( $this, 'after_label' ), 10, 1 );
|
|
add_action( 'complianz_tc_after_field', array( $this, 'after_field' ), 10, 1 );
|
|
|
|
$this->load();
|
|
}
|
|
|
|
/**
|
|
* Returns the single instance of this class.
|
|
*
|
|
* @since 1.0.0
|
|
* @access public
|
|
*
|
|
* @return cmplz_tc_field The singleton instance.
|
|
*/
|
|
public static function this() {
|
|
return self::$_this;
|
|
}
|
|
|
|
/**
|
|
* Renders the `<label>` element for a wizard field.
|
|
*
|
|
* Outputs a label with an optional `cmplz-disabled` class when the field
|
|
* is disabled, a title wrapper div, and an optional help/tooltip icon
|
|
* rendered via cmplz_tc_icon(). Hooked to: complianz_tc_label_html.
|
|
*
|
|
* @since 1.0.0
|
|
* @access public
|
|
*
|
|
* @param array $args Field arguments merged with default_args. Relevant keys:
|
|
* - 'disabled' (bool) Whether the field is non-editable.
|
|
* - 'fieldname' (string) The input's id/for attribute value.
|
|
* - 'label' (string) Human-readable label text (pre-escaped).
|
|
* - 'tooltip' (string) Optional tooltip text for the help icon.
|
|
* @return void
|
|
*/
|
|
public function label_html( $args ) {
|
|
?>
|
|
<label class="<?php echo $args['disabled'] ? 'cmplz-disabled' : ''; ?>" for="cmplz_<?php echo esc_attr( $args['fieldname'] ); ?>">
|
|
<div class="cmplz-title-wrap"><?php echo esc_html( $args['label'] ); ?></div>
|
|
<div>
|
|
<?php
|
|
if ( isset( $args['tooltip'] ) ) {
|
|
echo cmplz_tc_icon( 'help', 'default', $args['tooltip'] ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- HTML from trusted internal helper.
|
|
}
|
|
?>
|
|
</div>
|
|
</label>
|
|
<?php
|
|
}
|
|
|
|
|
|
/**
|
|
* Registers a field value string with active multilingual plugins.
|
|
*
|
|
* Registers the supplied string with Polylang (pll_register_string()),
|
|
* WPML (icl_register_string()), and the wpml_register_single_string
|
|
* action so it appears in each plugin's string-translation interface.
|
|
* Called via the cmplz_tc_register_translation action fired from save_field()
|
|
* and save_multiple().
|
|
*
|
|
* Hooked to: cmplz_tc_register_translation.
|
|
*
|
|
* @since 1.0.0
|
|
* @access public
|
|
*
|
|
* @param string $fieldname The unique field identifier used as the string name/key.
|
|
* @param string $translation_string The translatable field value to register.
|
|
* @return void
|
|
*/
|
|
public function register_translation( $fieldname, $translation_string ) {
|
|
// Polylang integration.
|
|
if ( function_exists( 'pll_register_string' ) ) {
|
|
pll_register_string( $fieldname, $translation_string, 'complianz' );
|
|
}
|
|
|
|
// WPML integration.
|
|
if ( function_exists( 'icl_register_string' ) ) {
|
|
icl_register_string( 'complianz', $fieldname, $translation_string );
|
|
}
|
|
|
|
do_action(
|
|
'wpml_register_single_string',
|
|
'complianz',
|
|
$fieldname,
|
|
$translation_string
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Populates the default_args map used by all field-type methods.
|
|
*
|
|
* Called once during __construct(). Every field's configuration array is
|
|
* merged with these defaults via wp_parse_args() before rendering, so any
|
|
* key omitted by the field definition falls back to a safe default value.
|
|
*
|
|
* @since 1.0.0
|
|
* @access public
|
|
*
|
|
* @return void
|
|
*/
|
|
public function load() {
|
|
$this->default_args = array(
|
|
'fieldname' => '',
|
|
'type' => 'text',
|
|
'required' => false,
|
|
'default' => '',
|
|
'label' => '',
|
|
'table' => false,
|
|
'callback_condition' => false,
|
|
'condition' => false,
|
|
'callback' => false,
|
|
'placeholder' => '',
|
|
'optional' => false,
|
|
'disabled' => false,
|
|
'hidden' => false,
|
|
'region' => false,
|
|
'media' => true,
|
|
'first' => false,
|
|
'warn' => false,
|
|
'cols' => false,
|
|
'minimum' => 0,
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Processes and persists wizard form submissions on init (priority 5).
|
|
*
|
|
* Validates the manage_options capability and verifies the complianz_tc_nonce
|
|
* nonce before touching any data. Handles three distinct POST sub-actions:
|
|
* removing a repeatable-field row (cmplz_tc_remove_multiple), adding a new
|
|
* empty row (cmplz_tc_add_multiple), and bulk-saving repeatable rows
|
|
* (cmplz_tc_multiple). Also processes custom document page/URL overrides
|
|
* for each document type and saves all remaining cmplz_-prefixed fields
|
|
* via save_field(). Fires cmplz_after_saved_all_fields when complete.
|
|
*
|
|
* Hooked to: init (priority 5).
|
|
*
|
|
* @since 1.0.0
|
|
* @access public
|
|
*
|
|
* @return void
|
|
*/
|
|
public function process_save() {
|
|
if ( ! current_user_can( 'manage_options' ) ) {
|
|
return;
|
|
}
|
|
if ( isset( $_POST['complianz_tc_nonce'] ) ) {
|
|
// Verify nonce before processing any posted data.
|
|
if ( ! wp_verify_nonce(
|
|
sanitize_text_field( wp_unslash( $_POST['complianz_tc_nonce'] ) ),
|
|
'complianz_tc_save'
|
|
) ) {
|
|
return;
|
|
}
|
|
|
|
$fields = COMPLIANZ_TC::$config->fields();
|
|
|
|
// Remove multiple field.
|
|
if ( isset( $_POST['cmplz_tc_remove_multiple'] ) ) {
|
|
$fieldnames = array_map(
|
|
function ( $el ) {
|
|
return sanitize_title( $el );
|
|
},
|
|
wp_unslash( $_POST['cmplz_tc_remove_multiple'] ) // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Values sanitized via sanitize_title() in array_map callback.
|
|
);
|
|
|
|
foreach ( $fieldnames as $fieldname => $key ) {
|
|
|
|
$page = $fields[ $fieldname ]['source'];
|
|
$options = get_option( 'complianz_tc_options_' . $page );
|
|
|
|
$multiple_field = $this->get_value(
|
|
$fieldname,
|
|
array()
|
|
);
|
|
|
|
unset( $multiple_field[ $key ] );
|
|
|
|
$options[ $fieldname ] = $multiple_field;
|
|
if ( ! empty( $options ) ) {
|
|
update_option(
|
|
'complianz_tc_options_' . $page,
|
|
$options
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Add multiple field.
|
|
if ( isset( $_POST['cmplz_tc_add_multiple'] ) ) {
|
|
$fieldname
|
|
= $this->sanitize_fieldname( sanitize_text_field( wp_unslash( $_POST['cmplz_tc_add_multiple'] ) ) );
|
|
$this->add_multiple_field( $fieldname );
|
|
}
|
|
|
|
// Save multiple field.
|
|
if ( ( isset( $_POST['cmplz-save'] )
|
|
|| isset( $_POST['cmplz-next'] ) )
|
|
&& isset( $_POST['cmplz_tc_multiple'] )
|
|
) {
|
|
$fieldnames
|
|
= $this->sanitize_array( wp_unslash( $_POST['cmplz_tc_multiple'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Array sanitized recursively inside sanitize_array(). // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Array sanitized recursively inside sanitize_array().
|
|
$this->save_multiple( $fieldnames );
|
|
}
|
|
|
|
// Save the custom URL's for not Complianz generated pages.
|
|
$docs = COMPLIANZ_TC::$document->get_document_types();
|
|
foreach ( $docs as $document ) {
|
|
if ( isset( $_POST[ 'cmplz_' . $document . '_custom_page' ] ) ) {
|
|
$doc_id = intval( $_POST[ 'cmplz_' . $document . '_custom_page' ] );
|
|
update_option( 'cmplz_' . $document . '_custom_page', $doc_id );
|
|
// If we have an actual privacy statement (custom), set it as the privacy URL for WP.
|
|
if ( 'privacy-statement' === $document && $doc_id > 0 ) {
|
|
COMPLIANZ_TC::$document->set_wp_privacy_policy( $doc_id, 'privacy-statement' );
|
|
}
|
|
}
|
|
if ( isset( $_POST[ 'cmplz_' . $document . '_custom_page_url' ] ) ) {
|
|
$url = esc_url_raw( wp_unslash( $_POST[ 'cmplz_' . $document . '_custom_page_url' ] ) );
|
|
update_option( 'cmplz_' . $document . '_custom_page_url', $url );
|
|
}
|
|
}
|
|
|
|
// Save data.
|
|
$posted_fields = array_filter( wp_unslash( $_POST ), array( $this, 'filter_complianz_tc_fields' ), ARRAY_FILTER_USE_KEY ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Values are sanitized per field type inside save_field() → sanitize().
|
|
foreach ( $posted_fields as $fieldname => $fieldvalue ) {
|
|
$this->save_field( $fieldname, $fieldvalue );
|
|
}
|
|
do_action( 'cmplz_after_saved_all_fields', $posted_fields );
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* Recursively sanitises every scalar value in a nested array.
|
|
*
|
|
* Walks the supplied array depth-first. Scalar leaves are passed through
|
|
* sanitize_text_field(); nested arrays are recursed into. Used to sanitise
|
|
* the cmplz_tc_multiple POST payload before it is handed to save_multiple().
|
|
*
|
|
* @since 1.0.0
|
|
* @access public
|
|
*
|
|
* @param array $items The input array to sanitise (passed by reference internally).
|
|
* @return array The sanitised array with all scalar values cleaned.
|
|
*/
|
|
public function sanitize_array( $items ) {
|
|
foreach ( $items as &$value ) {
|
|
if ( ! is_array( $value ) ) {
|
|
$value = sanitize_text_field( $value );
|
|
} else {
|
|
$this->sanitize_array( $value ); // phpcs:ignore -- Recursive call; $value is passed by reference.
|
|
}
|
|
}
|
|
|
|
return $items;
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* Checks whether a field has a conditional display rule.
|
|
*
|
|
* Returns true when the field's config has a non-empty 'condition' key,
|
|
* which means the field's visibility depends on the current value of
|
|
* another field. Used by save_field() to skip required-field validation
|
|
* for fields that are not currently visible.
|
|
*
|
|
* @since 1.0.0
|
|
* @access public
|
|
*
|
|
* @param string $fieldname The field identifier to look up in the config.
|
|
* @return bool True when the field has a condition, false otherwise.
|
|
*/
|
|
public function is_conditional( $fieldname ) {
|
|
$fields = COMPLIANZ_TC::$config->fields();
|
|
if ( isset( $fields[ $fieldname ]['condition'] )
|
|
&& $fields[ $fieldname ]['condition']
|
|
) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Checks whether a field is a repeatable "multiple" type.
|
|
*
|
|
* Returns true for fields of type 'thirdparties' or 'processors', which
|
|
* store an array of repeatable sub-entries (rows) rather than a single
|
|
* scalar value. Used to determine the correct save and render strategy.
|
|
*
|
|
* @since 1.0.0
|
|
* @access public
|
|
*
|
|
* @param string $fieldname The field identifier to look up in the config.
|
|
* @return bool True for thirdparties/processors types, false otherwise.
|
|
*/
|
|
public function is_multiple_field( $fieldname ) {
|
|
$fields = COMPLIANZ_TC::$config->fields();
|
|
if ( isset( $fields[ $fieldname ]['type'] )
|
|
&& ( 'thirdparties' === $fields[ $fieldname ]['type'] )
|
|
) {
|
|
return true;
|
|
}
|
|
if ( isset( $fields[ $fieldname ]['type'] )
|
|
&& ( 'processors' === $fields[ $fieldname ]['type'] )
|
|
) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
/**
|
|
* Saves repeatable-field row data from the wizard form.
|
|
*
|
|
* Iterates over the submitted fieldname/row map, sanitises every value,
|
|
* marks each entry as saved_by_user so it is not overwritten by automatic
|
|
* imports, merges the rows into the existing stored array, and persists
|
|
* the result. For translatable types (cookies, thirdparties, processors,
|
|
* editor) each sub-value is registered with multilingual plugins via the
|
|
* cmplz_register_translation action.
|
|
*
|
|
* @since 1.0.0
|
|
* @access public
|
|
*
|
|
* @param array $fieldnames Map of fieldname => array of row data submitted
|
|
* from cmplz_tc_multiple POST entries.
|
|
* @return void
|
|
*/
|
|
public function save_multiple( $fieldnames ) {
|
|
if ( ! current_user_can( 'manage_options' ) ) {
|
|
return;
|
|
}
|
|
|
|
$fields = COMPLIANZ_TC::$config->fields();
|
|
foreach ( $fieldnames as $fieldname => $saved_fields ) {
|
|
|
|
if ( ! isset( $fields[ $fieldname ] ) ) {
|
|
return;
|
|
}
|
|
|
|
$page = $fields[ $fieldname ]['source'];
|
|
$type = $fields[ $fieldname ]['type'];
|
|
$options = get_option( 'complianz_tc_options_' . $page );
|
|
$multiple_field = $this->get_value( $fieldname, array() );
|
|
|
|
foreach ( $saved_fields as $key => $value ) {
|
|
$value = is_array( $value )
|
|
? array_map( 'sanitize_text_field', $value )
|
|
: sanitize_text_field( $value );
|
|
// store the fact that this value was saved from the back-end, so should not get overwritten.
|
|
$value['saved_by_user'] = true;
|
|
$multiple_field[ $key ] = $value;
|
|
|
|
// Make cookies and thirdparties translatable.
|
|
if ( 'cookies' === $type || 'thirdparties' === $type
|
|
|| 'processors' === $type
|
|
|| 'editor' === $type
|
|
) {
|
|
if ( isset( $fields[ $fieldname ]['translatable'] )
|
|
&& $fields[ $fieldname ]['translatable']
|
|
) {
|
|
foreach ( $value as $value_key => $field_value ) {
|
|
do_action(
|
|
'cmplz_register_translation',
|
|
$key . '_' . $fieldname . '_' . $value_key,
|
|
$field_value
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
$options[ $fieldname ] = $multiple_field;
|
|
if ( ! empty( $options ) ) {
|
|
update_option( 'complianz_tc_options_' . $page, $options );
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Sanitises and persists a single wizard field value to the options table.
|
|
*
|
|
* Checks the manage_options capability, strips the cmplz_ prefix from the
|
|
* fieldname, applies the cmplz_fieldvalue filter, sanitises the value
|
|
* according to the field type (via sanitize()), records validation errors
|
|
* for empty required non-conditional fields, registers translatable values,
|
|
* and updates the complianz_tc_options_{source} option. Fires
|
|
* complianz_tc_before_save_{source}_option and
|
|
* complianz_tc_after_save_{source}_option hooks around the update.
|
|
*
|
|
* @since 1.0.0
|
|
* @access public
|
|
*
|
|
* @param string $fieldname The POST key (prefixed with cmplz_) for the field.
|
|
* @param mixed $fieldvalue The raw submitted value; will be sanitised by this method.
|
|
* @return void
|
|
*/
|
|
public function save_field( $fieldname, $fieldvalue ) {
|
|
if ( ! current_user_can( 'manage_options' ) ) {
|
|
return;
|
|
}
|
|
|
|
$fieldvalue = apply_filters( 'cmplz_fieldvalue', $fieldvalue, $fieldname );
|
|
$fields = COMPLIANZ_TC::$config->fields();
|
|
$fieldname = str_replace( 'cmplz_', '', $fieldname );
|
|
|
|
// Do not save callback fields.
|
|
if ( isset( $fields[ $fieldname ]['callback'] ) ) {
|
|
return;
|
|
}
|
|
|
|
$type = $fields[ $fieldname ]['type'];
|
|
$page = $fields[ $fieldname ]['source'];
|
|
$required = isset( $fields[ $fieldname ]['required'] ) ? $fields[ $fieldname ]['required'] : false;
|
|
$fieldvalue = $this->sanitize( $fieldvalue, $type );
|
|
|
|
if ( ! $this->is_conditional( $fieldname ) && $required
|
|
&& empty( $fieldvalue )
|
|
) {
|
|
$this->form_errors[] = $fieldname;
|
|
}
|
|
|
|
// Make translatable.
|
|
if ( 'text' === $type || 'textarea' === $type || 'editor' === $type || 'url' === $type ) {
|
|
if ( isset( $fields[ $fieldname ]['translatable'] )
|
|
&& $fields[ $fieldname ]['translatable']
|
|
) {
|
|
do_action( 'cmplz_tc_register_translation', $fieldname, $fieldvalue );
|
|
}
|
|
}
|
|
|
|
$options = get_option( 'complianz_tc_options_' . $page );
|
|
if ( ! is_array( $options ) ) {
|
|
$options = array();
|
|
}
|
|
$prev_value = isset( $options[ $fieldname ] ) ? $options[ $fieldname ] : false;
|
|
do_action( 'complianz_tc_before_save_' . $page . '_option', $fieldname, $fieldvalue, $prev_value, $type );
|
|
$options[ $fieldname ] = $fieldvalue;
|
|
|
|
update_option( 'complianz_tc_options_' . $page, $options );
|
|
|
|
do_action( 'complianz_tc_after_save_' . $page . '_option', $fieldname, $fieldvalue, $prev_value, $type );
|
|
}
|
|
|
|
|
|
/**
|
|
* Adds a new empty row to a repeatable field's stored array.
|
|
*
|
|
* Appends a blank entry to the existing multiple-field value and persists
|
|
* it. For used_cookies fields a timestamped key is generated automatically
|
|
* when $cookie_type is not provided. Prevents duplicating an existing key,
|
|
* re-adding a previously deleted cookie, and adding built-in WordPress
|
|
* cookies (whose names start with wordpress_).
|
|
*
|
|
* @since 1.0.0
|
|
* @access public
|
|
*
|
|
* @param string $fieldname The multiple-field identifier.
|
|
* @param string|false $cookie_type Optional cookie type key. When false and the
|
|
* field is 'used_cookies', a custom_{timestamp}
|
|
* key is generated. Default false.
|
|
* @return void
|
|
*/
|
|
public function add_multiple_field( $fieldname, $cookie_type = false ) {
|
|
if ( ! current_user_can( 'manage_options' ) ) {
|
|
return;
|
|
}
|
|
|
|
$fields = COMPLIANZ_TC::$config->fields();
|
|
|
|
$page = $fields[ $fieldname ]['source'];
|
|
$options = get_option( 'complianz_tc_options_' . $page );
|
|
|
|
$multiple_field = $this->get_value( $fieldname, array() );
|
|
if ( 'used_cookies' === $fieldname && ! $cookie_type ) {
|
|
$cookie_type = 'custom_' . time();
|
|
}
|
|
if ( ! is_array( $multiple_field ) ) {
|
|
$multiple_field = array( $multiple_field );
|
|
}
|
|
|
|
if ( $cookie_type ) {
|
|
// Prevent key from being added twice.
|
|
foreach ( $multiple_field as $index => $cookie ) {
|
|
if ( $cookie['key'] === $cookie_type ) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Don't add field if it was deleted previously.
|
|
$deleted_cookies = get_option( 'cmplz_deleted_cookies' );
|
|
if ( ( $deleted_cookies
|
|
&& in_array( $cookie_type, $deleted_cookies, true ) )
|
|
) {
|
|
return;
|
|
}
|
|
|
|
// Don't add default WordPress cookies.
|
|
if ( strpos( $cookie_type, 'wordpress_' ) !== false ) {
|
|
return;
|
|
}
|
|
|
|
$multiple_field[] = array( 'key' => $cookie_type );
|
|
} else {
|
|
$multiple_field[] = array();
|
|
}
|
|
|
|
$options[ $fieldname ] = $multiple_field;
|
|
|
|
if ( ! empty( $options ) ) {
|
|
update_option( 'complianz_tc_options_' . $page, $options );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Sanitises a field value according to its declared type.
|
|
*
|
|
* Maps each supported field type to the appropriate WordPress sanitisation
|
|
* function: colorpicker → sanitize_hex_color(), text/phone →
|
|
* sanitize_text_field(), multicheckbox → array_map(sanitize_text_field),
|
|
* email → sanitize_email(), url → esc_url_raw(), number → intval(),
|
|
* textarea/editor → wp_kses_post(). css and javascript types are stored
|
|
* as-is (capability check is the gate). Returns false when the current
|
|
* user lacks manage_options.
|
|
*
|
|
* @since 1.0.0
|
|
* @access public
|
|
*
|
|
* @param mixed $value The raw value to sanitise.
|
|
* @param string $type The field type string (e.g. 'text', 'checkbox', 'url').
|
|
* @return array|bool|int|string|false The sanitised value, or false on capability failure.
|
|
*/
|
|
public function sanitize( $value, $type ) {
|
|
if ( ! current_user_can( 'manage_options' ) ) {
|
|
return false;
|
|
}
|
|
switch ( $type ) {
|
|
case 'colorpicker':
|
|
return sanitize_hex_color( $value );
|
|
case 'text':
|
|
return sanitize_text_field( $value );
|
|
case 'multicheckbox':
|
|
if ( ! is_array( $value ) ) {
|
|
$value = array( $value );
|
|
}
|
|
|
|
return array_map( 'sanitize_text_field', $value );
|
|
case 'phone':
|
|
$value = sanitize_text_field( $value );
|
|
|
|
return $value;
|
|
case 'email':
|
|
return sanitize_email( $value );
|
|
case 'url':
|
|
return esc_url_raw( $value );
|
|
case 'number':
|
|
return intval( $value );
|
|
case 'css':
|
|
case 'javascript':
|
|
return $value;
|
|
case 'editor':
|
|
case 'textarea':
|
|
return wp_kses_post( $value );
|
|
}
|
|
|
|
return sanitize_text_field( $value );
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* Filters the POST array to retain only known Complianz field keys.
|
|
*
|
|
* Used as an array_filter() callback with ARRAY_FILTER_USE_KEY to extract
|
|
* only the keys from $_POST that are prefixed with cmplz_ and correspond
|
|
* to a registered field in the config. Prevents arbitrary POST data from
|
|
* reaching save_field().
|
|
*
|
|
* @since 1.0.0
|
|
* @access private
|
|
*
|
|
* @param string $fieldname The POST key to test.
|
|
* @return bool True when the key is a known cmplz_ field, false otherwise.
|
|
*/
|
|
private function filter_complianz_tc_fields(
|
|
$fieldname
|
|
) {
|
|
if ( strpos( $fieldname, 'cmplz_' ) !== false
|
|
&& isset(
|
|
COMPLIANZ_TC::$config->fields[ str_replace(
|
|
'cmplz_',
|
|
'',
|
|
$fieldname
|
|
) ]
|
|
)
|
|
) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Opens the field wrapper div and prepends conditional-visibility attributes.
|
|
*
|
|
* Builds CSS class strings and HTML data attributes for multi-condition
|
|
* support (condition-check-N, data-condition-question-N, data-condition-answer-N).
|
|
* Evaluates the field's current condition via condition_applies() to add the
|
|
* cmplz-hidden class for fields whose condition is not currently met. Also
|
|
* handles hidden, first, type, cols/col/colspan classes, and calls
|
|
* get_master_label() for section headings.
|
|
*
|
|
* Hooked to: complianz_tc_before_label.
|
|
*
|
|
* @since 1.0.0
|
|
* @access public
|
|
*
|
|
* @param array $args Merged field arguments. Relevant keys: condition, hidden,
|
|
* first, type, cols, col, colspan, fieldname, master_label.
|
|
* @return void
|
|
*/
|
|
public function before_label( $args ) {
|
|
$condition_class = '';
|
|
$condition_question = '';
|
|
$condition_answer = '';
|
|
|
|
if ( ! empty( $args['condition'] ) ) {
|
|
$condition_count = 1;
|
|
foreach ( $args['condition'] as $question => $answer ) {
|
|
$question = esc_attr( $question );
|
|
$answer = esc_attr( $answer );
|
|
$condition_class .= "condition-check-{$condition_count} ";
|
|
$condition_question .= "data-condition-answer-{$condition_count}='{$answer}' ";
|
|
$condition_answer .= "data-condition-question-{$condition_count}='{$question}' ";
|
|
++$condition_count;
|
|
}
|
|
}
|
|
|
|
$hidden_class = ( $args['hidden'] ) ? 'hidden' : '';
|
|
$cmplz_hidden = $this->condition_applies( $args ) ? '' : 'cmplz-hidden';
|
|
$first_class = ( $args['first'] ) ? 'first' : '';
|
|
$type = 'notice' === $args['type'] ? '' : $args['type'];
|
|
|
|
$cols_class = isset( $args['cols'] ) && $args['cols'] ? "cmplz-cols-{$args['cols']}" : '';
|
|
$col_class = isset( $args['col'] ) ? "cmplz-col-{$args['col']}" : '';
|
|
$colspan_class = isset( $args['colspan'] ) ? "cmplz-colspan-{$args['colspan']}" : '';
|
|
|
|
$this->get_master_label( $args );
|
|
|
|
echo '<div class="field-group ' .
|
|
esc_attr(
|
|
$args['fieldname'] . ' ' .
|
|
esc_attr( $cols_class ) . ' ' .
|
|
esc_attr( $col_class ) . ' ' .
|
|
esc_attr( $colspan_class ) . ' ' .
|
|
'cmplz-' . $type . ' ' .
|
|
$hidden_class . ' ' .
|
|
$first_class . ' ' .
|
|
$condition_class . ' ' .
|
|
$cmplz_hidden
|
|
)
|
|
. '" ';
|
|
|
|
echo $condition_question; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Built from esc_attr()-escaped values above.
|
|
echo $condition_answer; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Built from esc_attr()-escaped values above.
|
|
|
|
echo '><div class="cmplz-field"><div class="cmplz-label">';
|
|
}
|
|
|
|
/**
|
|
* Renders an optional section-level heading above a field group.
|
|
*
|
|
* Outputs an `<h2>` heading wrapped in `.cmplz-master-label` when the
|
|
* field args include a 'master_label' key. Used to visually separate
|
|
* groups of fields within the same wizard step.
|
|
*
|
|
* @since 1.0.0
|
|
* @access public
|
|
*
|
|
* @param array $args Field arguments. Only 'master_label' (string) is used.
|
|
* @return void
|
|
*/
|
|
public function get_master_label( $args ) {
|
|
if ( ! isset( $args['master_label'] ) ) {
|
|
return;
|
|
}
|
|
?>
|
|
<div class="cmplz-master-label"><h2><?php echo esc_html( $args['master_label'] ); ?></h2></div>
|
|
<?php
|
|
}
|
|
|
|
/**
|
|
* Renders an inline validation error message for a required field.
|
|
*
|
|
* Checks whether the field's name is present in the form_errors list
|
|
* (populated by save_field() for empty required fields) and outputs a
|
|
* styled error div when it is. Hooked to: complianz_tc_before_label.
|
|
*
|
|
* @since 1.0.0
|
|
* @access public
|
|
*
|
|
* @param array $args Field arguments. Uses 'fieldname' (string) to match
|
|
* against the form_errors array.
|
|
* @return void
|
|
*/
|
|
public function show_errors(
|
|
$args
|
|
) {
|
|
if ( in_array( $args['fieldname'], $this->form_errors, true ) ) {
|
|
?>
|
|
<div class="cmplz-form-errors">
|
|
<?php
|
|
esc_html_e(
|
|
'This field is required. Please complete the question before continuing',
|
|
'complianz-terms-conditions'
|
|
)
|
|
?>
|
|
</div>
|
|
<?php
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Renders the tooltip icon inside a field label when a tooltip is set.
|
|
*
|
|
* Outputs the help icon SVG via cmplz_tc_icon() when 'tooltip' is present
|
|
* in the field args. This is an alternative entry point to the tooltip
|
|
* rendered by label_html(); it can be used when callers need the icon
|
|
* in a different DOM position.
|
|
*
|
|
* @since 1.0.0
|
|
* @access public
|
|
*
|
|
* @param array $args Field arguments. Uses 'tooltip' (string) when present.
|
|
* @return void
|
|
*/
|
|
public function in_label( $args ) {
|
|
if ( isset( $args['tooltip'] ) ) {
|
|
echo cmplz_tc_icon( 'help', 'default', $args['tooltip'] ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- HTML from trusted internal helper.
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Closes the `.cmplz-label` wrapper div opened by before_label().
|
|
*
|
|
* Hooked to: complianz_tc_after_label.
|
|
*
|
|
* @since 1.0.0
|
|
* @access public
|
|
*
|
|
* @param array $args Field arguments (unused; present for hook compatibility).
|
|
* @return void
|
|
*/
|
|
public function after_label( $args ) { // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.Found -- Required by complianz_tc_after_label hook signature.
|
|
echo '</div>';
|
|
}
|
|
|
|
/**
|
|
* Closes the field wrapper and renders the optional help sidebar notice.
|
|
*
|
|
* Closes the `.cmplz-field` inner div, outputs the help text (via
|
|
* cmplz_tc_sidebar_notice() with kses-sanitised content) inside a
|
|
* `.cmplz-help-warning-wrap` div, fires the cmplz_tc_notice_{fieldname}
|
|
* action for field-specific notice injection, then closes the outer
|
|
* `.field-group` wrapper opened by before_label().
|
|
*
|
|
* Hooked to: complianz_tc_after_field.
|
|
*
|
|
* @since 1.0.0
|
|
* @access public
|
|
*
|
|
* @param array $args Field arguments. Uses 'help' (string) and 'fieldname' (string).
|
|
* @return void
|
|
*/
|
|
public function after_field( $args ) {
|
|
|
|
$this->get_comment( $args );
|
|
echo '</div><!--close in after field-->';
|
|
echo '<div class="cmplz-help-warning-wrap">';
|
|
if ( isset( $args['help'] ) ) {
|
|
cmplz_tc_sidebar_notice( wp_kses_post( $args['help'] ) );
|
|
}
|
|
|
|
do_action( 'cmplz_tc_notice_' . $args['fieldname'], $args );
|
|
|
|
echo '</div>';
|
|
echo '</div>';
|
|
}
|
|
|
|
|
|
/**
|
|
* Renders a single-line text input field.
|
|
*
|
|
* Fires the standard label hooks, then outputs an `<input type="text">`
|
|
* with the current stored value, the cmplz_-prefixed field name, optional
|
|
* required attribute, and check/times validation icons.
|
|
*
|
|
* @since 1.0.0
|
|
* @access public
|
|
*
|
|
* @param array $args Merged field arguments (fieldname, default, required, placeholder).
|
|
* @return void
|
|
*/
|
|
public function text( $args ) {
|
|
if ( ! $this->show_field( $args ) ) {
|
|
return;
|
|
}
|
|
|
|
$fieldname = 'cmplz_' . $args['fieldname'];
|
|
$value = $this->get_value( $args['fieldname'], $args['default'] );
|
|
$required = $args['required'] ? 'required' : '';
|
|
$is_required = $args['required'] ? 'is-required' : '';
|
|
$check_icon = cmplz_tc_icon( 'check', 'success' );
|
|
$times_icon = cmplz_tc_icon( 'times', 'error' );
|
|
?>
|
|
|
|
<?php do_action( 'complianz_tc_before_label', $args ); ?>
|
|
<?php do_action( 'complianz_tc_label_html', $args ); ?>
|
|
<?php do_action( 'complianz_tc_after_label', $args ); ?>
|
|
|
|
<input <?php echo $required; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Hardcoded attribute string. ?>
|
|
class="validation <?php echo $is_required; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Hardcoded CSS class. ?>"
|
|
placeholder="<?php echo esc_html( $args['placeholder'] ); ?>"
|
|
type="text"
|
|
value="<?php echo esc_html( $value ); ?>"
|
|
name="<?php echo esc_html( $fieldname ); ?>"
|
|
id="<?php echo esc_html( $fieldname ); ?>"
|
|
>
|
|
<?php echo $check_icon; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- HTML from trusted internal helper. ?>
|
|
<?php echo $times_icon; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- HTML from trusted internal helper. ?>
|
|
|
|
<?php do_action( 'complianz_tc_after_field', $args ); ?>
|
|
|
|
<?php
|
|
}
|
|
|
|
/**
|
|
* Renders a URL input field with a browser-side URL pattern constraint.
|
|
*
|
|
* Identical to text() but adds a pattern attribute that accepts URLs
|
|
* (with or without http(s)://) for client-side validation. The value is
|
|
* stored via esc_url_raw() on save.
|
|
*
|
|
* @since 1.0.0
|
|
* @access public
|
|
*
|
|
* @param array $args Merged field arguments (fieldname, default, required, placeholder).
|
|
* @return void
|
|
*/
|
|
public function url( $args ) {
|
|
if ( ! $this->show_field( $args ) ) {
|
|
return;
|
|
}
|
|
|
|
$fieldname = 'cmplz_' . $args['fieldname'];
|
|
$value = $this->get_value( $args['fieldname'], $args['default'] );
|
|
$required = $args['required'] ? 'required' : '';
|
|
$is_required = $args['required'] ? 'is-required' : '';
|
|
$check_icon = cmplz_tc_icon( 'check', 'success' );
|
|
$times_icon = cmplz_tc_icon( 'times', 'error' );
|
|
|
|
?>
|
|
|
|
<?php do_action( 'complianz_tc_before_label', $args ); ?>
|
|
<?php do_action( 'complianz_tc_label_html', $args ); ?>
|
|
<?php do_action( 'complianz_tc_after_label', $args ); ?>
|
|
|
|
<input <?php echo $required; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Hardcoded attribute string. ?>
|
|
class="validation <?php echo $is_required; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Hardcoded CSS class. ?>"
|
|
placeholder="<?php echo esc_html( $args['placeholder'] ); ?>"
|
|
type="text"
|
|
pattern="(http(s)?(:\/\/))?(www\.)?[\#a-zA-Z0-9\-_\.\/\:].*"
|
|
value="<?php echo esc_html( $value ); ?>"
|
|
name="<?php echo esc_html( $fieldname ); ?>"
|
|
id="<?php echo esc_html( $fieldname ); ?>"
|
|
>
|
|
<?php echo $check_icon; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- HTML from trusted internal helper. ?>
|
|
<?php echo $times_icon; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- HTML from trusted internal helper. ?>
|
|
|
|
<?php do_action( 'complianz_tc_after_field', $args ); ?>
|
|
|
|
<?php
|
|
}
|
|
|
|
/**
|
|
* Renders an email address input field.
|
|
*
|
|
* Outputs `<input type="email">` with the current value. The submitted
|
|
* value is sanitised with sanitize_email() during save.
|
|
*
|
|
* @since 1.0.0
|
|
* @access public
|
|
*
|
|
* @param array $args Merged field arguments (fieldname, default, required, placeholder).
|
|
* @return void
|
|
*/
|
|
public function email( $args ) {
|
|
if ( ! $this->show_field( $args ) ) {
|
|
return;
|
|
}
|
|
|
|
$fieldname = 'cmplz_' . $args['fieldname'];
|
|
$value = $this->get_value( $args['fieldname'], $args['default'] );
|
|
$required = $args['required'] ? 'required' : '';
|
|
$is_required = $args['required'] ? 'is-required' : '';
|
|
$check_icon = cmplz_tc_icon( 'check', 'success' );
|
|
$times_icon = cmplz_tc_icon( 'times', 'error' );
|
|
?>
|
|
|
|
<?php do_action( 'complianz_tc_before_label', $args ); ?>
|
|
<?php do_action( 'complianz_tc_label_html', $args ); ?>
|
|
<?php do_action( 'complianz_tc_after_label', $args ); ?>
|
|
|
|
<input <?php echo $required; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Hardcoded attribute string. ?>
|
|
class="validation <?php echo $is_required; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Hardcoded CSS class. ?>"
|
|
placeholder="<?php echo esc_html( $args['placeholder'] ); ?>"
|
|
type="email"
|
|
value="<?php echo esc_html( $value ); ?>"
|
|
name="<?php echo esc_html( $fieldname ); ?>"
|
|
id="<?php echo esc_html( $fieldname ); ?>"
|
|
>
|
|
<?php echo $check_icon; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- HTML from trusted internal helper. ?>
|
|
<?php echo $times_icon; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- HTML from trusted internal helper. ?>
|
|
|
|
<?php do_action( 'complianz_tc_after_field', $args ); ?>
|
|
|
|
<?php
|
|
}
|
|
|
|
/**
|
|
* Renders a telephone number input field.
|
|
*
|
|
* Outputs `<input type="text" autocomplete="tel">` to trigger the mobile
|
|
* keyboard's telephone layout. The submitted value is sanitised with
|
|
* sanitize_text_field() during save.
|
|
*
|
|
* @since 1.0.0
|
|
* @access public
|
|
*
|
|
* @param array $args Merged field arguments (fieldname, default, required, placeholder).
|
|
* @return void
|
|
*/
|
|
public function phone( $args ) {
|
|
if ( ! $this->show_field( $args ) ) {
|
|
return;
|
|
}
|
|
|
|
$fieldname = 'cmplz_' . $args['fieldname'];
|
|
$value = $this->get_value( $args['fieldname'], $args['default'] );
|
|
$required = $args['required'] ? 'required' : '';
|
|
$is_required = $args['required'] ? 'is-required' : '';
|
|
$check_icon = cmplz_tc_icon( 'check', 'success' );
|
|
$times_icon = cmplz_tc_icon( 'times', 'error' );
|
|
|
|
?>
|
|
|
|
<?php do_action( 'complianz_tc_before_label', $args ); ?>
|
|
<?php do_action( 'complianz_tc_label_html', $args ); ?>
|
|
<?php do_action( 'complianz_tc_after_label', $args ); ?>
|
|
|
|
<input autocomplete="tel" <?php echo $required; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Hardcoded attribute string. ?>
|
|
class="validation <?php echo $is_required; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Hardcoded CSS class. ?>"
|
|
placeholder="<?php echo esc_html( $args['placeholder'] ); ?>"
|
|
type="text"
|
|
value="<?php echo esc_html( $value ); ?>"
|
|
name="<?php echo esc_html( $fieldname ); ?>"
|
|
>
|
|
<?php echo $check_icon; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- HTML from trusted internal helper. ?>
|
|
<?php echo $times_icon; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- HTML from trusted internal helper. ?>
|
|
|
|
<?php do_action( 'complianz_tc_after_field', $args ); ?>
|
|
|
|
<?php
|
|
}
|
|
|
|
/**
|
|
* Renders a numeric input field with min and step constraints.
|
|
*
|
|
* Outputs `<input type="number">` with the configured minimum value and
|
|
* a step derived from 'validation_step' (default 1). The submitted value
|
|
* is cast to int via intval() during save.
|
|
*
|
|
* @since 1.0.0
|
|
* @access public
|
|
*
|
|
* @param array $args Merged field arguments. Relevant keys: fieldname, default,
|
|
* required, placeholder, minimum, validation_step.
|
|
* @return void
|
|
*/
|
|
public function number(
|
|
$args
|
|
) {
|
|
$fieldname = 'cmplz_' . $args['fieldname'];
|
|
$value = $this->get_value(
|
|
$args['fieldname'],
|
|
$args['default']
|
|
);
|
|
if ( ! $this->show_field( $args ) ) {
|
|
return;
|
|
}
|
|
?>
|
|
|
|
<?php do_action( 'complianz_tc_before_label', $args ); ?>
|
|
<?php do_action( 'complianz_tc_label_html', $args ); ?>
|
|
<?php do_action( 'complianz_tc_after_label', $args ); ?>
|
|
<input
|
|
<?php
|
|
if ( $args['required'] ) {
|
|
echo 'required';
|
|
}
|
|
?>
|
|
class="validation
|
|
<?php
|
|
if ( $args['required'] ) {
|
|
echo 'is-required';
|
|
}
|
|
?>
|
|
"
|
|
placeholder="<?php echo esc_html( $args['placeholder'] ); ?>"
|
|
type="number"
|
|
value="<?php echo esc_html( $value ); ?>"
|
|
name="<?php echo esc_html( $fieldname ); ?>"
|
|
id="<?php echo esc_html( $fieldname ); ?>"
|
|
min="<?php echo esc_attr( $args['minimum'] ); ?>" step="<?php echo isset( $args['validation_step'] ) ? intval( $args['validation_step'] ) : 1; ?>"
|
|
>
|
|
<?php do_action( 'complianz_tc_after_field', $args ); ?>
|
|
<?php
|
|
}
|
|
|
|
|
|
/**
|
|
* Renders a toggle-switch checkbox field.
|
|
*
|
|
* Outputs a hidden input (for unchecked state) and a styled toggle-switch
|
|
* checkbox. Supports a $force_value override used by the cookies/thirdparties
|
|
* sub-field renderers. Disabled checkboxes pass their current value through
|
|
* a hidden placeholder so it is not wiped on save.
|
|
*
|
|
* @since 1.0.0
|
|
* @access public
|
|
*
|
|
* @param array $args Merged field arguments (fieldname, default, required, disabled).
|
|
* @param mixed $force_value Optional value override; when truthy, overrides the stored value.
|
|
* Default false.
|
|
* @return void
|
|
*/
|
|
public function checkbox(
|
|
$args,
|
|
$force_value = false
|
|
) {
|
|
$fieldname = 'cmplz_' . $args['fieldname'];
|
|
|
|
$value = $force_value ? $force_value
|
|
: $this->get_value( $args['fieldname'], $args['default'] );
|
|
$placeholder_value = ( $args['disabled'] && $value ) ? $value : 0;
|
|
if ( ! $this->show_field( $args ) ) {
|
|
return;
|
|
}
|
|
?>
|
|
<?php do_action( 'complianz_tc_before_label', $args ); ?>
|
|
<?php do_action( 'complianz_tc_label_html', $args ); ?>
|
|
<?php do_action( 'complianz_tc_after_label', $args ); ?>
|
|
|
|
<label class="cmplz-switch">
|
|
<input name="<?php echo esc_html( $fieldname ); ?>" type="hidden"
|
|
value="<?php echo esc_attr( $placeholder_value ); ?>"/>
|
|
|
|
<input name="<?php echo esc_html( $fieldname ); ?>" size="40"
|
|
type="checkbox"
|
|
<?php
|
|
if ( $args['disabled'] ) {
|
|
echo 'disabled';
|
|
}
|
|
?>
|
|
class="
|
|
<?php
|
|
if ( $args['required'] ) {
|
|
echo 'is-required';
|
|
}
|
|
?>
|
|
"
|
|
value="1" <?php checked( 1, $value, true ); ?> />
|
|
<span class="cmplz-slider cmplz-round"></span>
|
|
</label>
|
|
|
|
<?php do_action( 'complianz_tc_after_field', $args ); ?>
|
|
<?php
|
|
}
|
|
|
|
/**
|
|
* Renders a group of checkboxes allowing multiple selections.
|
|
*
|
|
* Builds three parallel index arrays (value, default, disabled) keyed by
|
|
* option value, then iterates over $args['options'] to output a styled
|
|
* `.cmplz-checkbox-container` label for each option. Options in the
|
|
* disabled index are wrapped in a `.cmplz-not-allowed` div. A hidden 0
|
|
* input per option ensures unchecked values are posted. Falls back to a
|
|
* "No options found" notice when the options array is empty.
|
|
*
|
|
* @since 1.0.0
|
|
* @access public
|
|
*
|
|
* @param array $args Merged field arguments. Relevant keys: fieldname, options
|
|
* (assoc array of value => label), default, disabled (bool|array),
|
|
* required.
|
|
* @return void
|
|
*/
|
|
public function multicheckbox( $args ) {
|
|
if ( ! $this->show_field( $args ) ) {
|
|
return;
|
|
}
|
|
|
|
$fieldname = 'cmplz_' . $args['fieldname'];
|
|
|
|
// Initialize.
|
|
$default_index = array();
|
|
$disabled_index = array();
|
|
$value_index = array();
|
|
$validate = '';
|
|
$check_icon = '';
|
|
|
|
if ( ! empty( $args['options'] ) ) {
|
|
// Value index.
|
|
$value = cmplz_tc_get_value( $args['fieldname'], false, false, false );
|
|
foreach ( $args['options'] as $option_key => $option_label ) {
|
|
if ( is_array( $value ) && isset( $value[ $option_key ] ) && $value[ $option_key ] ) { // If value is not set it is an empty string.
|
|
$value_index[ $option_key ] = 'checked';
|
|
} else {
|
|
$value_index[ $option_key ] = '';
|
|
}
|
|
}
|
|
|
|
// Default index.
|
|
$defaults = apply_filters( 'cmplz_tc_default_value', $args['default'], $fieldname );
|
|
foreach ( $args['options'] as $option_key => $option_label ) {
|
|
if ( ! is_array( $defaults ) ) { // If default is not an array, treat it as a scalar.
|
|
$default_index[ $option_key ] = ( $option_key === $defaults ) ? 'cmplz-default' : '';
|
|
} else {
|
|
$default_index[ $option_key ] = in_array( $option_key, $defaults, true ) ? 'cmplz-default' : '';
|
|
}
|
|
}
|
|
|
|
// Disabled index.
|
|
foreach ( $args['options'] as $option_key => $option_label ) {
|
|
if ( is_array( $args['disabled'] ) && in_array( $option_key, $args['disabled'], true ) ) {
|
|
$disabled_index[ $option_key ] = 'cmplz-disabled';
|
|
} else {
|
|
$disabled_index[ $option_key ] = '';
|
|
}
|
|
}
|
|
|
|
// Required.
|
|
$validate = $args['required'] ? 'class="cmplz-validate-multicheckbox"' : '';
|
|
|
|
// Check icon.
|
|
$check_icon = cmplz_tc_icon( 'check', 'success' );
|
|
}
|
|
|
|
?>
|
|
|
|
<?php do_action( 'complianz_tc_before_label', $args ); ?>
|
|
<?php do_action( 'complianz_tc_label_html', $args ); ?>
|
|
<?php do_action( 'complianz_tc_after_label', $args ); ?>
|
|
|
|
<div <?php echo $validate; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Hardcoded attribute string from trusted internal logic. ?>>
|
|
|
|
<?php
|
|
if ( ! empty( $args['options'] ) ) {
|
|
foreach ( $args['options'] as $option_key => $option_label ) {
|
|
if ( 'cmplz-disabled' === $disabled_index[ $option_key ] ) {
|
|
echo '<div class="cmplz-not-allowed">';
|
|
}
|
|
?>
|
|
<label class="cmplz-checkbox-container <?php echo esc_attr( $disabled_index[ $option_key ] ); ?>"><?php echo esc_html( $option_label ); ?>
|
|
<input
|
|
name="<?php echo esc_html( $fieldname ); ?>[<?php echo esc_attr( $option_key ); ?>]"
|
|
type="hidden"
|
|
value="0"
|
|
>
|
|
<input
|
|
name="<?php echo esc_html( $fieldname ); ?>[<?php echo esc_attr( $option_key ); ?>]"
|
|
class="<?php echo esc_html( $fieldname ); ?>[<?php echo esc_attr( $option_key ); ?>]"
|
|
type="checkbox"
|
|
id="<?php echo esc_html( $fieldname ); ?>"
|
|
value="1"
|
|
<?php echo $value_index[ $option_key ]; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Hardcoded 'checked' or empty string. ?>
|
|
>
|
|
<div
|
|
class="checkmark <?php echo esc_attr( $default_index[ $option_key ] ); ?>"
|
|
<?php echo $value_index[ $option_key ]; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Hardcoded 'checked' or empty string. ?>
|
|
><?php echo $check_icon; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- HTML from trusted internal helper. ?></div>
|
|
</label>
|
|
<?php
|
|
if ( 'cmplz-disabled' === $disabled_index[ $option_key ] ) {
|
|
echo '</div>'; // Closes cmplz-not-allowed wrapper.
|
|
}
|
|
}
|
|
} else {
|
|
cmplz_tc_notice( __( 'No options found', 'complianz-terms-conditions' ) );
|
|
}
|
|
?>
|
|
|
|
</div>
|
|
|
|
<?php
|
|
do_action( 'complianz_tc_after_field', $args );
|
|
}
|
|
|
|
/**
|
|
* Renders a group of radio-button inputs.
|
|
*
|
|
* Iterates over $args['options'] to output a styled `.cmplz-radio-container`
|
|
* label for each option. Handles disabled options (individually via array or
|
|
* all via true) and marks default options with the cmplz-default CSS class.
|
|
*
|
|
* @since 1.0.0
|
|
* @access public
|
|
*
|
|
* @param array $args Merged field arguments. Relevant keys: fieldname, default,
|
|
* options (assoc value => label), required, disabled (bool|array).
|
|
* @return void
|
|
*/
|
|
public function radio( $args ) {
|
|
if ( ! $this->show_field( $args ) ) {
|
|
return;
|
|
}
|
|
|
|
$fieldname = 'cmplz_' . $args['fieldname'];
|
|
$value = $this->get_value( $args['fieldname'], $args['default'] );
|
|
$options = $args['options'];
|
|
$required = $args['required'] ? 'required' : '';
|
|
$check_icon = cmplz_tc_icon( 'bullet', 'default', '', 10 );
|
|
$disabled_index = array();
|
|
$default_index = array();
|
|
|
|
if ( ! empty( $options ) ) {
|
|
// Disabled index.
|
|
foreach ( $options as $option_value => $option_label ) {
|
|
if ( ( is_array( $args['disabled'] ) && in_array( $option_value, $args['disabled'], true ) ) || true === $args['disabled'] ) {
|
|
$disabled_index[ $option_value ] = 'cmplz-disabled';
|
|
} else {
|
|
$disabled_index[ $option_value ] = '';
|
|
}
|
|
}
|
|
// Default index.
|
|
foreach ( $options as $option_value => $option_label ) {
|
|
if ( is_array( $args['default'] ) && in_array( $option_value, $args['default'], true ) ) {
|
|
$default_index[ $option_value ] = 'cmplz-default';
|
|
} else {
|
|
$default_index[ $option_value ] = '';
|
|
}
|
|
}
|
|
}
|
|
|
|
?>
|
|
|
|
<?php do_action( 'complianz_tc_before_label', $args ); ?>
|
|
<?php do_action( 'complianz_tc_label_html', $args ); ?>
|
|
<?php do_action( 'complianz_tc_after_label', $args ); ?>
|
|
|
|
<?php
|
|
if ( ! empty( $options ) ) {
|
|
foreach ( $options as $option_value => $option_label ) {
|
|
if ( 'cmplz-disabled' === $disabled_index[ $option_value ] ) {
|
|
echo '<div class="cmplz-not-allowed">';
|
|
}
|
|
?>
|
|
<label class="cmplz-radio-container <?php echo esc_attr( $disabled_index[ $option_value ] ); ?>"><?php echo esc_html( $option_label ); ?>
|
|
<input
|
|
<?php echo $required; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Hardcoded attribute string. ?>
|
|
type="radio"
|
|
id="<?php echo esc_html( $fieldname ); ?>"
|
|
name="<?php echo esc_html( $fieldname ); ?>"
|
|
class="<?php echo esc_html( $fieldname ); ?>"
|
|
value="<?php echo esc_html( $option_value ); ?>"
|
|
<?php
|
|
if ( $option_value === $value ) {
|
|
echo 'checked';}
|
|
?>
|
|
>
|
|
<div class="radiobtn <?php echo esc_attr( $default_index[ $option_value ] ); ?>"
|
|
<?php echo $required; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Hardcoded attribute string. ?>
|
|
><?php echo $check_icon; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- HTML from trusted internal helper. ?></div>
|
|
</label>
|
|
<?php
|
|
if ( 'cmplz-disabled' === $disabled_index[ $option_value ] ) {
|
|
echo '</div>'; // Closes cmplz-not-allowed wrapper.
|
|
}
|
|
}
|
|
}
|
|
?>
|
|
|
|
<?php do_action( 'complianz_tc_after_field', $args ); ?>
|
|
<?php
|
|
}
|
|
|
|
|
|
/**
|
|
* Determines whether a field should be rendered based on its callback condition.
|
|
*
|
|
* A thin wrapper around condition_applies() that passes 'callback_condition'
|
|
* as the type. Field-type methods call this at the top to bail out early
|
|
* when the field's condition is not currently met.
|
|
*
|
|
* @since 1.0.0
|
|
* @access public
|
|
*
|
|
* @param array $args Merged field arguments containing 'callback_condition'.
|
|
* @return bool True when the field should be rendered, false when it should be hidden.
|
|
*/
|
|
public function show_field( $args ) {
|
|
$show = ( $this->condition_applies( $args, 'callback_condition' ) );
|
|
|
|
return $show;
|
|
}
|
|
|
|
|
|
/**
|
|
* Evaluates a named PHP function as a field condition.
|
|
*
|
|
* Calls the supplied function name and returns its boolean result. Supports
|
|
* negation by prefixing the function name with 'NOT ': the result is flipped
|
|
* before returning. Used by condition_applies() when a condition value is a
|
|
* callable string rather than a field/value pair.
|
|
*
|
|
* @since 1.0.0
|
|
* @access public
|
|
*
|
|
* @param string $func The callback function name. Prefix with 'NOT ' to invert the result.
|
|
* @return bool The (possibly inverted) return value of the callback function.
|
|
*/
|
|
public function function_callback_applies( $func ) {
|
|
$invert = false;
|
|
|
|
if ( strpos( $func, 'NOT ' ) !== false ) {
|
|
$invert = true;
|
|
$func = str_replace( 'NOT ', '', $func );
|
|
}
|
|
$show_field = $func();
|
|
if ( $invert ) {
|
|
$show_field = ! $show_field;
|
|
}
|
|
if ( $show_field ) {
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Evaluates whether a field's condition is currently satisfied.
|
|
*
|
|
* Supports two condition types: 'condition' (field/value pairs checked against
|
|
* current stored option values) and 'callback_condition' (PHP function names).
|
|
* Within a condition, multiple values for the same question are separated by
|
|
* commas and treated as OR. Multiple questions are AND-ed. Negation is
|
|
* expressed by prefixing a value with 'NOT '. When no condition is set the
|
|
* method returns true (field is visible by default).
|
|
*
|
|
* When checking 'condition', any array-type 'callback_condition' is merged in
|
|
* so a field can require both a stored-value condition and a function condition.
|
|
*
|
|
* @since 1.0.0
|
|
* @access public
|
|
*
|
|
* @param array $args The field argument array (merged with default_args).
|
|
* @param string|false $type The condition type to evaluate: 'condition',
|
|
* 'callback_condition', or false to auto-detect.
|
|
* Default false.
|
|
* @return bool True when the condition is satisfied (field is visible).
|
|
*/
|
|
public function condition_applies( $args, $type = false ) {
|
|
$default_args = $this->default_args;
|
|
$args = wp_parse_args( $args, $default_args );
|
|
|
|
if ( ! $type ) {
|
|
if ( $args['condition'] ) {
|
|
$type = 'condition';
|
|
} elseif ( $args['callback_condition'] ) {
|
|
$type = 'callback_condition';
|
|
}
|
|
}
|
|
|
|
if ( ! $type || ! $args[ $type ] ) {
|
|
return true;
|
|
}
|
|
|
|
// Function callbacks.
|
|
$maybe_is_function = is_string( $args[ $type ] ) ? str_replace( 'NOT ', '', $args[ $type ] ) : '';
|
|
if ( ! is_array( $args[ $type ] ) && ! empty( $args[ $type ] ) && function_exists( $maybe_is_function ) ) {
|
|
return $this->function_callback_applies( $args[ $type ] );
|
|
}
|
|
|
|
$condition = $args[ $type ];
|
|
|
|
// if we're checking the condition, but there's also a callback condition, check that one as well.
|
|
// but only if it's an array. Otherwise it's a func.
|
|
if ( 'condition' === $type && isset( $args['callback_condition'] ) && is_array( $args['callback_condition'] ) ) {
|
|
$condition += $args['callback_condition'];
|
|
}
|
|
|
|
foreach ( $condition as $c_fieldname => $c_value_content ) {
|
|
$c_values = $c_value_content;
|
|
// the possible multiple values are separated with comma instead of an array, so we can add NOT.
|
|
if ( ! is_array( $c_value_content ) && strpos( $c_value_content, ',' ) !== false ) {
|
|
$c_values = explode( ',', $c_value_content );
|
|
}
|
|
$c_values = is_array( $c_values ) ? $c_values : array( $c_values );
|
|
|
|
foreach ( $c_values as $c_value ) {
|
|
$maybe_is_function = str_replace( 'NOT ', '', $c_value );
|
|
if ( function_exists( $maybe_is_function ) ) {
|
|
$match = $this->function_callback_applies( $c_value );
|
|
if ( ! $match ) {
|
|
return false;
|
|
}
|
|
} else {
|
|
$actual_value = cmplz_tc_get_value( $c_fieldname );
|
|
|
|
$fieldtype = $this->get_field_type( $c_fieldname );
|
|
|
|
if ( strpos( $c_value, 'NOT ' ) === false ) {
|
|
$invert = false;
|
|
} else {
|
|
$invert = true;
|
|
$c_value = str_replace( 'NOT ', '', $c_value );
|
|
}
|
|
|
|
if ( 'multicheckbox' === $fieldtype ) {
|
|
if ( ! is_array( $actual_value ) ) {
|
|
$actual_value = array( $actual_value );
|
|
}
|
|
// Get all items that are set to true.
|
|
$actual_value = array_filter(
|
|
$actual_value,
|
|
function ( $item ) {
|
|
return 1 === $item;
|
|
}
|
|
);
|
|
$actual_value = array_keys( $actual_value );
|
|
|
|
$match = false;
|
|
foreach ( $c_values as $check_each_value ) {
|
|
if ( in_array(
|
|
$check_each_value,
|
|
$actual_value,
|
|
true
|
|
)
|
|
) {
|
|
$match = true;
|
|
}
|
|
}
|
|
} else {
|
|
// When the actual value is an array, one match is enough.
|
|
// Check all items; return false only when none matched.
|
|
// This preserves the AND property of the outer condition.
|
|
$match = ( $c_value === $actual_value || in_array( $actual_value, $c_values, true ) );
|
|
|
|
}
|
|
if ( $invert ) {
|
|
$match = ! $match;
|
|
}
|
|
if ( ! $match ) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Returns the field type string for a given fieldname.
|
|
*
|
|
* Looks up the 'type' key in the plugin config for the supplied fieldname.
|
|
* Used by condition_applies() to handle multicheckbox fields differently
|
|
* from scalar fields during condition evaluation.
|
|
*
|
|
* @since 1.0.0
|
|
* @access public
|
|
*
|
|
* @param string $fieldname The field identifier to look up.
|
|
* @return string|false The field type string (e.g. 'text', 'multicheckbox'),
|
|
* or false when the fieldname is not registered.
|
|
*/
|
|
public function get_field_type( $fieldname ) {
|
|
if ( ! isset( COMPLIANZ_TC::$config->fields[ $fieldname ] ) ) {
|
|
return false;
|
|
}
|
|
|
|
return COMPLIANZ_TC::$config->fields[ $fieldname ]['type'];
|
|
}
|
|
|
|
/**
|
|
* Renders a multi-line textarea input field.
|
|
*
|
|
* Outputs a `<textarea>` element with the current stored value, the
|
|
* cmplz_-prefixed field name, optional required attribute, and check/times
|
|
* validation icons. The submitted value is sanitised with wp_kses_post()
|
|
* during save.
|
|
*
|
|
* @since 1.0.0
|
|
* @access public
|
|
*
|
|
* @param array $args Merged field arguments (fieldname, default, required, placeholder).
|
|
* @return void
|
|
*/
|
|
public function textarea(
|
|
$args
|
|
) {
|
|
$fieldname = 'cmplz_' . $args['fieldname'];
|
|
$check_icon = cmplz_tc_icon( 'check', 'success' );
|
|
$times_icon = cmplz_tc_icon( 'times', 'error' );
|
|
$value = $this->get_value( $args['fieldname'], $args['default'] );
|
|
if ( ! $this->show_field( $args ) ) {
|
|
return;
|
|
}
|
|
?>
|
|
<?php do_action( 'complianz_tc_before_label', $args ); ?>
|
|
<?php do_action( 'complianz_tc_label_html', $args ); ?>
|
|
<?php do_action( 'complianz_tc_after_label', $args ); ?>
|
|
<textarea name="<?php echo esc_html( $fieldname ); ?>" id="<?php echo esc_html( $fieldname ); ?>"
|
|
<?php
|
|
if ( $args['required'] ) {
|
|
echo 'required';
|
|
}
|
|
?>
|
|
class="validation
|
|
<?php
|
|
if ( $args['required'] ) {
|
|
echo 'is-required';
|
|
}
|
|
?>
|
|
"
|
|
placeholder="<?php echo esc_html( $args['placeholder'] ); ?>"><?php echo esc_html( $value ); ?></textarea>
|
|
|
|
<?php echo $check_icon; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- HTML from trusted internal helper. ?>
|
|
<?php echo $times_icon; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- HTML from trusted internal helper. ?>
|
|
<?php do_action( 'complianz_tc_after_field', $args ); ?>
|
|
<?php
|
|
}
|
|
|
|
/**
|
|
* Renders a WP-editor (TinyMCE/Gutenberg) rich-text field.
|
|
*
|
|
* Forces the 'first' flag so the field occupies the full wizard column.
|
|
* Passes media_buttons (controlled by $args['media']), editor_height of 300 px,
|
|
* and textarea_rows of 15 to wp_editor(). The submitted content is sanitised
|
|
* with wp_kses_post() during save.
|
|
*
|
|
* @since 1.0.0
|
|
* @access public
|
|
*
|
|
* @param array $args Merged field arguments (fieldname, default, media).
|
|
* @param string $step Current wizard step identifier (passed through to wp_editor). Default ''.
|
|
* @return void
|
|
*/
|
|
public function editor( $args, $step = '' ) { // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundAfterLastUsed -- $step is part of the public API signature; callers may pass it for future use.
|
|
$fieldname = 'cmplz_' . $args['fieldname'];
|
|
$args['first'] = true;
|
|
$media = $args['media'] ? true : false;
|
|
|
|
$value = $this->get_value( $args['fieldname'], $args['default'] );
|
|
|
|
if ( ! $this->show_field( $args ) ) {
|
|
return;
|
|
}
|
|
|
|
?>
|
|
|
|
<?php do_action( 'complianz_tc_before_label', $args ); ?>
|
|
<?php do_action( 'complianz_tc_label_html', $args ); ?>
|
|
<?php do_action( 'complianz_tc_after_label', $args ); ?>
|
|
|
|
<?php
|
|
$settings = array(
|
|
'media_buttons' => $media,
|
|
'editor_height' => 300,
|
|
// In pixels; takes precedence over textarea_rows and has no default value.
|
|
'textarea_rows' => 15,
|
|
);
|
|
wp_editor( $value, $fieldname, $settings );
|
|
?>
|
|
<?php do_action( 'complianz_tc_after_field', $args ); ?>
|
|
<?php
|
|
}
|
|
|
|
/**
|
|
* Renders a JavaScript code editor field using the Ace editor library.
|
|
*
|
|
* Outputs a div styled for Ace, a hidden `<textarea>` (the actual POST value),
|
|
* and an inline script that initialises Ace in JavaScript mode and syncs
|
|
* changes back to the textarea. The saved value is stored as-is (no
|
|
* sanitisation beyond the capability check in sanitize()).
|
|
*
|
|
* @since 1.0.0
|
|
* @access public
|
|
*
|
|
* @param array $args Merged field arguments (fieldname, default).
|
|
* @return void
|
|
*/
|
|
public function javascript(
|
|
$args
|
|
) {
|
|
$fieldname = 'cmplz_' . $args['fieldname'];
|
|
$value = $this->get_value(
|
|
$args['fieldname'],
|
|
$args['default']
|
|
);
|
|
if ( ! $this->show_field( $args ) ) {
|
|
return;
|
|
}
|
|
?>
|
|
|
|
<?php do_action( 'complianz_tc_before_label', $args ); ?>
|
|
<?php do_action( 'complianz_tc_label_html', $args ); ?>
|
|
<?php do_action( 'complianz_tc_after_label', $args ); ?>
|
|
<div id="<?php echo esc_html( $fieldname ); ?>editor"
|
|
style="height: 200px; width: 100%"><?php echo esc_html( $value ); ?></div>
|
|
<?php do_action( 'complianz_tc_after_field', $args ); ?>
|
|
<script>
|
|
var <?php echo esc_html( $fieldname ); ?> =
|
|
ace.edit("<?php echo esc_html( $fieldname ); ?>editor");
|
|
<?php echo esc_html( $fieldname ); ?>.setTheme("ace/theme/monokai");
|
|
<?php echo esc_html( $fieldname ); ?>.session.setMode("ace/mode/javascript");
|
|
jQuery(document).ready(function ($) {
|
|
var textarea = $('textarea[name="<?php echo esc_html( $fieldname ); ?>"]');
|
|
<?php echo esc_html( $fieldname ); ?>.
|
|
getSession().on("change", function () {
|
|
textarea.val(<?php echo esc_html( $fieldname ); ?>.getSession().getValue()
|
|
)
|
|
});
|
|
});
|
|
</script>
|
|
<textarea style="display:none"
|
|
name="<?php echo esc_html( $fieldname ); ?>"><?php echo esc_html( $value ); ?></textarea>
|
|
<?php
|
|
}
|
|
|
|
/**
|
|
* Renders a CSS code editor field using the Ace editor library.
|
|
*
|
|
* Identical to javascript() but initialises Ace in CSS mode and uses a
|
|
* slightly taller editor (290 px vs 200 px). The saved value is stored
|
|
* as-is (no sanitisation beyond the capability check in sanitize()).
|
|
*
|
|
* @since 1.0.0
|
|
* @access public
|
|
*
|
|
* @param array $args Merged field arguments (fieldname, default).
|
|
* @return void
|
|
*/
|
|
public function css(
|
|
$args
|
|
) {
|
|
$fieldname = 'cmplz_' . $args['fieldname'];
|
|
|
|
$value = $this->get_value( $args['fieldname'], $args['default'] );
|
|
if ( ! $this->show_field( $args ) ) {
|
|
return;
|
|
}
|
|
?>
|
|
|
|
<?php do_action( 'complianz_tc_before_label', $args ); ?>
|
|
<?php do_action( 'complianz_tc_label_html', $args ); ?>
|
|
<?php do_action( 'complianz_tc_after_label', $args ); ?>
|
|
<div id="<?php echo esc_html( $fieldname ); ?>editor"
|
|
style="height: 290px; width: 100%"><?php echo esc_html( $value ); ?></div>
|
|
<?php do_action( 'complianz_tc_after_field', $args ); ?>
|
|
<script>
|
|
var <?php echo esc_html( $fieldname ); ?> =
|
|
ace.edit("<?php echo esc_html( $fieldname ); ?>editor");
|
|
<?php echo esc_html( $fieldname ); ?>.setTheme("ace/theme/monokai");
|
|
<?php echo esc_html( $fieldname ); ?>.session.setMode("ace/mode/css");
|
|
jQuery(document).ready(function ($) {
|
|
var textarea = $('textarea[name="<?php echo esc_html( $fieldname ); ?>"]');
|
|
<?php echo esc_html( $fieldname ); ?>.
|
|
getSession().on("change", function () {
|
|
textarea.val(<?php echo esc_html( $fieldname ); ?>.getSession().getValue()
|
|
)
|
|
});
|
|
});
|
|
</script>
|
|
<textarea style="display:none"
|
|
name="<?php echo esc_html( $fieldname ); ?>"><?php echo esc_html( $value ); ?></textarea>
|
|
<?php
|
|
}
|
|
|
|
/**
|
|
* Checks whether a wizard step/section contains at least one visible field.
|
|
*
|
|
* Iterates over all fields for the given page/step/section combination and
|
|
* returns true on the first field that either has a callback (always shown)
|
|
* or passes show_field(). Returns false when all fields are hidden by their
|
|
* conditions. Used by the wizard to skip empty steps.
|
|
*
|
|
* @since 1.0.0
|
|
* @access public
|
|
*
|
|
* @param string $page The option source / document type to query fields for.
|
|
* @param int|false $step The wizard step index. Default false (all steps).
|
|
* @param int|false $section The wizard section index. Default false (all sections).
|
|
* @return bool True when at least one field is visible in the step/section.
|
|
*/
|
|
public function step_has_fields( $page, $step = false, $section = false ) {
|
|
$fields = COMPLIANZ_TC::$config->fields( $page, $step, $section );
|
|
foreach ( $fields as $fieldname => $args ) {
|
|
$default_args = $this->default_args;
|
|
$args = wp_parse_args( $args, $default_args );
|
|
|
|
$type = ( $args['callback'] ) ? 'callback'
|
|
: $args['type'];
|
|
$args['fieldname'] = $fieldname;
|
|
|
|
if ( 'callback' === $type ) {
|
|
return true;
|
|
} elseif ( $this->show_field( $args ) ) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Retrieves and renders all fields for a wizard step/section.
|
|
*
|
|
* Fetches the field list from the config, marks the first field with
|
|
* $args['first'] = true for CSS-first-child targeting, merges each field's
|
|
* args with default_args, then dispatches to the appropriate field-type
|
|
* method via a switch statement. Supports all registered types: callback,
|
|
* text, url, email, phone, number, checkbox, multicheckbox, radio, select,
|
|
* textarea, editor, javascript, css, notice, label, button, upload, document,
|
|
* cookies, services, multiple, thirdparties, processors, colorpicker,
|
|
* borderradius, borderwidth.
|
|
*
|
|
* @since 1.0.0
|
|
* @access public
|
|
*
|
|
* @param string $source The option source identifier used to filter fields.
|
|
* @param int|false $step The wizard step index. Default false.
|
|
* @param int|false $section The section index. Default false.
|
|
* @param string|false $get_by_fieldname Retrieve a single field by name. Default false.
|
|
* @return void
|
|
*/
|
|
public function get_fields(
|
|
$source,
|
|
$step = false,
|
|
$section = false,
|
|
$get_by_fieldname = false
|
|
) {
|
|
|
|
$fields = COMPLIANZ_TC::$config->fields(
|
|
$source,
|
|
$step,
|
|
$section,
|
|
$get_by_fieldname
|
|
);
|
|
|
|
$i = 0;
|
|
foreach ( $fields as $fieldname => $args ) {
|
|
if ( 0 === $i ) {
|
|
$args['first'] = true;
|
|
}
|
|
++$i;
|
|
$default_args = $this->default_args;
|
|
$args = wp_parse_args( $args, $default_args );
|
|
|
|
$type = ( $args['callback'] ) ? 'callback'
|
|
: $args['type'];
|
|
$args['fieldname'] = $fieldname;
|
|
switch ( $type ) {
|
|
case 'callback':
|
|
$this->callback( $args );
|
|
break;
|
|
case 'text':
|
|
$this->text( $args );
|
|
break;
|
|
case 'document':
|
|
$this->document( $args );
|
|
break;
|
|
case 'button':
|
|
$this->button( $args );
|
|
break;
|
|
case 'upload':
|
|
$this->upload( $args );
|
|
break;
|
|
case 'url':
|
|
$this->url( $args );
|
|
break;
|
|
case 'select':
|
|
$this->select( $args );
|
|
break;
|
|
case 'colorpicker':
|
|
$this->colorpicker( $args );
|
|
break;
|
|
case 'borderradius':
|
|
$this->border_radius( $args );
|
|
break;
|
|
case 'borderwidth':
|
|
$this->border_width( $args );
|
|
break;
|
|
case 'checkbox':
|
|
$this->checkbox( $args );
|
|
break;
|
|
case 'textarea':
|
|
$this->textarea( $args );
|
|
break;
|
|
case 'cookies':
|
|
$this->cookies( $args );
|
|
break;
|
|
case 'services':
|
|
$this->services( $args );
|
|
break;
|
|
case 'multiple':
|
|
$this->multiple( $args );
|
|
break;
|
|
case 'radio':
|
|
$this->radio( $args );
|
|
break;
|
|
case 'multicheckbox':
|
|
$this->multicheckbox( $args );
|
|
break;
|
|
case 'javascript':
|
|
$this->javascript( $args );
|
|
break;
|
|
case 'css':
|
|
$this->css( $args );
|
|
break;
|
|
case 'email':
|
|
$this->email( $args );
|
|
break;
|
|
case 'phone':
|
|
$this->phone( $args );
|
|
break;
|
|
case 'thirdparties':
|
|
$this->thirdparties( $args );
|
|
break;
|
|
case 'processors':
|
|
$this->processors( $args );
|
|
break;
|
|
case 'number':
|
|
$this->number( $args );
|
|
break;
|
|
case 'notice':
|
|
$this->notice( $args );
|
|
break;
|
|
case 'editor':
|
|
$this->editor( $args );
|
|
break;
|
|
case 'label':
|
|
$this->label( $args );
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Renders a field whose content is provided by a custom action hook.
|
|
*
|
|
* Fires the label hooks (before_label, label_html, after_label) then
|
|
* dispatches to the `cmplz_tc_{callback}` action, where `{callback}` is
|
|
* the field's 'callback' key. This allows plugin extensions to inject
|
|
* arbitrary HTML into any wizard field position. Fires after_field last.
|
|
*
|
|
* @since 1.0.0
|
|
* @access public
|
|
*
|
|
* @param array $args Merged field arguments. Uses 'callback' (string) as the action suffix.
|
|
* @return void
|
|
*/
|
|
public function callback(
|
|
$args
|
|
) {
|
|
$callback = $args['callback'];
|
|
do_action( 'complianz_tc_before_label', $args );
|
|
?>
|
|
<?php do_action( 'complianz_tc_label_html', $args ); ?>
|
|
<?php
|
|
do_action( 'complianz_tc_after_label', $args );
|
|
do_action( "cmplz_tc_$callback", $args );
|
|
do_action( 'complianz_tc_after_field', $args );
|
|
}
|
|
|
|
/**
|
|
* Renders an inline warning notice in place of a field.
|
|
*
|
|
* Used for fields of type 'notice' to display a styled warning message
|
|
* (via cmplz_tc_notice()) where a normal input would appear. The field
|
|
* label text is used as the notice message.
|
|
*
|
|
* @since 1.0.0
|
|
* @access public
|
|
*
|
|
* @param array $args Merged field arguments. Uses 'label' (string) as the notice message.
|
|
* @return void
|
|
*/
|
|
public function notice(
|
|
$args
|
|
) {
|
|
if ( ! $this->show_field( $args ) ) {
|
|
return;
|
|
}
|
|
do_action( 'complianz_tc_before_label', $args );
|
|
cmplz_tc_notice( $args['label'], 'warning' );
|
|
do_action( 'complianz_tc_after_label', $args );
|
|
do_action( 'complianz_tc_after_field', $args );
|
|
}
|
|
|
|
/**
|
|
* Renders a `<select>` dropdown field.
|
|
*
|
|
* Outputs a dropdown with a blank "Choose an option" placeholder followed by
|
|
* one `<option>` per entry in $args['options']. The currently stored value is
|
|
* pre-selected. The submitted value is sanitised with sanitize_text_field()
|
|
* on save (default path in sanitize()).
|
|
*
|
|
* @since 1.0.0
|
|
* @access public
|
|
*
|
|
* @param array $args Merged field arguments. Relevant keys: fieldname, default,
|
|
* options (assoc value => label), required.
|
|
* @return void
|
|
*/
|
|
public function select(
|
|
$args
|
|
) {
|
|
|
|
$fieldname = 'cmplz_' . $args['fieldname'];
|
|
|
|
$value = $this->get_value( $args['fieldname'], $args['default'] );
|
|
if ( ! $this->show_field( $args ) ) {
|
|
return;
|
|
}
|
|
|
|
?>
|
|
<?php do_action( 'complianz_tc_before_label', $args ); ?>
|
|
<?php do_action( 'complianz_tc_label_html', $args ); ?>
|
|
<?php do_action( 'complianz_tc_after_label', $args ); ?>
|
|
<select
|
|
<?php
|
|
if ( $args['required'] ) {
|
|
echo 'required';
|
|
}
|
|
?>
|
|
name="<?php echo esc_html( $fieldname ); ?>" id="<?php echo esc_attr( $fieldname ); ?>">
|
|
<option value="">
|
|
<?php
|
|
esc_html_e(
|
|
'Choose an option',
|
|
'complianz-terms-conditions'
|
|
)
|
|
?>
|
|
</option>
|
|
<?php
|
|
foreach (
|
|
$args['options'] as $option_key => $option_label
|
|
) {
|
|
?>
|
|
<option
|
|
value="<?php echo esc_html( $option_key ); ?>"
|
|
<?php
|
|
echo ( $value === $option_key )
|
|
? 'selected'
|
|
: ''
|
|
?>
|
|
><?php echo esc_html( $option_label ); ?></option>
|
|
<?php } ?>
|
|
</select>
|
|
|
|
<?php do_action( 'complianz_tc_after_field', $args ); ?>
|
|
<?php
|
|
}
|
|
|
|
/**
|
|
* Renders a display-only label field with no interactive input.
|
|
*
|
|
* Used for fields of type 'label' that provide contextual headings or
|
|
* descriptive text within the wizard layout. Fires the standard label
|
|
* hooks and after_field but outputs no input element.
|
|
*
|
|
* @since 1.0.0
|
|
* @access public
|
|
*
|
|
* @param array $args Merged field arguments (fieldname).
|
|
* @return void
|
|
*/
|
|
public function label(
|
|
$args
|
|
) {
|
|
|
|
$fieldname = 'cmplz_' . $args['fieldname'];
|
|
if ( ! $this->show_field( $args ) ) {
|
|
return;
|
|
}
|
|
|
|
?>
|
|
<?php do_action( 'complianz_tc_before_label', $args ); ?>
|
|
<?php do_action( 'complianz_tc_label_html', $args ); ?>
|
|
<?php do_action( 'complianz_tc_after_label', $args ); ?>
|
|
|
|
<?php do_action( 'complianz_tc_after_field', $args ); ?>
|
|
<?php
|
|
}
|
|
|
|
/**
|
|
* Renders a button or form-submit action field.
|
|
*
|
|
* Supports two modes controlled by $args['post_get']: 'get' renders an
|
|
* anchor tag linking to the settings page with an 'action' query arg;
|
|
* anything else renders a `<input type="submit">` that POSTs to the wizard.
|
|
* Disabled buttons render as grey with href="#" (GET mode) or the HTML
|
|
* disabled attribute (POST mode). Supports a confirmation dialog via
|
|
* $args['warn'].
|
|
*
|
|
* @since 1.0.0
|
|
* @access public
|
|
*
|
|
* @param array $args Merged field arguments. Relevant keys: fieldname, label,
|
|
* disabled (bool), post_get ('get'|'post'), action (string),
|
|
* warn (string|false) — confirm() message text.
|
|
* @return void
|
|
*/
|
|
public function button(
|
|
$args
|
|
) {
|
|
$fieldname = 'cmplz_' . $args['fieldname'];
|
|
if ( ! $this->show_field( $args ) ) {
|
|
return;
|
|
}
|
|
|
|
?>
|
|
<?php do_action( 'complianz_tc_before_label', $args ); ?>
|
|
<?php do_action( 'complianz_tc_label_html', $args ); ?>
|
|
<?php do_action( 'complianz_tc_after_label', $args ); ?>
|
|
<?php if ( 'get' === $args['post_get'] ) { ?>
|
|
<a
|
|
<?php
|
|
if ( $args['disabled'] ) {
|
|
echo 'disabled'; }
|
|
?>
|
|
href="
|
|
<?php
|
|
echo $args['disabled']
|
|
? '#'
|
|
: esc_url( cmplz_tc_settings_page() . '&action=' . $args['action'] )
|
|
?>
|
|
"
|
|
class="button"><?php echo esc_html( $args['label'] ); ?></a>
|
|
<?php } else { ?>
|
|
<input
|
|
<?php
|
|
if ( $args['warn'] ) {
|
|
echo 'onclick="return confirm(\'' . esc_js( $args['warn'] )
|
|
. '\');"'; }
|
|
?>
|
|
<?php
|
|
if ( $args['disabled'] ) {
|
|
echo 'disabled';
|
|
}
|
|
?>
|
|
class="button" type="submit"
|
|
name="<?php echo esc_attr( $args['action'] ); ?>"
|
|
value="<?php echo esc_html( $args['label'] ); ?>">
|
|
<?php } ?>
|
|
|
|
<?php do_action( 'complianz_tc_after_field', $args ); ?>
|
|
<?php
|
|
}
|
|
|
|
/**
|
|
* Renders a file-upload field pair (file picker + submit button).
|
|
*
|
|
* Outputs a file input named cmplz-upload-file and a submit button whose
|
|
* name comes from $args['action']. The submit is disabled when $args['disabled']
|
|
* is true.
|
|
*
|
|
* @since 1.0.0
|
|
* @access public
|
|
*
|
|
* @param array $args Merged field arguments. Relevant keys: label, disabled (bool),
|
|
* action (string — the submit button name).
|
|
* @return void
|
|
*/
|
|
public function upload(
|
|
$args
|
|
) {
|
|
if ( ! $this->show_field( $args ) ) {
|
|
return;
|
|
}
|
|
|
|
?>
|
|
<?php do_action( 'complianz_tc_before_label', $args ); ?>
|
|
<?php do_action( 'complianz_tc_label_html', $args ); ?>
|
|
<?php do_action( 'complianz_tc_after_label', $args ); ?>
|
|
|
|
<input type="file" type="submit" name="cmplz-upload-file"
|
|
value="<?php echo esc_html( $args['label'] ); ?>">
|
|
<input
|
|
<?php
|
|
if ( $args['disabled'] ) {
|
|
echo 'disabled'; }
|
|
?>
|
|
class="button" type="submit"
|
|
name="<?php echo esc_attr( $args['action'] ); ?>"
|
|
value="
|
|
<?php
|
|
esc_html_e(
|
|
'Start',
|
|
'complianz-terms-conditions'
|
|
)
|
|
?>
|
|
">
|
|
<?php do_action( 'complianz_tc_after_field', $args ); ?>
|
|
<?php
|
|
}
|
|
|
|
|
|
/**
|
|
* Renders the wizard form's nonce field and Save button.
|
|
*
|
|
* Outputs the complianz_tc_nonce hidden input (used to verify the form
|
|
* submission in process_save()) and a primary button-style submit input.
|
|
* Must be called inside a `<table>` / `<tr>` wrapper in the wizard template.
|
|
*
|
|
* @since 1.0.0
|
|
* @access public
|
|
*
|
|
* @return void
|
|
*/
|
|
public function save_button() {
|
|
wp_nonce_field( 'complianz_tc_save', 'complianz_tc_nonce' );
|
|
?>
|
|
<th></th>
|
|
<td>
|
|
<input class="button button-primary" type="submit"
|
|
name="cmplz-save"
|
|
value="<?php esc_html_e( 'Save', 'complianz-terms-conditions' ); ?>">
|
|
|
|
</td>
|
|
<?php
|
|
}
|
|
|
|
|
|
/**
|
|
* Renders a simple repeatable text-area field with add/remove controls.
|
|
*
|
|
* Outputs an "Add new" submit button and, for each stored row, a textarea
|
|
* for the 'description' sub-field and a "Remove" button. Row data is
|
|
* submitted as cmplz_multiple[{fieldname}][{key}][description]. This is
|
|
* a lightweight alternative to cookies/thirdparties for simple repeatable
|
|
* text entries.
|
|
*
|
|
* @since 1.0.0
|
|
* @access public
|
|
*
|
|
* @param array $args Merged field arguments (fieldname, label).
|
|
* @return void
|
|
*/
|
|
public function multiple(
|
|
$args
|
|
) {
|
|
$values = $this->get_value( $args['fieldname'] );
|
|
if ( ! $this->show_field( $args ) ) {
|
|
return;
|
|
}
|
|
?>
|
|
<?php do_action( 'complianz_before_label', $args ); ?>
|
|
<label><?php echo esc_html( $args['label'] ); ?></label>
|
|
<?php do_action( 'complianz_after_label', $args ); ?>
|
|
<button class="button" type="submit" name="cmplz_add_multiple"
|
|
value="<?php echo esc_html( $args['fieldname'] ); ?>">
|
|
<?php
|
|
esc_html_e(
|
|
'Add new',
|
|
'complianz-terms-conditions'
|
|
)
|
|
?>
|
|
</button>
|
|
<br><br>
|
|
<?php
|
|
if ( $values ) {
|
|
foreach ( $values as $key => $value ) {
|
|
?>
|
|
|
|
<div>
|
|
<div>
|
|
<label>
|
|
<?php
|
|
esc_html_e(
|
|
'Description',
|
|
'complianz-terms-conditions'
|
|
)
|
|
?>
|
|
</label>
|
|
</div>
|
|
<div>
|
|
<textarea class="cmplz_multiple"
|
|
name="cmplz_multiple[<?php echo esc_html( $args['fieldname'] ); ?>][<?php echo esc_attr( $key ); ?>][description]">
|
|
<?php
|
|
if ( isset( $value['description'] ) ) {
|
|
echo esc_html( $value['description'] ); }
|
|
?>
|
|
</textarea>
|
|
</div>
|
|
|
|
</div>
|
|
<button class="button cmplz-remove" type="submit"
|
|
name="cmplz_remove_multiple[<?php echo esc_html( $args['fieldname'] ); ?>]"
|
|
value="<?php echo esc_attr( $key ); ?>">
|
|
<?php
|
|
esc_html_e(
|
|
'Remove',
|
|
'complianz-terms-conditions'
|
|
)
|
|
?>
|
|
</button>
|
|
<?php
|
|
}
|
|
}
|
|
?>
|
|
<?php do_action( 'complianz_after_field', $args ); ?>
|
|
<?php
|
|
}
|
|
|
|
/**
|
|
* Returns a localised heading string for a language-grouped cookies/services block.
|
|
*
|
|
* Produces either "Cookies in {language name}" or "Services in {language name}",
|
|
* where the language name is resolved from the plugin's language_codes map.
|
|
* Falls back to the uppercased locale code when the language is not in the map.
|
|
*
|
|
* @since 1.0.0
|
|
* @access private
|
|
*
|
|
* @param string $language The ISO 639-1 language code (e.g. 'nl', 'fr').
|
|
* @param string $type Display context: 'cookie' (default) or 'service'.
|
|
* @return string The localised heading string.
|
|
*/
|
|
private function get_language_descriptor( $language, $type = 'cookie' ) {
|
|
// translators: %s is the name of the language (e.g. "Dutch", "French").
|
|
$string = 'cookie' === $type ? __( 'Cookies in %s', 'complianz-terms-conditions' ) : __( 'Services in %s', 'complianz-terms-conditions' );
|
|
if ( isset( COMPLIANZ_TC::$config->language_codes[ $language ] ) ) {
|
|
$string = sprintf(
|
|
$string,
|
|
COMPLIANZ_TC::$config->language_codes[ $language ]
|
|
);
|
|
} else {
|
|
$string = sprintf(
|
|
$string,
|
|
strtoupper( $language )
|
|
);
|
|
}
|
|
|
|
return $string;
|
|
}
|
|
|
|
/**
|
|
* Returns the current stored value for a field, with a default fallback.
|
|
*
|
|
* Looks up the field's source option group in the config, reads it from
|
|
* the WordPress options table, and applies the cmplz_tc_default_value filter
|
|
* when no value has been stored yet. Returns false when the fieldname is
|
|
* not registered in the config.
|
|
*
|
|
* @since 1.0.0
|
|
* @access public
|
|
*
|
|
* @param string $fieldname The field identifier to retrieve.
|
|
* @param mixed $default_value Value to return (after filtering) when nothing is stored.
|
|
* Default '' (empty string).
|
|
* @return mixed The stored value, the filtered default, or false when the
|
|
* fieldname is not found in the config.
|
|
*/
|
|
public function get_value( $fieldname, $default_value = '' ) { // phpcs:ignore Universal.NamingConventions.NoReservedKeywordParameterNames.defaultFound -- Renaming would break all callers.
|
|
$fields = COMPLIANZ_TC::$config->fields();
|
|
|
|
if ( ! isset( $fields[ $fieldname ] ) ) {
|
|
return false;
|
|
}
|
|
|
|
$source = $fields[ $fieldname ]['source'];
|
|
$options = get_option( 'complianz_tc_options_' . $source );
|
|
$value = isset( $options[ $fieldname ] )
|
|
? $options[ $fieldname ] : false;
|
|
|
|
// If no value is stored, apply the filtered default.
|
|
$value = ( false !== $value ) ? $value
|
|
: apply_filters( 'cmplz_tc_default_value', $default_value, $fieldname );
|
|
|
|
return $value;
|
|
}
|
|
|
|
/**
|
|
* Validates and sanitises a fieldname against the registered field list.
|
|
*
|
|
* Returns a sanitize_text_field()-cleaned version of the fieldname when it
|
|
* exists in the config, or false when it does not. Used to prevent arbitrary
|
|
* fieldname injection when processing the cmplz_tc_add_multiple POST key.
|
|
*
|
|
* @since 1.0.0
|
|
* @access public
|
|
*
|
|
* @param string $fieldname The raw fieldname to validate (may come from $_POST).
|
|
* @return string|false Sanitised fieldname string, or false when not registered.
|
|
*/
|
|
public function sanitize_fieldname(
|
|
$fieldname
|
|
) {
|
|
$fields = COMPLIANZ_TC::$config->fields();
|
|
if ( array_key_exists( $fieldname, $fields ) ) {
|
|
return sanitize_text_field( $fieldname );
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
/**
|
|
* Renders an optional explanatory comment below a field's input.
|
|
*
|
|
* Outputs the 'comment' value from the field args inside a `.cmplz-comment`
|
|
* div. Returns early when no comment is set. The comment is output without
|
|
* escaping, so callers are responsible for any HTML sanitisation.
|
|
*
|
|
* @since 1.0.0
|
|
* @access public
|
|
*
|
|
* @param array $args Field arguments. Uses 'comment' (string) when present.
|
|
* @return void
|
|
*/
|
|
public function get_comment(
|
|
$args
|
|
) {
|
|
if ( ! isset( $args['comment'] ) ) {
|
|
return;
|
|
}
|
|
?>
|
|
<div class="cmplz-comment"><?php echo $args['comment']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Comment content may contain trusted HTML; callers are responsible for sanitisation. ?></div>
|
|
<?php
|
|
}
|
|
|
|
/**
|
|
* Returns whether any required-field validation errors were recorded.
|
|
*
|
|
* Checks whether the form_errors list is non-empty after a save. The wizard
|
|
* uses this to decide whether to advance to the next step or keep the user
|
|
* on the current step.
|
|
*
|
|
* @since 1.0.0
|
|
* @access public
|
|
*
|
|
* @return bool True when at least one required field was empty on save, false otherwise.
|
|
*/
|
|
public function has_errors() {
|
|
if ( count( $this->form_errors ) > 0 ) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
}
|
|
} //class closure
|