prefix . 'pys_options'; } private static function table_exists(): bool { if (self::$table_exists !== null) { return self::$table_exists; } // Try object cache first $cached = wp_cache_get('pys_options_table_exists', 'pys'); if ($cached !== false) { return self::$table_exists = (bool) $cached; } global $wpdb; $table = self::storage_table(); $exists = (bool) $wpdb->get_var( $wpdb->prepare("SHOW TABLES LIKE %s", $table) ); if (!$exists) { // Create table $charset_collate = $wpdb->get_charset_collate(); $sql = "CREATE TABLE IF NOT EXISTS $table ( id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, option_name VARCHAR(191) NOT NULL, option_value LONGTEXT NOT NULL, migrated TINYINT(1) NOT NULL DEFAULT 1, updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY option_name (option_name) ) $charset_collate;"; require_once ABSPATH . 'wp-admin/includes/upgrade.php'; dbDelta($sql); // After dbDelta table exists $exists = true; } // Cache for 12h; adjust as you like wp_cache_set('pys_options_table_exists', $exists, 'pys', 12 * HOUR_IN_SECONDS); return self::$table_exists = $exists; } /** * Constructor * * @param string $slug */ public function __construct( $slug ) { $this->slug = $slug; $this->option_key = 'pys_' . $slug; } public function getSlug() { return $this->slug; } /** * Load options fields and options defaults from specified files * * @param string $fields Path to options fields file * @param string $defaults Path to options defaults file */ public function locateOptions( $fields, $defaults ) { $this->loadJSON( $fields, false ); $this->loadJSON( $defaults, true ); $this->defaults_json_path = $defaults; self::table_exists(); } public function resetToDefaults() { if ( ! file_exists( $this->defaults_json_path ) ) { return; } // List of fields to preserve $preserve_fields = [ 'enabled', 'tracking_id', 'enable_server_container', 'server_container_url', 'transport_url', 'pixel_id', 'use_server_api', 'advanced_matching_enabled', 'server_access_api_token', 'use_server_api', 'verify_meta_tag', 'gtm_id', 'gtm_just_data_layer', ]; // Load current values $this->maybeLoad(); $current_values = $this->values; // Preserve the values of specified fields $preserved_values = []; foreach ( $preserve_fields as $field ) { if ( isset( $current_values[ $field ] ) ) { $preserved_values[ $field ] = $current_values[ $field ]; } } // Load default values $content = file_get_contents( $this->defaults_json_path ); $default_values = json_decode( $content, true ); // Merge preserved values with default values $merged_values = array_merge( $default_values, $preserved_values ); // Update options with the merged values $this->updateOptions( $merged_values ); } /** * Load options fields or options defaults from specified file * * @param string $file * @param bool $is_defaults */ private function loadJSON( $file, $is_defaults ) { if ( ! file_exists( $file ) ) { return; } $content = file_get_contents( $file ); $values = json_decode( $content, true ); if ( null === $values ) { return; } if ( $is_defaults ) { $this->defaults = $values; } else { $this->options = $values; } } /** * Reading from a new table. * * @return array|null array of values or null if there is no entry */ private function pys_get_from_storage( $option_key ) { global $wpdb; if (!self::$table_exists) { return null; } $table = self::storage_table(); // Safe to query the table $row = $wpdb->get_row( $wpdb->prepare( "SELECT option_value FROM $table WHERE option_name = %s LIMIT 1", $option_key ), ARRAY_A ); if ( ! $row ) { return null; } $val = maybe_unserialize( $row['option_value'] ); return is_array( $val ) ? $val : []; } /** * Write to a new table. * * @return bool */ private function pys_set_in_storage( $option_key, $value, $migrated = 1 ) { global $wpdb; if (!self::$table_exists) { return null; } $table = self::storage_table(); $data = [ 'option_value' => maybe_serialize( $value ), 'migrated' => (int) $migrated, ]; $format = [ '%s', '%d' ]; // Checking if there is a record $exists = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM {$table} WHERE option_name = %s", $option_key ) ); if ( $exists ) { // We are updating $result = $wpdb->update( $table, $data, [ 'option_name' => $option_key ], $format, [ '%s' ] ); } else { // Insert a new one $result = $wpdb->insert( $table, array_merge( [ 'option_name' => $option_key ], $data ), array_merge( [ '%s' ], $format ) ); } return $result !== false; } /** * Add new option field * * @param string $key * @param string $field_type * @param mixed $default */ public function addOption( $key, $field_type, $default ) { $this->options[ $key ] = $field_type; $this->defaults[ $key ] = $default; } /** * Gets an option value or its default value * * @param string $key Option key * @param mixed $fallback Option fallback value if no default is set * * @return mixed The value specified for the option or a default value for the option. */ public function getOption( $key, $fallback = null ) { $this->maybeLoad(); // get option default if unset if ( ! isset( $this->values[ $key ] ) ) { $this->values[ $key ] = isset( $this->defaults[ $key ] ) ? $this->defaults[ $key ] : null; } // use fall back value if default is not set if ( null === $this->values[ $key ] && ! is_null( $fallback ) ) { $this->values[ $key ] = $fallback; } return $this->values[ $key ]; } public function setOption($key, $value){ $this->maybeLoad(); if (isset($value) ) { $this->values[ $key ] = $value; } } /** * Load values from database * * @param bool $force Force options load */ private function maybeLoad( $force = false ) { if ( ! $force && ! empty( $this->values ) ) { return; // already loaded } // 1) Let's try a new table $stored = $this->pys_get_from_storage( $this->option_key ); if ( is_array( $stored ) ) { $this->values = wp_parse_args( $stored, $this->defaults ); return; } // 2) Let's try the old way (wp_options) $legacy = get_option( $this->option_key, null ); if ( is_array( $legacy ) ) { // Migration to a new table $this->pys_set_in_storage( $this->option_key, $legacy, 1 ); $this->values = wp_parse_args( $legacy, $this->defaults ); return; } // 3) If nothing, we take defaults $this->values = $this->defaults; } public function reloadOptions() { $this->maybeLoad( true ); } /** * Sanitize and save options * * @param null|array $values Optional. If set, options values will be received from param instead of $_POST. */ public function updateOptions( $values = null ) { $this->maybeLoad(); if ( is_array( $values ) ) { $form_data = $values; } else { if ( isset( $_POST['pys'][ $this->slug ] ) && is_array( $_POST['pys'][ $this->slug ] ) ) { $form_data = $_POST['pys'][ $this->slug ]; } else { $form_data = []; } } // We only apply valid fields and sanitize them foreach ( $form_data as $key => $value ) { if ( isset( $this->options[ $key ] ) ) { $this->values[ $key ] = $this->sanitize_form_field( $key, $value ); } } // We write to a new table (primarily) $written = $this->pys_set_in_storage( $this->option_key, $this->values, 1 ); if ( ! $written ) { // Fallback: if we couldn't write to our table, we save it in wp_options update_option( $this->option_key, $this->values, false ); } } /** * Sanitize form field * * @param string $key Field key * @param array $value Field value * * @return mixed Sanitized field value */ private function sanitize_form_field( $key, $value ) { $type = $this->options[ $key ]; // look for very specific sanitization filter $filter_name = "{$this->option_key}_settings_sanitize_{$key}_field"; if ( has_filter( $filter_name ) ) { return apply_filters( $filter_name, $value ); } // look for a sanitize_FIELDTYPE_field method if ( is_callable( array( $this, 'sanitize_' . $type . '_field' ) ) ) { return $this->{'sanitize_' . $type . '_field'}( $value ); } // fallback to text return $this->sanitize_text_field( $value ); } /** * Output text input * * @param $key * @param string $placeholder * @param bool $disabled * @param bool $hidden * @param bool $empty */ public function render_text_input( $key, $placeholder = '', $disabled = false, $hidden = false, $empty = false, $type = 'standard' ) { $attr_name = "pys[$this->slug][$key]"; $attr_id = 'pys_' . $this->slug . '_' . $key; $attr_value = $empty == false ? $this->getOption( $key ) : ""; $classes = array( "input-$type" ); if ( $hidden ) { $classes[] = 'form-control-hidden'; } $classes = implode( ' ', $classes ); ?> type="text" name="" id="" value="" placeholder="" class=""> slug][$key]"; $attr_id = 'pys_' . $this->slug . '_' . $key; $attr_value = $empty == false ? $this->getOption( $key ) : ""; $classes = array( "input-$type", "passwordInput" ); if ( $hidden ) { $classes[] = 'form-control-hidden'; } $classes = implode( ' ', $classes ); ?>
type="text" name="" id="" value="" placeholder="" class="" style="display:none;" >
slug][$key][]"; $attr_id = 'pys_' . $this->slug . '_' . $key . '_' . $index; $values = (array) $this->getOption( $key ); $attr_value = isset( $values[ $index ] ) ? $values[ $index ] : null; ?> slug][$key][]"; $attr_id = 'pys_' . $this->slug . '_' . $key . '_' . $index; $values = (array) $this->getOption( $key ); $attr_value = isset( $values[ $index ] ) ? $values[ $index ] : null; ?> slug][$key][]"; $attr_id = 'pys_' . $this->slug . '_' . $key . '_' . $index; $values = (array) $this->getOption( $key ); $attr_value = isset( $values[ $index ] ) ? $values[ $index ] : null; ?> name="" id="" value="" placeholder="" class="input-standard"> slug][$key]"; $attr_id = 'pys_' . $this->slug . '_' . $key; $attr_value = $this->getOption( $key ); $classes = array( 'form-control' ); if( $hidden ) { $classes[] = 'form-control-hidden'; } $classes = implode( ' ', $classes ); ?> slug][$key]"; $attr_id = 'pys_' . $this->slug . '_' . $key; $attr_value = $this->getOption( $key ); $input_class = $label_class = ''; $classes = array( "$type-switch" ); if ( $collapse ) { $classes[] = 'collapse-control'; } if ( $disabled ) { $classes[] = 'disabled'; $attr_name = ""; $attr_value = $default; } if ( $type === 'primary' ) { $input_class = 'primary-switch-input'; $label_class = 'primary-switch-btn'; } elseif ( $type === 'secondary' ) { $input_class = 'custom-switch-input'; $label_class = 'custom-switch-btn'; } $classes = implode( ' ', $classes ); ?>
id="" class="" data-target="pys_slug ); ?>__panel"> id="" class="">
slug][$key][]"; $attr_id = 'pys_' . $this->slug . '_' . $key . "_" . $index; $attr_values = (array) $this->getOption( $key ); $value = "index_" . $index; $valueIndex = array_search( $value, $attr_values ); $classes = array( 'secondary-switch' ); $classes = implode( ' ', $classes ); ?>
id="" class="custom-switch-input ">
slug][$key]"; $attr_value = $this->getOption( $key ); $id = $key . "_" . random_int( 1, 1000000 ); ?>
>
slug][$key][]"; $attr_values = (array) $this->getOption( $key ); $isChecked = !in_array( $value, $attr_values ); $id = $key . "_" . random_int( 1, 1000000 ); ?>
" class="small-control-input" >
slug][$key][]"; $attr_values = (array) $this->getOption( $key ); $id = $key . "_" . random_int( 1, 1000000 ); $isChecked = in_array($value, $attr_values, true); ?>
>
slug][$key]"; ?>
class="custom-control-input" id="" getOption( $key ), $value ); ?> value="">
slug][$key]"; $attr_id = 'pys_' . $this->slug . '_' . $key; $attr_value = $this->getOption( $key ); ?>
type="number" name="" id="" value="" placeholder="" min="" max="" step="" >
slug][$key]"; $attr_id = 'pys_' . $this->slug . '_' . $key; $attr_value = $this->getOption( $key ); ?>
type="number" name="" id="" value="" placeholder="" min="" max="" step="" >
slug][$key]"; $attr_id = 'pys_' . $this->slug . '_' . $key; $classes = array( 'select-standard' ); if ( $visibility_target ) { $classes[] = 'controls-visibility'; } if($searchable){ $classes[] = 'pys-pysselect2'; } $classes = implode( ' ', $classes ); ?>
slug][$key][]"; $attr_id = 'pys_' . $this->slug . '_' . $key; $selected = $this->getOption( $key ) ? $this->getOption( $key ) : array(); ?> slug][$key][]"; $attr_id = 'pys_' . $this->slug . '_' . $key; $tags = $this->getOption( $key ); $tags = is_array( $tags ) ? array_filter( $tags ) : array(); $tags = array_diff($tags,$default); ?> $value ) { $new_value = $this->sanitize_text_field( $value ); if ( ! empty( $new_value ) && ! in_array( $new_value, $sanitized ) ) { $sanitized[ $key ] = $new_value; } } return $sanitized; } /** * Sanitize array field value * * @param $values * * @return array */ public function sanitize_array_textarea_field( $values ) { $values = is_array( $values ) ? $values : array(); $sanitized = array(); foreach ( $values as $key => $value ) { $new_value = $this->sanitize_textarea_field( $value ); if ( ! empty( $new_value ) && ! in_array( $new_value, $sanitized ) ) { $sanitized[ $key ] = $new_value; } } return $sanitized; } /** * Sanitize array field value with duplicates value * * @param $values * * @return array */ public function sanitize_array_v_field( $values ) { $values = is_array( $values ) ? $values : array(); $sanitized = array(); foreach ( $values as $key => $value ) { $new_value = $this->sanitize_text_field( $value ); if ( ! empty( $new_value ) ) { $sanitized[ $key ] = $new_value; } } return $sanitized; } public function render_checkbox_input_array( $key, $label, $index = 0, $disabled = false ) { $attr_name = "pys[$this->slug][$key][]"; $attr_values = (array) $this->getOption( $key ); $value = "index_" . $index; $valueIndex = array_search( $value, $attr_values ); $id = $key . "_" . rand( 1, 1000000 ); ?>
>
disabled="disabled" class="custom-switch-input">
'.$label.''; } public function convertTimeToSeconds($timeValue = 24, $type = 'hours') { switch ($type){ case 'hours': $time = $timeValue * 60 * 60; break; case 'minute': $time = $timeValue * 60; break; case 'seconds': $time = $timeValue; break; } return $time; } public function renderValueOptionsBlock($context, $useEnable = true, $usePopover = true, $useBorder = true, $title = '', $disabled = false) { if ( empty( $context ) ) { return; } if ( empty( $title ) ) { $title = 'Value parameter settings:'; } $prefixes = [ 'purchase', 'initiate_checkout', 'complete_registration' ]; if ( count( array_filter( $prefixes, function ( $prefix ) use ( $context ) { return strpos( $context, $prefix ) !== false; } ) ) > 0 ) { $priceText = 'Order\'s total'; $percentText = 'Percent of the order\'s total'; } else { $priceText = 'Product price'; $percentText = 'Percent of the product price'; } ?>
getOption( $context . '_value_enabled' ) ) || $useEnable ) : ?>
render_switcher_input( $context . '_value_enabled', true ) : renderDummySwitcher(); ?>

getOption( $context . '_value_enabled' ) ) && !$disabled) { renderCollapseTargetAttributes( $context . '_value_enabled', PYS() ); } ?>>
render_radio_input( $context . '_value_option', 'price', $priceText ) : renderDummyRadioInput($priceText); ?> render_radio_input( $context . '_value_option', 'cog', 'Price minus Cost of Goods.', true, true ): renderDummyRadioInput('Price minus Cost of Goods'); ?> render_radio_input( $context . '_value_option', 'cog', 'Price minus Cost of Goods', false ): renderDummyRadioInput('Price minus Cost of Goods'); ?>
render_radio_input( $context . '_value_option', 'global', 'Use global value' ) : renderDummyRadioInput('Use global value') ; ?> render_number_input( $context . '_value_global' ) : renderDummyNumberInput(0); ?>