start_controls_section( 'aafe_display_conditions_section', [ 'label' => ' ' . esc_html__( 'AAFE Display Conditions', 'addronix-addons-for-elementor' ), 'tab' => Controls_Manager::TAB_ADVANCED, ] ); $element->add_control( 'aafe_display_conditions_data', [ 'label' => esc_html__( 'Conditions', 'addronix-addons-for-elementor' ), 'type' => 'aafe_display_conditions', 'default' => '', ] ); $element->end_controls_section(); } public function should_render( $should_render, Element_Base $element ) { if ( ! $should_render ) { return $should_render; } // Always render in the Elementor Editor if ( \Elementor\Plugin::$instance->editor->is_edit_mode() ) { return $should_render; } $settings = $element->get_settings_for_display(); $conditions_json = isset( $settings['aafe_display_conditions_data'] ) ? $settings['aafe_display_conditions_data'] : ''; if ( empty( $conditions_json ) ) { return $should_render; } $groups = []; // Elementor sometimes pre-parses JSON into arrays. if ( is_array( $conditions_json ) ) { $groups = $conditions_json; } else if ( is_string( $conditions_json ) ) { $groups = json_decode( $conditions_json, true ); // If it returned a string (double encoded), decode again. if ( is_string( $groups ) ) { $groups = json_decode( $groups, true ); } // If it's still not an array, try unslashing. if ( ! is_array( $groups ) ) { $unslashed = wp_unslash( $conditions_json ); $groups = json_decode( $unslashed, true ); if ( is_string( $groups ) ) { $groups = json_decode( $groups, true ); } } } if ( empty( $groups ) || ! is_array( $groups ) ) { return $should_render; } // Evaluate groups (OR logic) $group_passed = false; foreach ( $groups as $group ) { if ( ! is_array( $group ) || empty( $group ) ) { continue; } // Evaluate conditions within group (AND logic) $conditions_passed = true; foreach ( $group as $condition ) { if ( ! is_array( $condition ) ) { continue; } $type = $condition['type'] ?? ''; $operator = $condition['operator'] ?? 'is'; $value = $condition['value'] ?? ''; $is_match = $this->evaluate_condition( $type, $value ); if ( 'is_not' === $operator ) { $is_match = ! $is_match; } if ( ! $is_match ) { $conditions_passed = false; break; // AND logic fails if one condition fails } } if ( $conditions_passed ) { $group_passed = true; break; // OR logic passes if one group passes } } return $group_passed; } private function evaluate_condition( $type, $value ) { $user_agent = isset( $_SERVER['HTTP_USER_AGENT'] ) ? strtolower( sanitize_text_field( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ) ) : ''; switch ( $type ) { case 'login_status': if ( 'logged_in' === $value ) return is_user_logged_in(); if ( 'logged_out' === $value ) return ! is_user_logged_in(); return true; case 'user_role': if ( ! is_user_logged_in() ) return false; $user = wp_get_current_user(); return in_array( $value, (array) $user->roles, true ); case 'os': if ( 'windows' === $value && strpos( $user_agent, 'windows' ) !== false ) return true; if ( 'mac' === $value && strpos( $user_agent, 'macintosh' ) !== false ) return true; if ( 'linux' === $value && strpos( $user_agent, 'linux' ) !== false && strpos( $user_agent, 'android' ) === false ) return true; if ( 'ios' === $value && ( strpos( $user_agent, 'iphone' ) !== false || strpos( $user_agent, 'ipad' ) !== false || strpos( $user_agent, 'ipod' ) !== false ) ) return true; if ( 'android' === $value && strpos( $user_agent, 'android' ) !== false ) return true; return false; case 'browser': $has = function ( $needle ) use ( $user_agent ) { return false !== strpos( $user_agent, $needle ); }; $is_edge = $has( 'edg' ); $is_opera = $has( 'opr/' ) || $has( 'opera' ); $is_firefox = $has( 'firefox' ) || $has( 'fxios' ); $is_chrome = ( $has( 'chrome' ) || $has( 'crios' ) ) && ! $is_edge && ! $is_opera; $is_safari = $has( 'safari' ) && ! $has( 'chrome' ) && ! $has( 'crios' ) && ! $has( 'fxios' ) && ! $has( 'android' ) && ! $is_edge && ! $is_opera; if ( 'chrome' === $value ) return $is_chrome; if ( 'firefox' === $value ) return $is_firefox; if ( 'safari' === $value ) return $is_safari; if ( 'edge' === $value ) return $is_edge; if ( 'opera' === $value ) return $is_opera; return false; case 'date': // Site timezone aware. Value is either a single day "Y-m-d" or a range "from|to" // (either side may be empty for open-ended ranges). return $this->in_range( wp_date( 'Y-m-d' ), (string) $value ); case 'time': // Value is either a single "H:i" (matches that whole minute) or a range "from|to". // Ranges that pass midnight (e.g. 22:00|06:00) are supported. return $this->in_range( wp_date( 'H:i' ), (string) $value, true ); case 'url_param': // AAFE FIX BUG#8: Sanitize $_GET key and value before comparison. if ( strpos( $value, '=' ) !== false ) { list( $k, $v ) = explode( '=', $value, 2 ); $k = sanitize_key( $k ); return isset( $_GET[ $k ] ) && sanitize_text_field( wp_unslash( $_GET[ $k ] ) ) === $v; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only display condition check. } else { $param_key = sanitize_key( $value ); return isset( $_GET[ $param_key ] ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only display condition check. } default: return false; } } /** * Check whether $current (a zero-padded "Y-m-d" or "H:i" string, so plain string * comparison is chronological) is inside the stored value. * * @param string $current Current date or time. * @param string $value "single" or "from|to". * @param bool $wraparound Allow from > to (time ranges across midnight). * @return bool */ private function in_range( $current, $value, $wraparound = false ) { $value = trim( $value ); if ( '' === $value ) { return false; } if ( false === strpos( $value, '|' ) ) { return $current === $value; } list( $from, $to ) = array_map( 'trim', explode( '|', $value, 2 ) ); if ( '' === $from && '' === $to ) { return true; } if ( '' === $from ) { return $current <= $to; } if ( '' === $to ) { return $current >= $from; } if ( $wraparound && $from > $to ) { return $current >= $from || $current <= $to; } return $current >= $from && $current <= $to; } }