\WP_REST_Server::READABLE, 'callback' => array( __CLASS__, 'get_settings' ), 'permission_callback' => array( __CLASS__, 'check_admin' ), ), array( 'methods' => \WP_REST_Server::EDITABLE, 'callback' => array( __CLASS__, 'update_settings' ), 'permission_callback' => array( __CLASS__, 'check_admin' ), ), ) ); register_rest_route( 'harvify-cookie/v1', '/license', array( 'methods' => \WP_REST_Server::READABLE, 'callback' => array( __CLASS__, 'get_license' ), 'permission_callback' => array( __CLASS__, 'check_admin' ), ) ); register_rest_route( 'harvify-cookie/v1', '/settings/export', array( 'methods' => \WP_REST_Server::READABLE, 'callback' => array( __CLASS__, 'export_settings' ), 'permission_callback' => array( __CLASS__, 'check_admin' ), ) ); register_rest_route( 'harvify-cookie/v1', '/settings/import', array( 'methods' => \WP_REST_Server::CREATABLE, 'callback' => array( __CLASS__, 'import_settings' ), 'permission_callback' => array( __CLASS__, 'check_admin' ), ) ); } public static function check_admin() { return current_user_can( 'manage_options' ); } /** * Return current settings + license. */ public static function get_settings() { return rest_ensure_response( array( 'success' => true, 'settings' => Banner::get_settings(), 'license' => self::license_state(), 'templates' => function_exists( 'harvify_cookie_pro_templates' ) ? harvify_cookie_pro_templates() : array( 'default' => array( 'name' => 'Default', 'description' => 'Clean bottom bar.', ), ), ) ); } /** * Update settings — handles all free and Pro fields. */ public static function update_settings( $request ) { $params = $request->get_json_params(); $updated = self::sanitize( is_array( $params ) ? $params : array(), Banner::get_settings() ); update_option( 'harvify_cookie_banner_settings', $updated, false ); return rest_ensure_response( array( 'success' => true, 'settings' => $updated, ) ); } /** * Apply a request's values to the current settings, field by field. * * One sanitiser for BOTH the settings screen and Import. Until 1.0.22 Import * stored whatever the JSON contained, unsanitised -- including a custom CSS * field, which a free plugin hosted on WordPress.org may not save. * * Keys this method does not know are carried over from $current untouched, * so a value saved while licensed (custom CSS) survives a save without one. * * @param array $params Incoming values. * @param array $current Settings now. * @return array */ public static function sanitize( array $params, array $current ) { $updated = $current; // Position. if ( isset( $params['position'] ) ) { $allowed = array( 'bottom', 'top', 'center', 'corner' ); $updated['position'] = in_array( $params['position'], $allowed, true ) ? $params['position'] : $current['position']; } // Expiry. if ( isset( $params['consent_expiry_days'] ) ) { $updated['consent_expiry_days'] = max( 30, min( 730, absint( $params['consent_expiry_days'] ) ) ); } // Policy version. if ( isset( $params['policy_version'] ) ) { $updated['policy_version'] = sanitize_text_field( $params['policy_version'] ); } // Template. if ( isset( $params['template'] ) ) { $templates = function_exists( 'harvify_cookie_pro_templates' ) ? harvify_cookie_pro_templates() : array(); $updated['template'] = isset( $templates[ $params['template'] ] ) ? $params['template'] : $current['template']; } // Color fields. foreach ( array( 'backgroundColor', 'textColor', 'acceptButtonColor', 'acceptButtonText', 'rejectButtonColor', 'rejectButtonText', ) as $color_field ) { if ( isset( $params[ $color_field ] ) ) { // "transparent" is a real value -- the Reject button's own default -- // and until 1.0.25 a save could not put it back. $updated[ $color_field ] = 'transparent' === strtolower( trim( (string) $params[ $color_field ] ) ) ? 'transparent' : ( sanitize_hex_color( $params[ $color_field ] ) ?: $current[ $color_field ] ); } } // Logo. if ( isset( $params['logoUrl'] ) ) { $updated['logoUrl'] = esc_url_raw( $params['logoUrl'] ); } // Text customization fields. foreach ( array( 'bannerTitle', 'bannerDescription', 'acceptLabel', 'rejectLabel', 'settingsLabel', 'preferencesTitle', 'preferencesIntro', 'savePreferencesLabel', ) as $text_field ) { if ( isset( $params[ $text_field ] ) ) { $updated[ $text_field ] = sanitize_text_field( $params[ $text_field ] ); } } // Toggle fields. foreach ( array( 'googleConsentMode', 'scriptBlocking' ) as $toggle ) { if ( isset( $params[ $toggle ] ) ) { $updated[ $toggle ] = (bool) $params[ $toggle ]; } } // Controller identity (policy generator). Optional; the generator falls // back to the site name / admin email when empty. if ( isset( $params['controllerName'] ) ) { $updated['controllerName'] = sanitize_text_field( $params['controllerName'] ); } if ( isset( $params['controllerEmail'] ) ) { $email = sanitize_email( $params['controllerEmail'] ); $updated['controllerEmail'] = $email ? $email : ''; } if ( isset( $params['controllerAddress'] ) ) { $updated['controllerAddress'] = sanitize_textarea_field( $params['controllerAddress'] ); } // Geo regions. if ( isset( $params['geoRegions'] ) && is_array( $params['geoRegions'] ) ) { $allowed_regions = function_exists( 'harvify_cookie_region_countries' ) ? array_keys( harvify_cookie_region_countries() ) : array( 'eu', 'uk', 'eea', 'california', 'canada', 'brazil' ); $updated['geoRegions'] = array_values( array_intersect( array_map( 'sanitize_key', $params['geoRegions'] ), $allowed_regions ) ); } // Cookie declarations. if ( isset( $params['cookieDeclarations'] ) && is_array( $params['cookieDeclarations'] ) ) { $allowed_categories = array( 'necessary', 'analytics', 'marketing', 'preferences' ); $declarations = array(); foreach ( $params['cookieDeclarations'] as $cookie ) { if ( ! is_array( $cookie ) || empty( $cookie['name'] ) ) { continue; } $declarations[] = array( 'name' => sanitize_text_field( $cookie['name'] ), 'provider' => isset( $cookie['provider'] ) ? sanitize_text_field( $cookie['provider'] ) : '', 'purpose' => isset( $cookie['purpose'] ) ? sanitize_text_field( $cookie['purpose'] ) : '', 'category' => isset( $cookie['category'] ) && in_array( $cookie['category'], $allowed_categories, true ) ? $cookie['category'] : 'necessary', 'expiry' => isset( $cookie['expiry'] ) ? sanitize_text_field( $cookie['expiry'] ) : '', ); } $updated['cookieDeclarations'] = $declarations; } /** * Let the separately distributed Pro build sanitise its own fields. * * @param array $updated Settings about to be saved. * @param array $params Incoming values. */ return (array) apply_filters( 'harvify_cookie_save_settings', $updated, $params ); } /** * License state. */ public static function get_license() { return rest_ensure_response( array( 'success' => true, 'license' => self::license_state(), ) ); } /** * Export settings as JSON. */ public static function export_settings() { return rest_ensure_response( array( 'success' => true, 'settings' => Banner::get_settings(), ) ); } /** * Import settings from JSON. */ public static function import_settings( $request ) { $params = $request->get_json_params(); $imported = isset( $params['settings'] ) && is_array( $params['settings'] ) ? $params['settings'] : array(); if ( empty( $imported ) ) { return new \WP_Error( 'harvify_cookie_empty_import', 'No settings provided.', array( 'status' => 400 ) ); } // Through the SAME sanitiser as the settings screen. $merged = self::sanitize( $imported, Banner::get_settings() ); update_option( 'harvify_cookie_banner_settings', $merged, false ); return rest_ensure_response( array( 'success' => true, 'settings' => $merged, ) ); } /** * Build license state array. */ private static function license_state() { $state = array( 'active' => false, 'plan' => '', 'plans' => array(), ); // Per-plugin unlock gate (premium build + valid license), NOT the global // bundle flag which would leak Pro onto this plugin's free build. if ( function_exists( 'harvify_cookie_pro_is_active' ) ) { $state['active'] = harvify_cookie_pro_is_active(); } // Plan name is bundle-wide display info. if ( class_exists( '\Harvify\License\Bundle' ) ) { $state['plan'] = \Harvify\License\Bundle::plan_name(); } if ( function_exists( 'harvify_cookie_pro_is_available' ) ) { $state['available'] = harvify_cookie_pro_is_available(); } // Bundle pricing for upgrade card (single source: bundle-license Pricing). $state['plans'] = class_exists( '\Harvify\License\Pricing' ) ? \Harvify\License\Pricing::plans() : array(); $state['pricingUrl'] = class_exists( '\Harvify\License\Pricing' ) ? \Harvify\License\Pricing::pricing_url() : ''; $state['checkoutUrl'] = class_exists( '\Harvify\License\Pricing' ) ? \Harvify\License\Pricing::checkout_url( function_exists( 'harvify_cookie_fs' ) ? harvify_cookie_fs() : null ) : ''; $state['bundleName'] = 'Harvify Pro Bundle'; return $state; } }