self::get_page_url(),
'text' => __( 'Set AdSimple ID', 'adsimple-cookie-manager-for-wp' ),
] );
}
}
/**
* @since 2.0.6
*/
public static function save_adsimple_id_after_install() {
$field = static::get_fields( 'adsimple_id' );
if ( ! isset( $_REQUEST[ $field['name'] ] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- nonce verified in save_fields()
return;
}
// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- nonce verified in save_fields()
$id = isset( $_REQUEST[ $field['name'] ] ) ? sanitize_text_field( wp_unslash( $_REQUEST[ $field['name'] ] ) ) : '';
$result = static::update_adsimple_id( $id );
if ( is_wp_error( $result ) ) {
$error_data = $result->get_error_data( 'invalid_adsimple_id' );
AS_CM_Services_Notice::add_notice( 'status-save-adsimple-id', $result->get_error_message(), [ AS_CM_Manager::$action ], isset( $error_data['link'] ) ? $error_data['link'] : false );
return;
}
AS_CM_Services_Notice::add_notice( 'status-save-adsimple-id', __( "We successfully saved your AdSimple ID.", 'adsimple-cookie-manager-for-wp' ) );
}
/**
* @since 2.0.6
*/
public static function save_cache_options_fields() {
if ( ! AS_CM_Controllers_Options::is_configured() ) {
return;
}
$field = static::get_fields( 'cache' );
$data = $field['default'];
foreach ( array_keys( $data ) as $key ) {
$name = AS_CM_Helpers_General::prepare_name( $field['key'] . '_' . $key );
// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- nonce verified in save_fields()
if ( isset( $_REQUEST[ $name ] ) ) {
// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- nonce verified in save_fields()
$data[ $key ] = sanitize_text_field( wp_unslash( $_REQUEST[ $name ] ) );
}
}
static::update_cache_settings( $data );
AS_CM_Services_Notice::add_notice( 'status-save-cache-options', __( "We successfully saved cache options.", 'adsimple-cookie-manager-for-wp' ) );
}
/**
* @since 1.0.0
* @since 2.0.6 added hook _save_options
*/
public static function save_fields() {
global $pagenow;
if ( $pagenow != 'admin.php' ) {
return;
}
// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- page check only, no data processing
if ( ! isset( $_GET['page'] ) ) {
return;
}
// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- page check only, no data processing
if ( sanitize_text_field( wp_unslash( $_GET['page'] ) ) !== static::get_settings_page_key() ) {
return;
}
if ( ! current_user_can( 'administrator' ) ) {
AS_CM_Services_Notice::add_notice( 'status-save-adsimple-id', __( "You don't have access to make this.", 'adsimple-cookie-manager-for-wp' ) );
return;
}
do_action( AS_CM_Manager::$action . '_options_page' );
// phpcs:ignore WordPress.Security.NonceVerification.Missing -- nonce checked via do_action callbacks
if ( empty( $_POST ) ) {
return;
}
do_action( AS_CM_Manager::$action . '_save_options' );
}
/**
* init options
*/
public static function init_options_page() {
add_menu_page( __( 'Consent Manager', 'adsimple-cookie-manager-for-wp' ), __( 'Consent Manager', 'adsimple-cookie-manager-for-wp' ), 'manage_options', AS_CM_Controllers_Options::get_settings_page_key(),
[ __CLASS__, 'render_options_page_content' ], AS_CM_Helpers_General::get_full_assets_url( 'images/icon', 'png' ), 65 );
}
/**
* Ensure admin menu icon keeps original sizing and positioning.
*/
public static function print_menu_icon_styles() {
$page_key = sanitize_html_class( static::get_settings_page_key() );
?>
static::get_fields( 'adsimple_id' ) ] );
}
/**
* action on deactivate plugin
*
* @since 1.0.0
*
*/
public static function deactivate() {
delete_option( static::get_options_name() );
}
/**
* get full option name
*
* @param string $option
*
* @return string
*
* @since 1.0.0
*
*/
public static function get_full_option_name( $option ) {
return AS_CM_Helpers_General::prepare_name( $option );
}
/**
* get full name of options
*
* @return string
*
* @since 1.0.0
*
*/
public static function get_options_name() {
return static::get_full_option_name( static::OPTION_NAME );
}
/**
* get embed code
*
* @return string
*
* @since 1.0.0
*
*/
public static function get_option_embed_code() {
$embed_code = static::get_option( 'embed_code' );
if ( ! is_string( $embed_code ) ) {
return '';
}
return $embed_code;
}
/**
* get option embed url
*
* @return array
*
* @since 2.0.2
* @since 2.0.5 changed to array
*/
public static function get_option_embed_url() {
$count = preg_match_all( '/src=(["\'])(.*?)\1/', static::get_option_embed_code(), $match );
return $count === false ? [] : ( isset( $match[2] ) ? $match[2] : [] );
}
/**
* get embed code
*
* @return string
*
* @since 1.0.0
*
*/
public static function get_option_adsimple_id() {
return (string) static::get_option( 'adsimple_id' );
}
/**
* @return boolean
*
* @since 2.0.6
*/
public static function is_configured() {
$key = static::get_option_adsimple_id();
$code = static::get_option_embed_code();
return ! empty( $key ) && ! empty( $code );
}
/**
* get dashboard slug
*
* @param string $name
*
* @return string
*
* @since 1.0.0
*
*/
public static function get_slug( $name = '' ) {
return AS_CM_Helpers_General::prepare_name( static::ADMIN_PAGE . ( ! empty( $name ) ? '_' . $name : '' ), false );
}
/**
* get dashboard settings page key
*
* @param string $name
*
* @return string
*
* @since 2.0.14
*
*/
public static function get_settings_page_key( $name = '' ) {
return 'as_cm_settings' . ( $name !== '' ? ('-' . $name) : '');
}
/**
* get page url
*
* @param string $name
* @param array $args
*
* @return string
*
* @since 1.0.0
*
*/
public static function get_page_url( $name = '', $args = [] ) {
return AS_CM_Helpers_Transfer::get_link_with_params( array_merge( [
'page' => static::get_settings_page_key( $name ),
], $args ), admin_url( 'admin.php' ) );
}
/**
* get option value from DB
*
* @param string $name
*
* @return string|mixed
*
* @since 1.0.0
*
*/
public static function get_option( $name = '' ) {
static::load_options_values();
if ( empty( $name ) ) {
return static::$values;
}
return apply_filters( AS_CM_Manager::$action . '_option_value', isset( static::$values[ $name ] ) ? static::$values[ $name ] : static::get_default_value( $name ), $name );
}
/**
* load all options values for OPTION_NAME from DB
*
* @param boolean $reset
*
* @since 1.0.0
*
*/
protected static function load_options_values( $reset = false ) {
if ( static::$values === null || $reset === true ) {
static::$values = get_option( static::get_options_name(), [] );
}
}
/**
* get default value of field
*
* @param string $name
*
* @return mixed|null
*/
public static function get_default_value( $name ) {
foreach ( static::get_fields() as $field ) {
if ( $field['key'] == $name && isset( $field['default'] ) ) {
return $field['default'];
}
}
return null;
}
/**
* get fields
*
* @param string $key
*
* @return array|null
*
* @since 1.0.0
*
*/
public static function get_fields( $key = '' ) {
$fields = [
[
'key' => 'adsimple_id',
'label' => __( 'AdSimple ID', 'adsimple-cookie-manager-for-wp' ),
'attr' => [
'type' => 'text',
],
'default' => '',
],
[
'key' => 'cache',
'label' => '',
'attr' => [],
'default' => [ 'available' => false, 'period_type' => 'HOUR_IN_SECONDS', 'period_value' => 24, 'timestamp' => 0 ],
],
[
'key' => 'local_banner',
'label' => '',
'attr' => [],
'default' => static::get_local_banner_defaults(),
],
[
'key' => 'embed_code',
'label' => __( 'Embed code', 'adsimple-cookie-manager-for-wp' ),
'attr' => [
'type' => 'textarea',
],
'default' => '',
],
];
foreach ( $fields as $index => $field ) {
$field['name'] = AS_CM_Helpers_General::prepare_name( $field['key'] );
$fields[ $index ] = $field;
if ( ! empty( $key ) && $field['key'] == $key ) {
return $field;
}
}
return empty( $key ) ? $fields : null;
}
private function __clone() {}
/**
* update options in DB
*
* @param array $options
*
* @since 1.0.0
*
*/
public static function update_options( $options ) {
self::$values = $options;
update_option( static::get_options_name(), $options );
}
/**
* @return array
*
* @since 1.0.0
*/
public static function get_available_period_types() {
return [
'DAY_IN_SECONDS' => __( 'days', 'adsimple-cookie-manager-for-wp' ),
'HOUR_IN_SECONDS' => __( 'hours', 'adsimple-cookie-manager-for-wp' ),
'MINUTE_IN_SECONDS' => __( 'minutes', 'adsimple-cookie-manager-for-wp' ),
];
}
/**
* Update AdSimple ID and embed code
*
* @param string $raw_id
*
* @return true|WP_Error
*/
public static function update_adsimple_id( $raw_id ) {
$field = static::get_fields( 'adsimple_id' );
$id = AS_CM_Helpers_General::esc_sql( trim( (string) $raw_id ) );
if ( $id === '' ) {
return new \WP_Error( 'as_cm_empty_id', __( 'AdSimple ID can not be empty.', 'adsimple-cookie-manager-for-wp' ) );
}
$answer = AS_CM_Services_REST::get_embed_code( $id, get_bloginfo( 'url' ) );
if ( is_wp_error( $answer ) ) {
return $answer;
}
$options = (array) static::get_option();
$options[ $field['key'] ] = $id;
$options['embed_code'] = $answer['result'];
static::update_options( $options );
return true;
}
/**
* Update cache settings
*
* @param array $data
*/
public static function update_cache_settings( $data ) {
$field = static::get_fields( 'cache' );
$defaults = isset( $field['default'] ) ? $field['default'] : [ 'available' => false, 'period_type' => 'HOUR_IN_SECONDS', 'period_value' => 24, 'timestamp' => 0 ];
$sanitized = array_merge( $defaults, is_array( $data ) ? $data : [] );
$sanitized['available'] = ! empty( $sanitized['available'] );
$sanitized['period_type'] = isset( $sanitized['period_type'] ) && array_key_exists( $sanitized['period_type'], static::get_available_period_types() ) ? $sanitized['period_type'] : $defaults['period_type'];
$sanitized['period_value'] = isset( $sanitized['period_value'] ) ? absint( $sanitized['period_value'] ) : $defaults['period_value'];
$sanitized['timestamp'] = 0;
$options = (array) static::get_option();
$options['cache'] = $sanitized;
static::update_options( $options );
AS_CM_Services_Cache_Manager::delete_cache();
do_action( AS_CM_Manager::$action . '_after_save_cache_options' );
}
/**
* Sanitize local banner input array
*
* @param array $input
*
* @return array
*/
public static function sanitize_local_banner_input( $input ) {
$defaults = static::get_local_banner_defaults();
$data = array_replace_recursive( $defaults, is_array( $input ) ? $input : [] );
$allowed_positions = [ 'bottom', 'bottom-left', 'top', 'bottom-right', 'top-push', 'popup' ];
$allowed_layouts = [ 'classic', 'block', 'wire' ];
$allowed_themes = array_map(
function ( $index ) {
return 'theme' . $index;
},
range( 1, 16 )
);
$hex_pattern = '/^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/';
$sanitize_color = static function ( $value, $fallback ) use ( $hex_pattern ) {
$normalize = static function ( $color ) {
$color = strtoupper( $color );
$length = strlen( $color );
if ( 4 === $length ) { // #RGB
return sprintf(
'#%1$s%1$s%2$s%2$s%3$s%3$s',
$color[1],
$color[2],
$color[3]
);
}
if ( 5 === $length ) { // #RGBA -> drop alpha
return sprintf(
'#%1$s%1$s%2$s%2$s%3$s%3$s',
$color[1],
$color[2],
$color[3]
);
}
if ( 9 === $length ) { // #RRGGBBAA -> drop alpha
return substr( $color, 0, 7 );
}
return $color;
};
$color = is_string( $value ) ? trim( $value ) : '';
if ( preg_match( $hex_pattern, $color ) ) {
return $normalize( $color );
}
return $normalize( $fallback );
};
$data['enabled'] = ! empty( $data['enabled'] );
$data['position'] = in_array( $data['position'], $allowed_positions, true ) ? $data['position'] : $defaults['position'];
$data['layout'] = in_array( $data['layout'], $allowed_layouts, true ) ? $data['layout'] : $defaults['layout'];
$data['theme'] = in_array( $data['theme'], $allowed_themes, true ) ? $data['theme'] : $defaults['theme'];
$data['consent_ttl_days'] = isset( $data['consent_ttl_days'] ) ? absint( $data['consent_ttl_days'] ) : $defaults['consent_ttl_days'];
if ( $data['consent_ttl_days'] < 1 ) {
$data['consent_ttl_days'] = $defaults['consent_ttl_days'];
}
if ( $data['consent_ttl_days'] > 730 ) {
$data['consent_ttl_days'] = 730;
}
$sanitized_texts = [];
foreach ( $defaults['texts'] as $key => $default ) {
$value = isset( $data['texts'][ $key ] ) ? $data['texts'][ $key ] : '';
$sanitized_texts[ $key ] = ( $key === 'message' || $key === 'welcome_message' ) && function_exists( 'sanitize_textarea_field' )
? sanitize_textarea_field( $value )
: sanitize_text_field( $value );
}
$sanitized_links = [];
foreach ( $defaults['links'] as $key => $default ) {
$value = isset( $data['links'][ $key ] ) ? $data['links'][ $key ] : '';
if ( strpos( $key, '_page_id' ) !== false ) {
$sanitized_links[ $key ] = absint( $value );
continue;
}
if ( in_array( $key, [ 'privacy', 'imprint' ], true ) ) {
$sanitized_links[ $key ] = esc_url_raw( $value );
} else {
$sanitized_links[ $key ] = sanitize_text_field( $value );
}
}
$data['texts'] = AS_CM_Helpers_General::esc_sql( $sanitized_texts );
$data['links'] = AS_CM_Helpers_General::esc_sql( $sanitized_links );
$colors = [];
foreach ( $defaults['custom_colors'] as $key => $default_color ) {
$incoming = isset( $data['custom_colors'][ $key ] ) ? $data['custom_colors'][ $key ] : $default_color;
$colors[ $key ] = $sanitize_color( $incoming, $default_color );
}
$data['custom_colors'] = $colors;
$popup_effects = isset( $data['popup_effects'] ) && is_array( $data['popup_effects'] ) ? array_merge( $defaults['popup_effects'], $data['popup_effects'] ) : $defaults['popup_effects'];
$popup_effects['popup_shadow'] = isset( $popup_effects['popup_shadow'] ) && '0' === (string) $popup_effects['popup_shadow'] ? '0' : '1';
$popup_effects['popup_overlay_color'] = $sanitize_color( $popup_effects['popup_overlay_color'], $defaults['popup_effects']['popup_overlay_color'] );
$popup_effects['popup_overlay_opacity'] = isset( $popup_effects['popup_overlay_opacity'] ) ? absint( $popup_effects['popup_overlay_opacity'] ) : $defaults['popup_effects']['popup_overlay_opacity'];
if ( $popup_effects['popup_overlay_opacity'] > 100 ) {
$popup_effects['popup_overlay_opacity'] = 100;
}
$data['popup_effects'] = $popup_effects;
$settings_button = isset( $data['settings_button'] ) && is_array( $data['settings_button'] ) ? array_merge( $defaults['settings_button'], $data['settings_button'] ) : $defaults['settings_button'];
$settings_button['label'] = sanitize_text_field( isset( $settings_button['label'] ) ? $settings_button['label'] : $defaults['settings_button']['label'] );
$settings_button['iconColor'] = $sanitize_color( $settings_button['iconColor'], $defaults['settings_button']['iconColor'] );
$settings_button['iconBackground'] = $sanitize_color( $settings_button['iconBackground'], $defaults['settings_button']['iconBackground'] );
$settings_button['iconDesktopShow'] = isset( $settings_button['iconDesktopShow'] ) && '0' === (string) $settings_button['iconDesktopShow'] ? '0' : '1';
$settings_button['offsetSettingsTabPosition'] = isset( $settings_button['offsetSettingsTabPosition'] ) ? absint( $settings_button['offsetSettingsTabPosition'] ) : $defaults['settings_button']['offsetSettingsTabPosition'];
if ( $settings_button['offsetSettingsTabPosition'] > 90 ) {
$settings_button['offsetSettingsTabPosition'] = 90;
}
$data['settings_button'] = $settings_button;
$available_categories = isset( $data['cookie_categories']['availableCategories'] ) && is_array( $data['cookie_categories']['availableCategories'] )
? array_values( array_filter( array_unique( array_map( 'sanitize_key', $data['cookie_categories']['availableCategories'] ) ) ) )
: $defaults['cookie_categories']['availableCategories'];
$available_categories = array_values( array_diff( $available_categories, [ 'socialmedia' ] ) );
if ( empty( $available_categories ) ) {
$available_categories = $defaults['cookie_categories']['availableCategories'];
}
$states_input = isset( $data['cookie_categories']['categoryStates'] ) && is_array( $data['cookie_categories']['categoryStates'] ) ? $data['cookie_categories']['categoryStates'] : [];
$category_states = [];
foreach ( $available_categories as $category ) {
$category_state = isset( $states_input[ $category ] ) && is_array( $states_input[ $category ] ) ? $states_input[ $category ] : [];
$category_states[ $category ] = [
'activated' => ! empty( $category_state['activated'] ),
'locked' => ! empty( $category_state['locked'] ),
'visible' => array_key_exists( 'visible', $category_state ) ? (bool) $category_state['visible'] : true,
];
}
$data['cookie_categories'] = [
'availableCategories' => $available_categories,
'categoryStates' => $category_states,
];
$exclude_paths = isset( $data['visibility']['exclude_paths'] ) ? $data['visibility']['exclude_paths'] : '';
$data['visibility']['exclude_paths'] = function_exists( 'sanitize_textarea_field' )
? sanitize_textarea_field( $exclude_paths )
: sanitize_text_field( $exclude_paths );
$gcm_input = isset( $data['features']['google_consent_mode'] ) && is_array( $data['features']['google_consent_mode'] ) ? $data['features']['google_consent_mode'] : [];
$allowed_consent = [ 'denied', 'granted' ];
$gcm_sanitized = [
'enabled' => ! empty( $gcm_input['enabled'] ),
'default_consent' => in_array( isset( $gcm_input['default_consent'] ) ? $gcm_input['default_consent'] : '', $allowed_consent, true )
? $gcm_input['default_consent']
: $defaults['features']['google_consent_mode']['default_consent'],
];
// GPC: per W3C spec the signal is a single binary opt-out — enabling it implies
// auto-rejecting non-essential cookies and hiding the banner. Sub-flags are
// kept in the schema for forward-compat but always stored as true.
$gpc_input = isset( $data['features']['gpc_support'] ) && is_array( $data['features']['gpc_support'] ) ? $data['features']['gpc_support'] : [];
$gpc_sanitized = [
'enabled' => ! empty( $gpc_input['enabled'] ),
'auto_reject' => true,
'hide_banner' => true,
];
$data['features'] = [
'google_consent_mode' => $gcm_sanitized,
'gpc_support' => $gpc_sanitized,
];
return array_replace_recursive( $defaults, $data );
}
/**
* Update local banner settings
*
* @param array $input
*/
public static function update_local_banner( $input ) {
$options = (array) static::get_option();
$options['local_banner'] = static::sanitize_local_banner_input( $input );
static::update_options( $options );
}
/**
* Register REST routes for admin SPA
*/
public static function register_rest_routes() {
register_rest_route( 'as-cm/v1', '/options', [
[
'methods' => \WP_REST_Server::READABLE,
'callback' => [ __CLASS__, 'rest_get_options' ],
'permission_callback' => function () {
return current_user_can( 'manage_options' );
},
],
[
'methods' => \WP_REST_Server::EDITABLE,
'callback' => [ __CLASS__, 'rest_update_options' ],
'permission_callback' => function () {
return current_user_can( 'manage_options' );
},
],
] );
register_rest_route( 'as-cm/v1', '/disconnect', [
[
'methods' => \WP_REST_Server::EDITABLE,
'callback' => [ __CLASS__, 'rest_disconnect' ],
'permission_callback' => function () {
return current_user_can( 'manage_options' );
},
],
] );
}
/**
* REST callback: return current options
*
* @return WP_REST_Response
*/
public static function rest_get_options() {
return rest_ensure_response( static::prepare_rest_options_response() );
}
/**
* REST callback: persist options
*
* @param \WP_REST_Request $request
*
* @return WP_REST_Response|WP_Error
*/
public static function rest_update_options( \WP_REST_Request $request ) {
$params = $request->get_json_params();
// Only update adsimple_id if it's provided AND not empty (Local Setup can work without it)
if ( isset( $params['adsimple_id'] ) && trim( $params['adsimple_id'] ) !== '' ) {
$result = static::update_adsimple_id( $params['adsimple_id'] );
if ( is_wp_error( $result ) ) {
return $result;
}
}
if ( isset( $params['cache'] ) && static::is_configured() ) {
static::update_cache_settings( $params['cache'] );
}
if ( isset( $params['local_banner'] ) ) {
static::update_local_banner( $params['local_banner'] );
}
return rest_ensure_response( static::prepare_rest_options_response() );
}
/**
* REST callback: disconnect AdSimple ID and remove embed code
*
* @return WP_REST_Response
*/
public static function rest_disconnect() {
$field = static::get_fields( 'adsimple_id' );
$options = (array) static::get_option();
$options[ $field['key'] ] = '';
$options['embed_code'] = '';
static::update_options( $options );
AS_CM_Services_Cache_Manager::delete_cache();
return rest_ensure_response( static::prepare_rest_options_response() );
}
/**
* Prepare response payload for REST requests
*
* @return array
*/
protected static function prepare_rest_options_response() {
$local_banner = static::get_option_local_banner();
return [
'options' => [
'adsimple_id' => static::get_option_adsimple_id(),
'cache' => static::get_option( 'cache' ),
'local_banner' => $local_banner,
'local_banner_css_variables' => static::resolve_local_banner_css_variables( $local_banner ),
],
'is_configured' => static::is_configured(),
'cache_clear_url' => AS_CM_Services_Cache_Manager::get_clear_cache_url(),
'pages' => static::get_available_pages(),
];
}
protected static function ensure_svg_painter_fallback() {
if ( self::$svg_painter_stub_registered ) {
return;
}
self::$svg_painter_stub_registered = true;
add_action( 'admin_head', [ __CLASS__, 'print_svg_painter_stub' ], - 999 );
}
public static function print_svg_painter_stub() {
echo "\n";
}
/**
* Add type="module" attribute to the admin-app script tag.
*
* Uses the script_loader_tag filter which is reliable across all
* WordPress versions (4.1+), unlike wp_script_add_data('type','module')
* which only works in WordPress 6.3+.
*
* @param string $tag Script HTML tag.
* @param string $handle Script handle.
* @return string
*/
public static function add_module_type_attribute( $tag, $handle ) {
$app_handle = AS_CM_Helpers_General::prepare_name( 'admin-app', false );
if ( $handle !== $app_handle ) {
return $tag;
}
if ( strpos( $tag, 'type="module"' ) !== false ) {
return $tag;
}
$tag = str_replace( ' src=', ' type="module" src=', $tag );
return $tag;
}
/**
* Enqueue admin SPA assets
*
* Uses the $hook parameter (page hook suffix) for the page check instead of
* get_current_screen()->id because some plugins (WPML, dark-mode, snippet
* managers) can modify or delay the WP_Screen object, causing the screen ID
* check to fail silently and leaving the admin app un-loaded.
*/
public static function enqueue_admin_app( $hook ) {
$settings_key = static::get_settings_page_key();
$allowed_hooks = [
$settings_key,
'toplevel_page_' . $settings_key,
];
if ( ! in_array( $hook, $allowed_hooks, true ) ) {
return;
}
$entry = static::get_admin_manifest_entry();
if ( empty( $entry ) || empty( $entry['file'] ) ) {
return;
}
$handle = AS_CM_Helpers_General::prepare_name( 'admin-app', false );
$base_url = trailingslashit( AS_CM_Manager::$url . '/assets/admin' );
$base_path = trailingslashit( AS_CM_Manager::$path . 'assets/admin' );
$script_url = $base_url . $entry['file'];
self::ensure_svg_painter_fallback();
if ( wp_script_is( 'svg-painter', 'registered' ) ) {
wp_add_inline_script( 'svg-painter', '(function(){if(typeof window.svgPainter==="undefined"){window.svgPainter={init:function(){}};}})();', 'before' );
}
wp_enqueue_script( $handle, $script_url, [], AS_CM_Manager::$version, true );
add_filter( 'script_loader_tag', [ __CLASS__, 'add_module_type_attribute' ], 10, 2 );
// Load CSS from entry chunk if available
if ( ! empty( $entry['css'] ) && is_array( $entry['css'] ) ) {
foreach ( $entry['css'] as $index => $css_file ) {
$style_handle = $handle . '-style-' . $index;
wp_enqueue_style( $style_handle, $base_url . $css_file, [], AS_CM_Manager::$version );
}
} else {
// Fallback: Load CSS from manifest's style.css entry or directly
$manifest = static::get_admin_manifest();
$css_file = null;
if ( isset( $manifest['style.css']['file'] ) ) {
$css_file = $manifest['style.css']['file'];
} elseif ( file_exists( $base_path . 'css/main.css' ) ) {
$css_file = 'css/main.css';
}
if ( $css_file ) {
wp_enqueue_style( $handle . '-style', $base_url . $css_file, [], AS_CM_Manager::$version );
}
}
$preload = [
'rootId' => 'as-cm-admin-app',
'restUrl' => esc_url_raw( rest_url( 'as-cm/v1/options' ) ),
'scanRestBase' => esc_url_raw( rest_url( 'adsimple-consent/v1' ) ),
'siteUrl' => home_url( '/' ),
'pluginUrl' => esc_url_raw( AS_CM_Manager::$url ),
'pluginVersion' => AS_CM_Manager::$version,
'nonce' => wp_create_nonce( 'wp_rest' ),
'initialState' => static::prepare_rest_options_response(),
'cssVariables' => static::resolve_local_banner_css_variables( static::get_option_local_banner() ),
'pages' => static::get_available_pages(),
'i18n' => [
// Global
'app_title' => __( 'Consent Manager', 'adsimple-cookie-manager-for-wp' ),
'app_subtitle' => __( 'Configure your cookie consent banner', 'adsimple-cookie-manager-for-wp' ),
'loading' => __( 'Loading...', 'adsimple-cookie-manager-for-wp' ),
'loading_error' => __( 'Unable to load settings. Please refresh the page.', 'adsimple-cookie-manager-for-wp' ),
'no_options' => __( 'No configuration found.', 'adsimple-cookie-manager-for-wp' ),
'save' => __( 'Save', 'adsimple-cookie-manager-for-wp' ),
'save_success' => __( 'Settings saved successfully.', 'adsimple-cookie-manager-for-wp' ),
'save_error' => __( 'There was an error while saving. Please try again.', 'adsimple-cookie-manager-for-wp' ),
'button_save' => __( 'Save changes', 'adsimple-cookie-manager-for-wp' ),
'cancel' => __( 'Cancel', 'adsimple-cookie-manager-for-wp' ),
'close' => __( 'Close', 'adsimple-cookie-manager-for-wp' ),
'enabled' => __( 'Enabled', 'adsimple-cookie-manager-for-wp' ),
'disabled' => __( 'Disabled', 'adsimple-cookie-manager-for-wp' ),
'unsaved' => __( 'Unsaved', 'adsimple-cookie-manager-for-wp' ),
'saved' => __( 'Saved', 'adsimple-cookie-manager-for-wp' ),
'change' => __( 'Change', 'adsimple-cookie-manager-for-wp' ),
'period_days' => __( 'days', 'adsimple-cookie-manager-for-wp' ),
'period_hours' => __( 'hours', 'adsimple-cookie-manager-for-wp' ),
'period_minutes' => __( 'minutes', 'adsimple-cookie-manager-for-wp' ),
// Connection / Connected view
'connection_active' => __( 'AdSimple connected', 'adsimple-cookie-manager-for-wp' ),
'connection_inactive' => __( 'AdSimple not connected', 'adsimple-cookie-manager-for-wp' ),
'connection_hint' => __( 'Connect an AdSimple ID to load the banner from AdSimple servers', 'adsimple-cookie-manager-for-wp' ),
'connection_active_info' => __( 'The AdSimple banner is active. The local banner below serves as a fallback if the AdSimple server is unreachable.', 'adsimple-cookie-manager-for-wp' ),
'connection_update' => __( 'Update', 'adsimple-cookie-manager-for-wp' ),
'connection_connect' => __( 'Connect', 'adsimple-cookie-manager-for-wp' ),
'connected_title' => __( 'Connected with AdSimple', 'adsimple-cookie-manager-for-wp' ),
'connected_description' => __( 'Your cookie consent banner is managed by AdSimple. The banner is loaded from the AdSimple servers.', 'adsimple-cookie-manager-for-wp' ),
'connected_local_setup' => __( 'Configure local fallback banner', 'adsimple-cookie-manager-for-wp' ),
'tab_connection' => __( 'Connection', 'adsimple-cookie-manager-for-wp' ),
'label_adsimple_id' => __( 'AdSimple ID', 'adsimple-cookie-manager-for-wp' ),
'helper_adsimple_id' => __( 'You can find your AdSimple ID in the AdSimple dashboard.', 'adsimple-cookie-manager-for-wp' ),
'sidebar_status' => __( 'Status', 'adsimple-cookie-manager-for-wp' ),
'status_connected' => __( 'Connected', 'adsimple-cookie-manager-for-wp' ),
'plugin_version' => __( 'Plugin version', 'adsimple-cookie-manager-for-wp' ),
// Cache
'tab_cache' => __( 'Local Cache', 'adsimple-cookie-manager-for-wp' ),
'cache_enable' => __( 'Save a local copy of the script', 'adsimple-cookie-manager-for-wp' ),
'cache_hint' => __( 'Caching stores a copy of the banner script locally for faster delivery. Clear the cache after changing design or text in the AdSimple dashboard.', 'adsimple-cookie-manager-for-wp' ),
'cache_clear_every' => __( 'Clear every', 'adsimple-cookie-manager-for-wp' ),
'cache_clear' => __( 'Clear cache now', 'adsimple-cookie-manager-for-wp' ),
// Information
'info_heading' => __( 'Information', 'adsimple-cookie-manager-for-wp' ),
'cookie_overview' => __( 'Cookie Overview', 'adsimple-cookie-manager-for-wp' ),
'shortcode_info' => __( 'Use the shortcode', 'adsimple-cookie-manager-for-wp' ),
'shortcode_info_2' => __( 'to show an ordered list of all cookies on a subpage of your website.', 'adsimple-cookie-manager-for-wp' ),
'wp_head_info' => __( 'The popup is embedded using the', 'adsimple-cookie-manager-for-wp' ),
'wp_head_info_2' => __( 'hook. Your theme should support this hook.', 'adsimple-cookie-manager-for-wp' ),
'publish_view_site' => __( 'View site', 'adsimple-cookie-manager-for-wp' ),
// Disconnect
'disconnect_link' => __( 'Disconnect from AdSimple', 'adsimple-cookie-manager-for-wp' ),
'disconnect_title' => __( 'Disconnect AdSimple?', 'adsimple-cookie-manager-for-wp' ),
'disconnect_warning' => __( 'This will remove the AdSimple ID and the embedded banner script. Your site will no longer show the AdSimple consent banner. If you have configured a local fallback banner, it will be used instead.', 'adsimple-cookie-manager-for-wp' ),
'disconnect_confirm' => __( 'Yes, disconnect', 'adsimple-cookie-manager-for-wp' ),
// Sidebar
'sidebar_help' => __( 'Need Help?', 'adsimple-cookie-manager-for-wp' ),
'sidebar_help_description' => __( 'Visit our support page for help and setup guides.', 'adsimple-cookie-manager-for-wp' ),
'sidebar_docs' => __( 'Support', 'adsimple-cookie-manager-for-wp' ),
// Publish success
'publish_success_title' => __( 'Banner Published!', 'adsimple-cookie-manager-for-wp' ),
'publish_success_message' => __( 'Your cookie consent banner is now live on your website.', 'adsimple-cookie-manager-for-wp' ),
// Decision screen
'decision_branding' => __( 'AdSimple Compliance', 'adsimple-cookie-manager-for-wp' ),
'decision_title' => __( 'Welcome to Consent Manager', 'adsimple-cookie-manager-for-wp' ),
'decision_subtitle' => __( 'Choose how you want to set up your cookie consent banner. You can always change this later.', 'adsimple-cookie-manager-for-wp' ),
'decision_connect_title' => __( 'Connect with AdSimple', 'adsimple-cookie-manager-for-wp' ),
'decision_connect_description' => __( 'Link your existing AdSimple account for automatic updates, remote scanning, and premium features.', 'adsimple-cookie-manager-for-wp' ),
'decision_connect_feature_1' => __( 'Automatic legal text updates', 'adsimple-cookie-manager-for-wp' ),
'decision_connect_feature_2' => __( 'Remote cookie scanning', 'adsimple-cookie-manager-for-wp' ),
'decision_connect_feature_3' => __( 'Google Consent Mode v2', 'adsimple-cookie-manager-for-wp' ),
'decision_id_label' => __( 'AdSimple ID', 'adsimple-cookie-manager-for-wp' ),
'decision_id_hint' => __( 'Find your ID in the AdSimple dashboard.', 'adsimple-cookie-manager-for-wp' ),
'decision_connect_button' => __( 'Connect & Continue', 'adsimple-cookie-manager-for-wp' ),
'decision_local_title' => __( 'Local Setup', 'adsimple-cookie-manager-for-wp' ),
'decision_local_description' => __( 'Configure your consent banner manually. Perfect for getting started or testing.', 'adsimple-cookie-manager-for-wp' ),
'decision_local_feature_1' => __( 'Full design customization', 'adsimple-cookie-manager-for-wp' ),
'decision_local_feature_2' => __( 'Browser-based quick scan', 'adsimple-cookie-manager-for-wp' ),
'decision_local_feature_3' => __( 'No account required', 'adsimple-cookie-manager-for-wp' ),
'decision_local_info' => __( 'You can connect your AdSimple account at any time later to unlock premium features.', 'adsimple-cookie-manager-for-wp' ),
'decision_local_button' => __( 'Start Local Setup', 'adsimple-cookie-manager-for-wp' ),
'decision_footer' => __( 'Both options provide GDPR-compliant cookie consent management. Premium features require an AdSimple subscription.', 'adsimple-cookie-manager-for-wp' ),
// Wizard steps
'step_design' => __( 'Design', 'adsimple-cookie-manager-for-wp' ),
'step_texts' => __( 'Texts', 'adsimple-cookie-manager-for-wp' ),
'step_cookies' => __( 'Cookies', 'adsimple-cookie-manager-for-wp' ),
'step_settings' => __( 'Settings', 'adsimple-cookie-manager-for-wp' ),
'step_features' => __( 'Features', 'adsimple-cookie-manager-for-wp' ),
'step_review' => __( 'Review', 'adsimple-cookie-manager-for-wp' ),
// Design tab
'design_position_title' => __( 'Position & Layout', 'adsimple-cookie-manager-for-wp' ),
'design_position_description' => __( 'Choose where and how the banner appears on your website.', 'adsimple-cookie-manager-for-wp' ),
'design_position' => __( 'Banner Position', 'adsimple-cookie-manager-for-wp' ),
'design_layout' => __( 'Layout Style', 'adsimple-cookie-manager-for-wp' ),
'design_theme_title' => __( 'Color Theme', 'adsimple-cookie-manager-for-wp' ),
'design_theme_description' => __( 'Select a pre-defined color scheme or customize your own.', 'adsimple-cookie-manager-for-wp' ),
'design_custom_active' => __( 'Custom colors active', 'adsimple-cookie-manager-for-wp' ),
'design_preset_active' => __( 'Using preset colors', 'adsimple-cookie-manager-for-wp' ),
'design_custom_colors' => __( 'Custom Colors', 'adsimple-cookie-manager-for-wp' ),
'design_custom_colors_description' => __( 'Override the theme with your own colors', 'adsimple-cookie-manager-for-wp' ),
'design_effects_title' => __( 'Popup Effects', 'adsimple-cookie-manager-for-wp' ),
'design_effects_description' => __( 'Customize the visual effects of the consent popup.', 'adsimple-cookie-manager-for-wp' ),
'design_shadow' => __( 'Drop shadow', 'adsimple-cookie-manager-for-wp' ),
'design_shadow_description' => __( 'Add a shadow effect to the popup', 'adsimple-cookie-manager-for-wp' ),
'design_overlay_color' => __( 'Overlay color', 'adsimple-cookie-manager-for-wp' ),
'design_overlay_opacity' => __( 'Overlay opacity', 'adsimple-cookie-manager-for-wp' ),
'design_button_title' => __( 'Settings Button', 'adsimple-cookie-manager-for-wp' ),
'design_button_description' => __( 'Configure the floating settings button that allows users to reopen consent settings.', 'adsimple-cookie-manager-for-wp' ),
'design_button_label' => __( 'Button label', 'adsimple-cookie-manager-for-wp' ),
'design_button_desktop' => __( 'Show on desktop', 'adsimple-cookie-manager-for-wp' ),
'design_button_desktop_description' => __( 'Display the settings button on desktop devices', 'adsimple-cookie-manager-for-wp' ),
'design_button_icon_color' => __( 'Icon color', 'adsimple-cookie-manager-for-wp' ),
'design_button_icon_bg' => __( 'Icon background', 'adsimple-cookie-manager-for-wp' ),
'design_button_offset' => __( 'Position offset (%)', 'adsimple-cookie-manager-for-wp' ),
// Texts tab
'texts_banner_title' => __( 'Banner Texts', 'adsimple-cookie-manager-for-wp' ),
'texts_banner_description' => __( 'Customize the text content of your cookie consent banner.', 'adsimple-cookie-manager-for-wp' ),
'texts_links_title' => __( 'Legal Links', 'adsimple-cookie-manager-for-wp' ),
'texts_links_description' => __( 'Configure links to your privacy policy and legal notice pages.', 'adsimple-cookie-manager-for-wp' ),
'texts_privacy_title' => __( 'Privacy Policy', 'adsimple-cookie-manager-for-wp' ),
'texts_select_page' => __( 'Select from WordPress pages', 'adsimple-cookie-manager-for-wp' ),
'texts_manual_url' => __( 'Enter URL manually', 'adsimple-cookie-manager-for-wp' ),
'texts_imprint_title' => __( 'Legal Notice / Imprint', 'adsimple-cookie-manager-for-wp' ),
// Cookies tab
'cookies_categories_title' => __( 'Cookie Categories', 'adsimple-cookie-manager-for-wp' ),
'cookies_categories_description' => __( 'Configure how each cookie category behaves in the consent banner.', 'adsimple-cookie-manager-for-wp' ),
'cookies_required' => __( 'Required', 'adsimple-cookie-manager-for-wp' ),
'scan_results_pending' => __( 'Complete a scan to see results here.', 'adsimple-cookie-manager-for-wp' ),
// Position options
'position_popup' => __( 'Popup (center)', 'adsimple-cookie-manager-for-wp' ),
'position_bottom' => __( 'Bottom bar', 'adsimple-cookie-manager-for-wp' ),
'position_bottom_left' => __( 'Bottom left', 'adsimple-cookie-manager-for-wp' ),
'position_bottom_right' => __( 'Bottom right', 'adsimple-cookie-manager-for-wp' ),
'position_top' => __( 'Top bar', 'adsimple-cookie-manager-for-wp' ),
'position_top_push' => __( 'Top fixed', 'adsimple-cookie-manager-for-wp' ),
// Layout options
'layout_classic' => __( 'Classic', 'adsimple-cookie-manager-for-wp' ),
'layout_block' => __( 'Block', 'adsimple-cookie-manager-for-wp' ),
'layout_wire' => __( 'Wireframe', 'adsimple-cookie-manager-for-wp' ),
// Banner text field labels
'text_field_welcome_title' => __( 'Banner title', 'adsimple-cookie-manager-for-wp' ),
'text_field_welcome_message' => __( 'Banner text', 'adsimple-cookie-manager-for-wp' ),
'text_field_welcome_message_helper' => __( 'Supported placeholders: {cookie-einstellungen}…{/cookie-einstellungen} or {cookie-settings}…{/cookie-settings}, {erforderliche-cookies}…{/erforderliche-cookies} or {necessary-cookies}…{/necessary-cookies}', 'adsimple-cookie-manager-for-wp' ),
'text_field_allow' => __( 'Button "Allow all"', 'adsimple-cookie-manager-for-wp' ),
'text_field_necessary_only' => __( 'Button "Necessary only"', 'adsimple-cookie-manager-for-wp' ),
'text_field_policy' => __( 'Button "Privacy settings"', 'adsimple-cookie-manager-for-wp' ),
'text_field_save_settings' => __( 'Button "Save settings"', 'adsimple-cookie-manager-for-wp' ),
'text_field_settings_title' => __( 'Settings title', 'adsimple-cookie-manager-for-wp' ),
'text_field_settings_text' => __( 'Settings text', 'adsimple-cookie-manager-for-wp' ),
'text_field_close' => __( 'Button "Close"', 'adsimple-cookie-manager-for-wp' ),
// Link field labels
'link_field_privacy_url' => __( 'Privacy policy URL', 'adsimple-cookie-manager-for-wp' ),
'link_field_privacy_label' => __( 'Privacy policy label', 'adsimple-cookie-manager-for-wp' ),
'link_field_imprint_url' => __( 'Legal notice URL', 'adsimple-cookie-manager-for-wp' ),
'link_field_imprint_label' => __( 'Legal notice label', 'adsimple-cookie-manager-for-wp' ),
// Category state labels
'category_state_activated' => __( 'Activated by default', 'adsimple-cookie-manager-for-wp' ),
'category_state_locked' => __( 'Locked', 'adsimple-cookie-manager-for-wp' ),
'category_state_visible' => __( 'Visible', 'adsimple-cookie-manager-for-wp' ),
// Category names and descriptions
'category_unclassified' => __( 'Unclassified', 'adsimple-cookie-manager-for-wp' ),
'category_unclassified_desc' => __( 'Cookies that have not yet been categorized.', 'adsimple-cookie-manager-for-wp' ),
'category_required' => __( 'Required', 'adsimple-cookie-manager-for-wp' ),
'category_required_desc' => __( 'Essential cookies required for the website to function properly.', 'adsimple-cookie-manager-for-wp' ),
'category_statistics' => __( 'Statistics', 'adsimple-cookie-manager-for-wp' ),
'category_statistics_desc' => __( 'Cookies used to understand how visitors interact with the website.', 'adsimple-cookie-manager-for-wp' ),
'category_marketing' => __( 'Marketing', 'adsimple-cookie-manager-for-wp' ),
'category_marketing_desc' => __( 'Cookies used to track visitors across websites for advertising.', 'adsimple-cookie-manager-for-wp' ),
'category_statistics_anon' => __( 'Statistics (anonymous)', 'adsimple-cookie-manager-for-wp' ),
'category_statistics_anon_desc' => __( 'Anonymized statistics cookies that do not track individual users.', 'adsimple-cookie-manager-for-wp' ),
'category_preferences' => __( 'Preferences', 'adsimple-cookie-manager-for-wp' ),
'category_preferences_desc' => __( 'Cookies that remember user preferences and personalization settings.', 'adsimple-cookie-manager-for-wp' ),
// Settings tab
'settings_consent_title' => __( 'Consent Settings', 'adsimple-cookie-manager-for-wp' ),
'settings_consent_description' => __( 'Configure how long consent is stored and general behavior.', 'adsimple-cookie-manager-for-wp' ),
'settings_consent_ttl' => __( 'Consent validity (days)', 'adsimple-cookie-manager-for-wp' ),
'settings_visibility_title' => __( 'Visibility Rules', 'adsimple-cookie-manager-for-wp' ),
'settings_visibility_description' => __( 'Control where the banner appears or is hidden.', 'adsimple-cookie-manager-for-wp' ),
'settings_exclude_paths' => __( 'Exclude paths (one per line)', 'adsimple-cookie-manager-for-wp' ),
'settings_exclude_paths_hint' => __( 'Enter URL paths where the banner should not appear. E.g.: /checkout, /my-account/*', 'adsimple-cookie-manager-for-wp' ),
'settings_visibility_blocking_warning' => __( 'On excluded paths the banner is not displayed and automatic script blocking will not work either. Third-party scripts may load without consent on these pages.', 'adsimple-cookie-manager-for-wp' ),
// Features tab
'features_gcm_title' => __( 'Google Consent Mode v2', 'adsimple-cookie-manager-for-wp' ),
'features_recommended' => __( 'Recommended', 'adsimple-cookie-manager-for-wp' ),
'features_gcm_description' => __( 'Enable Google Consent Mode to ensure proper integration with Google services like Analytics, Ads, and Tag Manager.', 'adsimple-cookie-manager-for-wp' ),
'features_gcm_enable' => __( 'Enable Google Consent Mode', 'adsimple-cookie-manager-for-wp' ),
'features_gcm_enable_description' => __( 'Automatically signal consent to Google services', 'adsimple-cookie-manager-for-wp' ),
'features_gcm_default' => __( 'Default consent state', 'adsimple-cookie-manager-for-wp' ),
'features_gcm_denied' => __( 'Denied (recommended)', 'adsimple-cookie-manager-for-wp' ),
'features_gcm_denied_description' => __( 'No tracking until user gives consent', 'adsimple-cookie-manager-for-wp' ),
'features_gcm_granted' => __( 'Granted', 'adsimple-cookie-manager-for-wp' ),
'features_gcm_granted_description' => __( 'Tracking enabled by default until user opts out', 'adsimple-cookie-manager-for-wp' ),
// GPC (Global Privacy Control)
'features_gpc_title' => __( 'Global Privacy Control (GPC)', 'adsimple-cookie-manager-for-wp' ),
'features_gpc_description' => __( 'Respect the W3C Global Privacy Control signal. When a visitor\'s browser sends the GPC opt-out signal, non-essential cookies are automatically rejected and the consent banner is skipped — the settings badge stays visible so visitors can change their preferences.', 'adsimple-cookie-manager-for-wp' ),
'features_gpc_enable' => __( 'Enable GPC support', 'adsimple-cookie-manager-for-wp' ),
'features_gpc_enable_description' => __( 'Detect the GPC signal, auto-reject non-essential cookies and skip the banner', 'adsimple-cookie-manager-for-wp' ),
'features_gpc_learn_more' => __( 'Learn more about Global Privacy Control', 'adsimple-cookie-manager-for-wp' ),
// Review tab
'review_status_title' => __( 'Banner Status', 'adsimple-cookie-manager-for-wp' ),
'review_status_description' => __( 'Overview of your consent banner configuration.', 'adsimple-cookie-manager-for-wp' ),
'review_enabled' => __( 'Banner is enabled', 'adsimple-cookie-manager-for-wp' ),
'review_enabled_description' => __( 'Your cookie consent banner is active and visible to visitors.', 'adsimple-cookie-manager-for-wp' ),
'review_disabled' => __( 'Banner is disabled', 'adsimple-cookie-manager-for-wp' ),
'review_disabled_description' => __( 'Enable the banner in the Design tab to show it to visitors.', 'adsimple-cookie-manager-for-wp' ),
'review_summary_title' => __( 'Configuration Summary', 'adsimple-cookie-manager-for-wp' ),
'review_custom_active' => __( 'Custom colors active', 'adsimple-cookie-manager-for-wp' ),
'review_preset_active' => __( 'Using preset colors', 'adsimple-cookie-manager-for-wp' ),
'review_position' => __( 'Position', 'adsimple-cookie-manager-for-wp' ),
'review_layout' => __( 'Layout', 'adsimple-cookie-manager-for-wp' ),
'review_privacy' => __( 'Privacy Policy', 'adsimple-cookie-manager-for-wp' ),
'review_configured' => __( 'Configured', 'adsimple-cookie-manager-for-wp' ),
'review_missing' => __( 'Missing', 'adsimple-cookie-manager-for-wp' ),
'review_imprint' => __( 'Legal Notice', 'adsimple-cookie-manager-for-wp' ),
'review_consent_ttl' => __( 'Consent Validity', 'adsimple-cookie-manager-for-wp' ),
'review_days' => __( 'days', 'adsimple-cookie-manager-for-wp' ),
'review_gcm' => __( 'Google Consent Mode', 'adsimple-cookie-manager-for-wp' ),
'review_gpc' => __( 'Global Privacy Control', 'adsimple-cookie-manager-for-wp' ),
'review_enabled_short' => __( 'Enabled', 'adsimple-cookie-manager-for-wp' ),
'review_disabled_short' => __( 'Disabled', 'adsimple-cookie-manager-for-wp' ),
'review_actions_title' => __( 'Save & Publish', 'adsimple-cookie-manager-for-wp' ),
'review_actions_description' => __( 'Save your changes and publish the banner to your website.', 'adsimple-cookie-manager-for-wp' ),
'review_unsaved' => __( 'You have unsaved changes. Save before publishing to apply them.', 'adsimple-cookie-manager-for-wp' ),
'review_save' => __( 'Save Changes', 'adsimple-cookie-manager-for-wp' ),
'review_publish' => __( 'Publish Banner', 'adsimple-cookie-manager-for-wp' ),
// Quick scan
'quick_scan_title' => __( 'Quick Website Scan', 'adsimple-cookie-manager-for-wp' ),
'quick_scan_description' => __( 'Launch a live browser scan to capture cookies, storage writes, and third-party requests exactly as visitors experience them.', 'adsimple-cookie-manager-for-wp' ),
'quick_scan_creating' => __( 'Creating...', 'adsimple-cookie-manager-for-wp' ),
'quick_scan_stop' => __( 'Stop', 'adsimple-cookie-manager-for-wp' ),
'quick_scan_start' => __( 'Start Scan', 'adsimple-cookie-manager-for-wp' ),
'scan_progress' => __( 'Progress', 'adsimple-cookie-manager-for-wp' ),
'scan_pages' => __( 'pages', 'adsimple-cookie-manager-for-wp' ),
'scan_stats_pages' => __( 'Pages', 'adsimple-cookie-manager-for-wp' ),
'scan_stats_cookies' => __( 'Cookies', 'adsimple-cookie-manager-for-wp' ),
'scan_stats_hosts' => __( 'External Hosts', 'adsimple-cookie-manager-for-wp' ),
'quick_scan_injecting' => __( 'Loading website and injecting scanner...', 'adsimple-cookie-manager-for-wp' ),
'quick_scan_completing' => __( 'Finalizing scan results...', 'adsimple-cookie-manager-for-wp' ),
'quick_scan_completed' => __( 'Scan completed successfully!', 'adsimple-cookie-manager-for-wp' ),
'quick_scan_missing_endpoint' => __( 'Scan endpoint not configured.', 'adsimple-cookie-manager-for-wp' ),
'quick_scan_last_job' => __( 'Last scan:', 'adsimple-cookie-manager-for-wp' ),
'quick_scan_scanned_urls' => __( 'Scanned URLs', 'adsimple-cookie-manager-for-wp' ),
'quick_scan_no_urls' => __( 'URL details not available. Run a new scan to see details.', 'adsimple-cookie-manager-for-wp' ),
'quick_scan_dialog_title' => __( 'Start Multi-Page Scan?', 'adsimple-cookie-manager-for-wp' ),
'quick_scan_dialog_description' => __( 'The scanner will crawl all public pages of your website and capture cookies and external resources.', 'adsimple-cookie-manager-for-wp' ),
'quick_scan_dialog_cancel' => __( 'Cancel', 'adsimple-cookie-manager-for-wp' ),
// Scan results
'scan_results_title' => __( 'Scan Results', 'adsimple-cookie-manager-for-wp' ),
'scan_results_no_job' => __( 'No scan job selected. Start a scan to see results.', 'adsimple-cookie-manager-for-wp' ),
'scan_results_description' => __( 'Review detected services and assign them to cookie categories.', 'adsimple-cookie-manager-for-wp' ),
'scan_results_refresh' => __( 'Refresh', 'adsimple-cookie-manager-for-wp' ),
'scan_results_save' => __( 'Save', 'adsimple-cookie-manager-for-wp' ),
'scan_tab_cookies' => __( 'Cookies', 'adsimple-cookie-manager-for-wp' ),
'scan_tab_hosts' => __( 'External Hosts', 'adsimple-cookie-manager-for-wp' ),
'scan_results_no_cookies' => __( 'No cookies detected. WordPress admin cookies are automatically filtered.', 'adsimple-cookie-manager-for-wp' ),
'scan_select_category' => __( 'Category...', 'adsimple-cookie-manager-for-wp' ),
'scan_category_unassigned' => __( 'Unassigned', 'adsimple-cookie-manager-for-wp' ),
'scan_category_none' => __( 'None (blocked)', 'adsimple-cookie-manager-for-wp' ),
'scan_cookie_provider' => __( 'Provider', 'adsimple-cookie-manager-for-wp' ),
'scan_cookie_domain' => __( 'Domain', 'adsimple-cookie-manager-for-wp' ),
'scan_cookie_expires' => __( 'Expires', 'adsimple-cookie-manager-for-wp' ),
'scan_cookie_type' => __( 'Type', 'adsimple-cookie-manager-for-wp' ),
'scan_cookie_purpose' => __( 'Purpose / Description', 'adsimple-cookie-manager-for-wp' ),
'scan_cookie_purpose_placeholder' => __( 'Describe what this cookie is used for...', 'adsimple-cookie-manager-for-wp' ),
'scan_filter' => __( 'Filter', 'adsimple-cookie-manager-for-wp' ),
'scan_filter_all' => __( 'All', 'adsimple-cookie-manager-for-wp' ),
'scan_filter_unassigned' => __( 'Unassigned', 'adsimple-cookie-manager-for-wp' ),
'scan_col_host' => __( 'Host', 'adsimple-cookie-manager-for-wp' ),
'scan_col_count' => __( 'Count', 'adsimple-cookie-manager-for-wp' ),
'scan_col_category' => __( 'Category', 'adsimple-cookie-manager-for-wp' ),
'scan_col_categories' => __( 'Categories', 'adsimple-cookie-manager-for-wp' ),
'scan_add_host' => __( 'Add Host', 'adsimple-cookie-manager-for-wp' ),
'scan_manual' => __( 'manual', 'adsimple-cookie-manager-for-wp' ),
'scan_remove_host' => __( 'Remove', 'adsimple-cookie-manager-for-wp' ),
'scan_no_categories' => __( 'None (blocked)', 'adsimple-cookie-manager-for-wp' ),
'scan_results_all_hosts_are_cookies' => __( 'All external hosts are already associated with cookies.', 'adsimple-cookie-manager-for-wp' ),
'scan_results_no_hosts' => __( 'No external hosts detected.', 'adsimple-cookie-manager-for-wp' ),
'scan_summary_refresh_error' => __( 'Unable to fetch scan results.', 'adsimple-cookie-manager-for-wp' ),
'scan_summary_saved' => __( 'Changes saved.', 'adsimple-cookie-manager-for-wp' ),
'scan_summary_save_error' => __( 'Unable to save.', 'adsimple-cookie-manager-for-wp' ),
'scan_hosts_filtered_info' => __( 'Note: Domains already used for cookies are hidden here.', 'adsimple-cookie-manager-for-wp' ),
'scan_quickscan_warning' => __( 'This is a local quick scan. It is not guaranteed that all cookies and external resources will be detected. Third-party scripts may set additional cookies dynamically that cannot be captured by a single page scan.', 'adsimple-cookie-manager-for-wp' ),
// Preview
'preview_loading' => __( 'Loading...', 'adsimple-cookie-manager-for-wp' ),
'preview_hide' => __( 'Hide Preview', 'adsimple-cookie-manager-for-wp' ),
'preview_show' => __( 'Show Preview', 'adsimple-cookie-manager-for-wp' ),
],
];
wp_add_inline_script( $handle, 'window.AS_CM_ADMIN_APP = ' . wp_json_encode( $preload ) . ';', 'before' );
}
/**
* Load Vite manifest entry
*
* @return array
*/
protected static function get_admin_manifest_entry() {
$manifest = static::get_admin_manifest();
$entry_key = 'src/main.tsx';
return isset( $manifest[ $entry_key ] ) ? $manifest[ $entry_key ] : [];
}
/**
* Read Vite manifest file
*
* @return array
*/
protected static function get_admin_manifest() {
static $manifest = null;
if ( $manifest !== null ) {
return $manifest;
}
$manifest_path = AS_CM_Manager::$path . 'assets/admin/manifest.json';
if ( ! file_exists( $manifest_path ) ) {
$manifest = [];
return $manifest;
}
$contents = file_get_contents( $manifest_path );
$data = json_decode( $contents, true );
$manifest = is_array( $data ) ? $data : [];
return $manifest;
}
/**
* Local banner defaults
*
* @return array
*/
public static function get_local_banner_defaults() {
return [
'enabled' => true,
'position' => 'popup',
'layout' => 'classic',
'theme' => 'theme1',
'consent_ttl_days' => 180,
'custom_colors_enabled' => false,
'custom_colors' => static::get_local_banner_custom_color_defaults(),
'popup_effects' => [
'popup_shadow' => '1',
'popup_overlay_color' => '',
'popup_overlay_opacity'=> 50,
],
'settings_button' => [
'label' => __( 'Privacy settings', 'adsimple-cookie-manager-for-wp' ),
'iconColor' => '',
'iconBackground' => '',
'iconDesktopShow' => '1',
'offsetSettingsTabPosition' => 1,
],
'cookie_categories' => [
'availableCategories' => [
'unclassified',
'required',
'statistics',
'statistics-anonymous',
'marketing',
'preferences',
],
'categoryStates' => [
'unclassified' => [ 'activated' => false, 'locked' => false, 'visible' => true ],
'required' => [ 'activated' => true, 'locked' => true, 'visible' => true ],
'statistics' => [ 'activated' => false, 'locked' => false, 'visible' => true ],
'statistics-anonymous' => [ 'activated' => false, 'locked' => false, 'visible' => true ],
'marketing' => [ 'activated' => false, 'locked' => false, 'visible' => true ],
'preferences' => [ 'activated' => false, 'locked' => false, 'visible' => true ],
],
],
'visibility' => [
'exclude_paths' => '',
],
'features' => [
'google_consent_mode' => [
'enabled' => false,
'default_consent' => 'denied',
],
'gpc_support' => [
'enabled' => false,
'auto_reject' => true,
'hide_banner' => true,
],
],
'texts' => [
'welcome_title' => __( 'Notice on data processing', 'adsimple-cookie-manager-for-wp' ),
'welcome_message' => __( "If you want to use our website without restrictions, please click «Accept all». Data processing from third parties that we have classified as not necessary can be {necessary-cookies}rejected{/necessary-cookies}. You can change your {cookie-settings}Privacy Settings{/cookie-settings} at any time.", 'adsimple-cookie-manager-for-wp' ),
'allow' => __( 'Accept all', 'adsimple-cookie-manager-for-wp' ),
'necessary_cookies_only' => __( 'Necessary only', 'adsimple-cookie-manager-for-wp' ),
'allow_selected' => __( 'Save selection', 'adsimple-cookie-manager-for-wp' ),
'policy' => __( 'Privacy settings', 'adsimple-cookie-manager-for-wp' ),
'title' => __( 'Settings', 'adsimple-cookie-manager-for-wp' ),
'message' => __( 'Below you have the option to set your own preferences.', 'adsimple-cookie-manager-for-wp' ),
'close' => __( 'Close', 'adsimple-cookie-manager-for-wp' ),
'heading_notice' => __( 'Notice', 'adsimple-cookie-manager-for-wp' ),
'heading_settings' => __( 'Settings', 'adsimple-cookie-manager-for-wp' ),
'select_all_categories' => __( 'Allow all categories', 'adsimple-cookie-manager-for-wp' ),
'cookie_name' => __( 'NAME', 'adsimple-cookie-manager-for-wp' ),
'cookie_provider' => __( 'PROVIDER', 'adsimple-cookie-manager-for-wp' ),
'cookie_purpose' => __( 'PURPOSE', 'adsimple-cookie-manager-for-wp' ),
'cookie_expires' => __( 'EXPIRES', 'adsimple-cookie-manager-for-wp' ),
'cookie_type' => __( 'TYPE', 'adsimple-cookie-manager-for-wp' ),
'cookie_session' => __( 'Session', 'adsimple-cookie-manager-for-wp' ),
'external_hosts_title' => __( 'External Resource Domains', 'adsimple-cookie-manager-for-wp' ),
'external_hosts_description' => __( 'Below is a list of external resource domains used on our website that may process personal data.', 'adsimple-cookie-manager-for-wp' ),
'required_label' => __( 'Necessary', 'adsimple-cookie-manager-for-wp' ),
'required_description' => '',
'statistics_label' => __( 'Statistics', 'adsimple-cookie-manager-for-wp' ),
'statistics_description' => '',
'marketing_label' => __( 'Marketing', 'adsimple-cookie-manager-for-wp' ),
'marketing_description' => '',
],
'links' => [
'privacy' => '',
'privacy_label' => '',
'privacy_page_id' => 0,
'imprint' => '',
'imprint_label' => '',
'imprint_page_id' => 0,
],
];
}
/**
* Get local banner options merged with defaults
*
* @return array
*/
public static function get_option_local_banner() {
$current = static::get_option( 'local_banner' );
$defaults = static::get_local_banner_defaults();
$base = is_array( $current ) ? self::normalize_local_banner_structure( $current ) : [];
$merged = array_replace_recursive( $defaults, $base );
$merged['enabled'] = ! empty( $merged['enabled'] );
$merged['consent_ttl_days'] = isset( $merged['consent_ttl_days'] ) ? absint( $merged['consent_ttl_days'] ) : $defaults['consent_ttl_days'];
$merged['texts'] = isset( $merged['texts'] ) ? AS_CM_Helpers_General::stripcslashes( $merged['texts'] ) : $defaults['texts'];
// Fill empty text values with defaults so fields are never blank
foreach ( $defaults['texts'] as $text_key => $text_default ) {
if ( ! isset( $merged['texts'][ $text_key ] ) || '' === trim( $merged['texts'][ $text_key ] ) ) {
$merged['texts'][ $text_key ] = $text_default;
}
}
$merged['links'] = isset( $merged['links'] ) ? AS_CM_Helpers_General::stripcslashes( $merged['links'] ) : $defaults['links'];
$allowed_positions = [ 'bottom', 'bottom-left', 'top', 'bottom-right', 'top-push', 'popup' ];
$merged['position'] = isset( $merged['position'] ) && in_array( $merged['position'], $allowed_positions, true ) ? $merged['position'] : $defaults['position'];
$allowed_layouts = [ 'classic', 'block', 'wire' ];
$allowed_themes = array_map(
function ( $index ) {
return 'theme' . $index;
},
range( 1, 16 )
);
$merged['layout'] = isset( $merged['layout'] ) && in_array( $merged['layout'], $allowed_layouts, true ) ? $merged['layout'] : $defaults['layout'];
$merged['theme'] = isset( $merged['theme'] ) && in_array( $merged['theme'], $allowed_themes, true ) ? $merged['theme'] : $defaults['theme'];
$merged['custom_colors'] = isset( $merged['custom_colors'] ) && is_array( $merged['custom_colors'] )
? array_replace_recursive( $defaults['custom_colors'], $merged['custom_colors'] )
: $defaults['custom_colors'];
foreach ( $merged['custom_colors'] as $color_key => $color_value ) {
if ( is_string( $color_value ) && '' === trim( $color_value ) && isset( $defaults['custom_colors'][ $color_key ] ) ) {
$merged['custom_colors'][ $color_key ] = $defaults['custom_colors'][ $color_key ];
}
}
$merged['popup_effects'] = isset( $merged['popup_effects'] ) && is_array( $merged['popup_effects'] )
? array_replace_recursive( $defaults['popup_effects'], $merged['popup_effects'] )
: $defaults['popup_effects'];
$merged['popup_effects']['popup_shadow'] = isset( $merged['popup_effects']['popup_shadow'] ) && '0' === (string) $merged['popup_effects']['popup_shadow'] ? '0' : '1';
$merged['popup_effects']['popup_overlay_opacity'] = isset( $merged['popup_effects']['popup_overlay_opacity'] ) ? min( 100, max( 0, absint( $merged['popup_effects']['popup_overlay_opacity'] ) ) ) : $defaults['popup_effects']['popup_overlay_opacity'];
$merged['settings_button'] = isset( $merged['settings_button'] ) && is_array( $merged['settings_button'] )
? array_replace_recursive( $defaults['settings_button'], $merged['settings_button'] )
: $defaults['settings_button'];
$merged['settings_button']['label'] = isset( $merged['settings_button']['label'] ) ? sanitize_text_field( $merged['settings_button']['label'] ) : $defaults['settings_button']['label'];
$merged['settings_button']['iconDesktopShow'] = isset( $merged['settings_button']['iconDesktopShow'] ) && '0' === (string) $merged['settings_button']['iconDesktopShow'] ? '0' : '1';
$merged['settings_button']['offsetSettingsTabPosition'] = isset( $merged['settings_button']['offsetSettingsTabPosition'] )
? min( 90, max( 0, absint( $merged['settings_button']['offsetSettingsTabPosition'] ) ) )
: $defaults['settings_button']['offsetSettingsTabPosition'];
$merged['cookie_categories'] = isset( $merged['cookie_categories'] ) && is_array( $merged['cookie_categories'] )
? array_replace_recursive( $defaults['cookie_categories'], $merged['cookie_categories'] )
: $defaults['cookie_categories'];
$available_categories = isset( $merged['cookie_categories']['availableCategories'] ) && is_array( $merged['cookie_categories']['availableCategories'] )
? array_values( array_filter( array_unique( array_map( 'sanitize_key', $merged['cookie_categories']['availableCategories'] ) ) ) )
: $defaults['cookie_categories']['availableCategories'];
$available_categories = array_values( array_diff( $available_categories, [ 'socialmedia' ] ) );
if ( empty( $available_categories ) ) {
$available_categories = $defaults['cookie_categories']['availableCategories'];
}
$category_states = [];
foreach ( $available_categories as $category ) {
$incoming = isset( $merged['cookie_categories']['categoryStates'][ $category ] ) && is_array( $merged['cookie_categories']['categoryStates'][ $category ] )
? $merged['cookie_categories']['categoryStates'][ $category ]
: [ 'activated' => false, 'locked' => false, 'visible' => true ];
$category_states[ $category ] = [
'activated' => ! empty( $incoming['activated'] ),
'locked' => ! empty( $incoming['locked'] ),
'visible' => array_key_exists( 'visible', $incoming ) ? (bool) $incoming['visible'] : true,
];
}
$merged['cookie_categories']['availableCategories'] = $available_categories;
$merged['cookie_categories']['categoryStates'] = $category_states;
$merged['visibility']['exclude_paths'] = isset( $merged['visibility']['exclude_paths'] ) ? (string) $merged['visibility']['exclude_paths'] : '';
$merged['features'] = isset( $merged['features'] ) && is_array( $merged['features'] )
? array_replace_recursive( $defaults['features'], $merged['features'] )
: $defaults['features'];
unset( $merged['features']['tcf'] );
$merged['features']['google_consent_mode']['enabled'] = ! empty( $merged['features']['google_consent_mode']['enabled'] );
$merged['features']['google_consent_mode']['default_consent'] = in_array(
isset( $merged['features']['google_consent_mode']['default_consent'] ) ? $merged['features']['google_consent_mode']['default_consent'] : '',
[ 'denied', 'granted' ],
true
)
? $merged['features']['google_consent_mode']['default_consent']
: $defaults['features']['google_consent_mode']['default_consent'];
if ( ! isset( $merged['features']['gpc_support'] ) || ! is_array( $merged['features']['gpc_support'] ) ) {
$merged['features']['gpc_support'] = $defaults['features']['gpc_support'];
}
// GPC is a single binary signal per W3C spec: enabling it implies
// auto-rejecting non-essential cookies and hiding the banner.
// Sub-flags are kept in the schema for forward-compat but always
// coerced to true when enabled to avoid useless / broken combinations.
$merged['features']['gpc_support']['enabled'] = ! empty( $merged['features']['gpc_support']['enabled'] );
$merged['features']['gpc_support']['auto_reject'] = true;
$merged['features']['gpc_support']['hide_banner'] = true;
$merged['custom_colors_enabled'] = ! empty( $merged['custom_colors_enabled'] );
return $merged;
}
public static function get_option_local_banner_css_variables() {
return static::resolve_local_banner_css_variables( static::get_option_local_banner() );
}
/**
* Save local fallback banner settings
*/
public static function save_local_banner_settings() {
$field = static::get_fields( 'local_banner' );
if ( empty( $field ) ) {
return;
}
$name = function ( $suffix ) {
return AS_CM_Helpers_General::prepare_name( 'local_banner_' . $suffix );
};
// phpcs:disable WordPress.Security.NonceVerification.Recommended -- nonce verified in save_fields()
$payload = [
'enabled' => isset( $_REQUEST[ $name( 'enabled' ) ] ) ? 1 : 0,
'position' => isset( $_REQUEST[ $name( 'position' ) ] ) ? sanitize_text_field( wp_unslash( $_REQUEST[ $name( 'position' ) ] ) ) : null,
'consent_ttl_days' => isset( $_REQUEST[ $name( 'consent_ttl_days' ) ] ) ? absint( wp_unslash( $_REQUEST[ $name( 'consent_ttl_days' ) ] ) ) : null,
'texts' => [],
'links' => [
'privacy' => isset( $_REQUEST[ $name( 'link_privacy' ) ] ) ? esc_url_raw( wp_unslash( $_REQUEST[ $name( 'link_privacy' ) ] ) ) : '',
'privacy_label' => isset( $_REQUEST[ $name( 'link_privacy_label' ) ] ) ? sanitize_text_field( wp_unslash( $_REQUEST[ $name( 'link_privacy_label' ) ] ) ) : '',
'privacy_page_id' => isset( $_REQUEST[ $name( 'link_privacy_page_id' ) ] ) ? absint( wp_unslash( $_REQUEST[ $name( 'link_privacy_page_id' ) ] ) ) : 0,
'imprint' => isset( $_REQUEST[ $name( 'link_imprint' ) ] ) ? esc_url_raw( wp_unslash( $_REQUEST[ $name( 'link_imprint' ) ] ) ) : '',
'imprint_label' => isset( $_REQUEST[ $name( 'link_imprint_label' ) ] ) ? sanitize_text_field( wp_unslash( $_REQUEST[ $name( 'link_imprint_label' ) ] ) ) : '',
'imprint_page_id' => isset( $_REQUEST[ $name( 'link_imprint_page_id' ) ] ) ? absint( wp_unslash( $_REQUEST[ $name( 'link_imprint_page_id' ) ] ) ) : 0,
],
'settings_button' => [
'label' => isset( $_REQUEST[ $name( 'settings_button_label' ) ] ) ? sanitize_text_field( wp_unslash( $_REQUEST[ $name( 'settings_button_label' ) ] ) ) : '',
],
];
foreach ( [ 'welcome_title', 'welcome_message', 'allow', 'necessary_cookies_only', 'allow_selected', 'policy', 'title', 'message', 'close' ] as $text_key ) {
$payload['texts'][ $text_key ] = isset( $_REQUEST[ $name( 'text_' . $text_key ) ] ) ? sanitize_textarea_field( wp_unslash( $_REQUEST[ $name( 'text_' . $text_key ) ] ) ) : '';
}
// phpcs:enable WordPress.Security.NonceVerification.Recommended
static::update_local_banner( $payload );
AS_CM_Services_Notice::add_notice( 'status-save-local-banner', __( 'Local banner settings saved.', 'adsimple-cookie-manager-for-wp' ) );
}
/**
* Get list of published pages for link selectors
*
* @return array
*/
protected static function get_available_pages() {
$pages = get_pages( [ 'post_status' => 'publish' ] );
if ( empty( $pages ) ) {
return [];
}
return array_map( function ( $page ) {
return [
'id' => (int) $page->ID,
'title' => get_the_title( $page ),
'url' => get_permalink( $page ),
];
}, $pages );
}
public static function get_local_banner_theme_presets() {
return [
'theme1' => [
'popup' => [
'background' => '#ffffff',
'text' => '#333333',
'elements' => '#57ca85',
'link' => '#57ca85',
'checkbox' => '#ffffff',
],
'button' => [
'background' => '#57ca85',
'text' => '#ffffff',
],
'button_necessary_cookies_only' => [
'background' => '#fcb100',
'text' => '#ffffff',
],
],
'theme2' => [
'popup' => [
'background' => '#eaf7f7',
'text' => '#5c7291',
'elements' => '#56cbdb',
'link' => '#56cbdb',
'checkbox' => '#ffffff',
],
'button' => [
'background' => '#56cbdb',
'text' => '#ffffff',
],
'button_necessary_cookies_only' => [
'background' => '#fcb100',
'text' => '#ffffff',
],
],
'theme3' => [
'popup' => [
'background' => '#252e39',
'text' => '#ffffff',
'elements' => '#14a7d0',
'link' => '#14a7d0',
'checkbox' => '#ffffff',
],
'button' => [
'background' => '#14a7d0',
'text' => '#ffffff',
],
'button_necessary_cookies_only' => [
'background' => '#fcb100',
'text' => '#ffffff',
],
],
'theme4' => [
'popup' => [
'background' => '#000000',
'text' => '#00ff00',
'elements' => '#00ff00',
'link' => '#008000',
'checkbox' => '#000000',
],
'button' => [
'background' => '#0f0',
'text' => '#000000',
],
'button_necessary_cookies_only' => [
'background' => '#edf8f8',
'text' => '#000000',
],
],
'theme5' => [
'popup' => [
'background' => '#3937a3',
'text' => '#ffffff',
'elements' => '#e62576',
'link' => '#e62576',
'checkbox' => '#ffffff',
],
'button' => [
'background' => '#e62576',
'text' => '#ffffff',
],
'button_necessary_cookies_only' => [
'background' => '#7677ff',
'text' => '#ffffff',
],
],
'theme6' => [
'popup' => [
'background' => '#64386b',
'text' => '#ffcdfd',
'elements' => '#f8a8ff',
'link' => '#f8a8ff',
'checkbox' => '#000000',
],
'button' => [
'background' => '#f8a8ff',
'text' => '#3f0045',
],
'button_necessary_cookies_only' => [
'background' => '#ffffff',
'text' => '#585858',
],
],
'theme7' => [
'popup' => [
'background' => '#237afc',
'text' => '#ffffff',
'elements' => '#ffffff',
'link' => '#113c7d',
'checkbox' => '#237afc',
],
'button' => [
'background' => '#ffffff',
'text' => '#237afc',
],
'button_necessary_cookies_only' => [
'background' => '#89b8ff',
'text' => '#ffffff',
],
],
'theme8' => [
'popup' => [
'background' => '#aa0000',
'text' => '#ffdddd',
'elements' => '#ffffff',
'link' => '#ff0000',
'checkbox' => '#ff0000',
],
'button' => [
'background' => '#ff0000',
'text' => '#ffdddd',
],
'button_necessary_cookies_only' => [
'background' => '#d33039',
'text' => '#ffffff',
],
],
'theme9' => [
'popup' => [
'background' => '#383b75',
'text' => '#ffffff',
'elements' => '#f1d600',
'link' => '#f1d600',
'checkbox' => '#ffffff',
],
'button' => [
'background' => '#f1d600',
'text' => '#ffffff',
],
'button_necessary_cookies_only' => [
'background' => '#6b73c6',
'text' => '#ffffff',
],
],
'theme10' => [
'popup' => [
'background' => '#1d8a8a',
'text' => '#ffffff',
'elements' => '#62ffaa',
'link' => '#62ffaa',
'checkbox' => '#ffffff',
],
'button' => [
'background' => '#62ffaa',
'text' => '#ffffff',
],
'button_necessary_cookies_only' => [
'background' => '#ffffff',
'text' => '#585858',
],
],
'theme11' => [
'popup' => [
'background' => '#edeff5',
'text' => '#838391',
'elements' => '#4b81e8',
'link' => '#4b81e8',
'checkbox' => '#ffffff',
],
'button' => [
'background' => '#4b81e8',
'text' => '#ffffff',
],
'button_necessary_cookies_only' => [
'background' => '#fcb100',
'text' => '#ffffff',
],
],
'theme12' => [
'popup' => [
'background' => '#343c66',
'text' => '#cfcfe8',
'elements' => '#f71559',
'link' => '#f71559',
'checkbox' => '#ffffff',
],
'button' => [
'background' => '#f71559',
'text' => '#ffffff',
],
'button_necessary_cookies_only' => [
'background' => '#48548a',
'text' => '#ffffff',
],
],
'theme13' => [
'popup' => [
'background' => '#216942',
'text' => '#b2d192',
'elements' => '#afed71',
'link' => '#afed71',
'checkbox' => '#000000',
],
'button' => [
'background' => '#afed71',
'text' => '#000000',
],
'button_necessary_cookies_only' => [
'background' => '#ffffff',
'text' => '#585858',
],
],
'theme14' => [
'popup' => [
'background' => '#3c404d',
'text' => '#d6d6d6',
'elements' => '#8bed4f',
'link' => '#8bed4f',
'checkbox' => '#000000',
],
'button' => [
'background' => '#8bed4f',
'text' => '#000000',
'border' => '#8bed4f',
],
'button_necessary_cookies_only' => [
'background' => '#ffffff',
'text' => '#585858',
],
],
'theme15' => [
'popup' => [
'background' => '#eb6c44',
'text' => '#ffffff',
'elements' => '#f5d948',
'link' => '#f5d948',
'checkbox' => '#000000',
],
'button' => [
'background' => '#f5d948',
'text' => '#000000',
],
'button_necessary_cookies_only' => [
'background' => '#ffffff',
'text' => '#585858',
],
],
'theme16' => [
'popup' => [
'background' => '#efefef',
'text' => '#404040',
'elements' => '#8ec760',
'link' => '#8ec760',
'checkbox' => '#ffffff',
],
'button' => [
'background' => '#8ec760',
'text' => '#ffffff',
],
'button_necessary_cookies_only' => [
'background' => '#fcb100',
'text' => '#ffffff',
],
],
];
}
protected static function get_local_banner_custom_color_defaults( $theme_key = 'theme1' ) {
$themes = static::get_local_banner_theme_presets();
if ( ! isset( $themes[ $theme_key ] ) ) {
$theme_key = 'theme1';
}
$preset = $themes[ $theme_key ];
$popup_bg = static::normalize_hex_color_value( $preset['popup']['background'], '#ffffff' );
$popup_text = static::normalize_hex_color_value( $preset['popup']['text'], '#333333' );
$popup_elements = static::normalize_hex_color_value( $preset['popup']['elements'], '#57ca85' );
$popup_link = static::normalize_hex_color_value( $preset['popup']['link'], $popup_elements );
$popup_link_hover = isset( $preset['popup']['link_hover'] )
? static::normalize_hex_color_value( $preset['popup']['link_hover'], $popup_link )
: static::normalize_hex_color_value( static::mix_hex_colors( $popup_link, '#000000', 0.12 ), $popup_link );
$checkbox_color = static::normalize_hex_color_value( $preset['popup']['checkbox'] ?? $popup_elements, $popup_elements );
$button_bg = static::normalize_hex_color_value( $preset['button']['background'], '#10b8ca' );
$button_text = static::normalize_hex_color_value( $preset['button']['text'], '#ffffff' );
$button_border = static::normalize_hex_color_value( $preset['button']['border'] ?? $button_bg, $button_bg );
$button_hover = static::normalize_hex_color_value( static::mix_hex_colors( $button_bg, '#000000', 0.15 ), $button_bg );
$button_text_hover = $button_text;
$button_border_hover = $button_border;
$secondary_bg = static::normalize_hex_color_value( $preset['button_necessary_cookies_only']['background'], '#fcb100' );
$secondary_text = static::normalize_hex_color_value( $preset['button_necessary_cookies_only']['text'], '#ffffff' );
$secondary_border = static::normalize_hex_color_value( $preset['button_necessary_cookies_only']['border'] ?? $secondary_bg, $secondary_bg );
$secondary_hover = static::normalize_hex_color_value( static::mix_hex_colors( $secondary_bg, '#ffffff', 0.2 ), $secondary_bg );
$secondary_text_hover = $secondary_text;
$secondary_border_hover = $secondary_border;
return [
'popup' => $popup_bg,
'popupText' => $popup_text,
'popupElements' => $popup_elements,
'popupLink' => $popup_link,
'popupLinkHover' => $popup_link_hover,
'checkboxColor' => $checkbox_color,
'button' => $button_bg,
'buttonHover' => $button_hover,
'buttonText' => $button_text,
'buttonTextHover' => $button_text_hover,
'buttonBorder' => $button_border,
'buttonBorderHover' => $button_border_hover,
'necessaryCookiesOnlyButton' => $secondary_bg,
'necessaryCookiesOnlyButtonHover' => $secondary_hover,
'necessaryCookiesOnlyButtonText' => $secondary_text,
'necessaryCookiesOnlyButtonTextHover'=> $secondary_text_hover,
'necessaryCookiesOnlyButtonBorder' => $secondary_border,
'necessaryCookiesOnlyButtonBorderHover' => $secondary_border_hover,
];
}
protected static function resolve_local_banner_css_variables( $local_options ) {
$themes = static::get_local_banner_theme_presets();
$theme_key = isset( $local_options['theme'] ) ? sanitize_key( $local_options['theme'] ) : 'theme1';
if ( ! isset( $themes[ $theme_key ] ) ) {
$theme_key = 'theme1';
}
$custom_colors_enabled = ! empty( $local_options['custom_colors_enabled'] );
if ( $custom_colors_enabled ) {
$theme_key = 'theme1';
}
$preset = $themes[ $theme_key ];
$css = [
'--ascm-popup-bg' => static::normalize_hex_color_value( $preset['popup']['background'], '#ffffff' ),
'--ascm-popup-text' => static::normalize_hex_color_value( $preset['popup']['text'], '#1f2937' ),
'--ascm-popup-elements' => static::normalize_hex_color_value( $preset['popup']['elements'], '#57ca85' ),
'--ascm-popup-link' => static::normalize_hex_color_value( $preset['popup']['link'], $preset['popup']['elements'] ),
'--ascm-popup-link-hover' => static::normalize_hex_color_value( $preset['popup']['link_hover'] ?? $preset['popup']['link'], $preset['popup']['link'] ),
'--ascm-checkbox-color' => static::normalize_hex_color_value( $preset['popup']['checkbox'] ?? $preset['popup']['elements'], $preset['popup']['elements'] ),
'--ascm-button-primary-bg' => static::normalize_hex_color_value( $preset['button']['background'], '#10b8ca' ),
'--ascm-button-primary-text' => static::normalize_hex_color_value( $preset['button']['text'], '#ffffff' ),
'--ascm-button-primary-border' => static::normalize_hex_color_value( $preset['button']['border'] ?? $preset['button']['background'], $preset['button']['background'] ),
'--ascm-button-secondary-bg' => static::normalize_hex_color_value( $preset['button_necessary_cookies_only']['background'], '#fcb100' ),
'--ascm-button-secondary-text' => static::normalize_hex_color_value( $preset['button_necessary_cookies_only']['text'], '#ffffff' ),
'--ascm-button-secondary-border' => static::normalize_hex_color_value( $preset['button_necessary_cookies_only']['border'] ?? $preset['button_necessary_cookies_only']['background'], $preset['button_necessary_cookies_only']['background'] ),
];
$custom_colors = isset( $local_options['custom_colors'] ) && is_array( $local_options['custom_colors'] ) ? $local_options['custom_colors'] : [];
if ( $custom_colors_enabled ) {
$custom_defaults = static::get_local_banner_custom_color_defaults( 'theme1' );
$custom_map = [
'popup' => [ '--ascm-popup-bg' ],
'popupText' => [ '--ascm-popup-text' ],
'popupElements' => [ '--ascm-popup-elements' ],
'popupLink' => [ '--ascm-popup-link' ],
'popupLinkHover' => [ '--ascm-popup-link-hover' ],
'checkboxColor' => [ '--ascm-checkbox-color' ],
'button' => [ '--ascm-button-primary-bg' ],
'buttonHover' => [ '--ascm-button-primary-hover' ],
'buttonText' => [ '--ascm-button-primary-text' ],
'buttonTextHover' => [ '--ascm-button-primary-text-hover' ],
'buttonBorder' => [ '--ascm-button-primary-border' ],
'buttonBorderHover' => [ '--ascm-button-primary-border-hover' ],
'necessaryCookiesOnlyButton' => [ '--ascm-button-secondary-bg' ],
'necessaryCookiesOnlyButtonHover' => [ '--ascm-button-secondary-hover' ],
'necessaryCookiesOnlyButtonText' => [ '--ascm-button-secondary-text' ],
'necessaryCookiesOnlyButtonTextHover' => [ '--ascm-button-secondary-text-hover' ],
'necessaryCookiesOnlyButtonBorder' => [ '--ascm-button-secondary-border' ],
'necessaryCookiesOnlyButtonBorderHover' => [ '--ascm-button-secondary-border-hover' ],
];
foreach ( $custom_map as $custom_key => $variables ) {
$raw_value = isset( $custom_colors[ $custom_key ] ) ? $custom_colors[ $custom_key ] : '';
if ( '' === trim( (string) $raw_value ) ) {
$raw_value = isset( $custom_defaults[ $custom_key ] ) ? $custom_defaults[ $custom_key ] : '';
}
if ( '' === trim( (string) $raw_value ) ) {
continue;
}
$default_value = isset( $custom_defaults[ $custom_key ] ) ? $custom_defaults[ $custom_key ] : '#000000';
$value = static::normalize_hex_color_value( $raw_value, $default_value );
foreach ( (array) $variables as $variable ) {
$css[ $variable ] = $value;
}
}
}
if ( empty( $custom_colors['popupLinkHover'] ) && empty( $preset['popup']['link_hover'] ) ) {
$css['--ascm-popup-link-hover'] = static::mix_hex_colors( $css['--ascm-popup-link'], '#000000', 0.12 );
}
$css['--ascm-popup-muted'] = static::mix_hex_colors( $css['--ascm-popup-text'], $css['--ascm-popup-bg'], 0.45 );
$css['--ascm-button-primary-border'] = $css['--ascm-button-primary-border'] ?? $css['--ascm-button-primary-bg'];
$css['--ascm-button-primary-border-hover'] = $css['--ascm-button-primary-border-hover'] ?? $css['--ascm-button-primary-border'];
$css['--ascm-button-primary-hover'] = $css['--ascm-button-primary-hover'] ?? static::mix_hex_colors( $css['--ascm-button-primary-bg'], '#000000', 0.15 );
$css['--ascm-button-primary-text-hover'] = $css['--ascm-button-primary-text-hover'] ?? $css['--ascm-button-primary-text'];
$css['--ascm-button-secondary-border'] = $css['--ascm-button-secondary-border'] ?? $css['--ascm-button-secondary-bg'];
$css['--ascm-button-secondary-border-hover'] = $css['--ascm-button-secondary-border-hover'] ?? $css['--ascm-button-secondary-border'];
$css['--ascm-button-secondary-hover'] = $css['--ascm-button-secondary-hover'] ?? static::mix_hex_colors( $css['--ascm-button-secondary-bg'], '#ffffff', 0.2 );
$css['--ascm-button-secondary-text-hover'] = $css['--ascm-button-secondary-text-hover'] ?? $css['--ascm-button-secondary-text'];
$css['--ascm-checkbox-color'] = $css['--ascm-checkbox-color'] ?? $css['--ascm-popup-elements'];
$css['--ascm-border-color'] = static::mix_hex_colors( $css['--ascm-popup-bg'], $css['--ascm-popup-text'], 0.18 );
$css['--ascm-switch-track'] = static::mix_hex_colors( $css['--ascm-popup-bg'], $css['--ascm-popup-text'], 0.25 );
$css['--ascm-switch-track-active'] = $css['--ascm-popup-elements'];
$css['--ascm-switch-thumb'] = '#FFFFFF';
$css['--ascm-badge-bg'] = $css['--ascm-popup-bg'];
$css['--ascm-badge-text'] = $css['--ascm-button-secondary-text'];
$css['--ascm-badge-icon-bg'] = $css['--ascm-popup-elements'];
$css['--ascm-badge-icon-color'] = '#FFFFFF';
return $css;
}
protected static function normalize_hex_color_value( $value, $fallback ) {
$color = sanitize_hex_color( $value );
if ( ! $color ) {
$color = sanitize_hex_color( $fallback );
}
if ( ! $color ) {
$color = '#000000';
}
if ( strlen( $color ) === 4 ) {
$color = '#' . strtoupper( $color[1] . $color[1] . $color[2] . $color[2] . $color[3] . $color[3] );
} else {
$color = strtoupper( $color );
}
return $color;
}
protected static function mix_hex_colors( $color_one, $color_two, $ratio ) {
$color_one = static::normalize_hex_color_value( $color_one, '#000000' );
$color_two = static::normalize_hex_color_value( $color_two, '#000000' );
$ratio = max( 0, min( 1, (float) $ratio ) );
$rgb_one = static::hex_to_rgb( $color_one );
$rgb_two = static::hex_to_rgb( $color_two );
$r = (int) round( $rgb_one['r'] * ( 1 - $ratio ) + $rgb_two['r'] * $ratio );
$g = (int) round( $rgb_one['g'] * ( 1 - $ratio ) + $rgb_two['g'] * $ratio );
$b = (int) round( $rgb_one['b'] * ( 1 - $ratio ) + $rgb_two['b'] * $ratio );
return sprintf( '#%02X%02X%02X', $r, $g, $b );
}
protected static function hex_to_rgb( $hex ) {
$hex = ltrim( $hex, '#' );
return [
'r' => hexdec( substr( $hex, 0, 2 ) ),
'g' => hexdec( substr( $hex, 2, 2 ) ),
'b' => hexdec( substr( $hex, 4, 2 ) ),
];
}
protected static function normalize_local_banner_structure( $data ) {
if ( ! is_array( $data ) ) {
return [];
}
// Ensure arrays
if ( ! isset( $data['texts'] ) || ! is_array( $data['texts'] ) ) {
$data['texts'] = [];
}
if ( ! isset( $data['links'] ) || ! is_array( $data['links'] ) ) {
$data['links'] = [];
}
if ( ! isset( $data['links']['privacy_page_id'] ) ) {
$data['links']['privacy_page_id'] = 0;
}
if ( ! isset( $data['links']['imprint_page_id'] ) ) {
$data['links']['imprint_page_id'] = 0;
}
// Guard nested arrays that might be missing
if ( isset( $data['custom_colors'] ) && ! is_array( $data['custom_colors'] ) ) {
$data['custom_colors'] = [];
}
if ( isset( $data['popup_effects'] ) && ! is_array( $data['popup_effects'] ) ) {
$data['popup_effects'] = [];
}
if ( ! isset( $data['settings_button'] ) || ! is_array( $data['settings_button'] ) ) {
$data['settings_button'] = [];
}
if ( ! isset( $data['settings_button']['label'] ) ) {
$data['settings_button']['label'] = '';
}
if ( ! isset( $data['settings_button']['iconColor'] ) ) {
$data['settings_button']['iconColor'] = '';
}
if ( ! isset( $data['settings_button']['iconBackground'] ) ) {
$data['settings_button']['iconBackground'] = '';
}
if ( ! isset( $data['settings_button']['iconDesktopShow'] ) ) {
$data['settings_button']['iconDesktopShow'] = '1';
}
if ( ! isset( $data['settings_button']['offsetSettingsTabPosition'] ) ) {
$data['settings_button']['offsetSettingsTabPosition'] = 0;
}
if ( ! isset( $data['cookie_categories'] ) || ! is_array( $data['cookie_categories'] ) ) {
$data['cookie_categories'] = [];
}
if ( ! isset( $data['visibility'] ) || ! is_array( $data['visibility'] ) ) {
$data['visibility'] = [];
}
if ( ! isset( $data['features'] ) || ! is_array( $data['features'] ) ) {
$data['features'] = [];
}
if ( ! isset( $data['custom_colors_enabled'] ) ) {
$data['custom_colors_enabled'] = false;
}
return $data;
}
}