plugin = $plugin; $this->options = $options; } /** * Initializes the plugin, runs on `wp` hook. */ public function init() { $this->box_ids_to_load = $this->filter_boxes(); // Only add other hooks if necessary if( count( $this->box_ids_to_load ) > 0 ) { add_action( 'wp_enqueue_scripts', array( $this, 'load_assets' ), 90 ); } } /** * Get global rules for all boxes * * @return array */ protected function get_filter_rules() { $rules = get_option( 'boxzilla_rules', array() ); if( ! is_array( $rules ) ) { return array(); } return $rules; } /** * Match a string against an array of patterns, glob-style. * * @param string $string * @param array $patterns * * @return boolean */ protected function match_patterns( $string, $patterns ) { $string = strtolower( $string ); foreach( $patterns as $pattern ) { $pattern = rtrim( $pattern, '/' ); $pattern = strtolower( $pattern ); if( function_exists( 'fnmatch' ) ) { $match = fnmatch( $pattern, $string ); } else { $match = ( $pattern === $string ); } if( $match ) { return true; } } return false; } /** * @return string */ protected function get_request_url() { // strip trailing slashes $request_uri = rtrim( $_SERVER['REQUEST_URI'], '/' ); // strip ? and query string $qpos = strpos( $request_uri, '?' ); if( $qpos ) { $request_uri = substr( $request_uri, 0, $qpos ); } return $request_uri; } /** * Check if this rule passes (conditional matches expected value) * * @param string $condition * @param string $value * @param boolean $qualifier * * @return bool */ protected function match_rule( $condition, $value, $qualifier = true ) { $matched = false; // cast value to array & trim whitespace or excess comma's $value = array_map( 'trim', explode( ',', rtrim( trim( $value ), ',' ) ) ); switch ( $condition ) { case 'everywhere'; $matched = true; break; case 'is_url': $matched = $this->match_patterns( $this->get_request_url(), $value ); break; case 'is_referer': if( ! empty( $_SERVER['HTTP_REFERER'] ) ) { $referer = $_SERVER['HTTP_REFERER']; $matched = $this->match_patterns( $referer, $value ); } break; case 'is_post_type': $post_type = (string) get_post_type(); $matched = in_array( $post_type, (array) $value ); break; case 'is_single': case 'is_post': // convert to empty string if array with just empty string in it $value = ( $value === array( '' ) ) ? '' : $value; $matched = is_single( $value ); break; case 'is_post_in_category': $matched = is_singular( 'post' ) && has_category( $value ); break; case 'is_page': $matched = is_page( $value ); break; case 'is_post_with_tag': $matched = is_singular( 'post' ) && has_tag( $value ); break; case 'is_user_logged_in': $matched = is_user_logged_in(); break; } /** * Filters whether a given box rule matches the condition and expected value. * * The dynamic portion of the hook, `$condition`, refers to the condition being matched. * * @param boolean $matched * @param array $value */ $matched = apply_filters( 'boxzilla_box_rule_matches_' . $condition, $matched, $value ); // if qualifier is set to false, we need to reverse this value here. if( ! $qualifier ) { $matched = ! $matched; } return $matched; } /** * Checks which boxes should be loaded for this request. * * @return array */ private function filter_boxes() { $box_ids_to_load = array(); $rules = $this->get_filter_rules(); foreach( $rules as $box_id => $box_rules ) { $matched = false; $comparision = isset( $box_rules['comparision'] ) ? $box_rules['comparision'] : 'any'; // loop through all rules for all boxes foreach ( $box_rules as $rule ) { // skip faulty values (and comparision rule) if( empty( $rule['condition'] ) ) { continue; } $qualifier = isset( $rule['qualifier'] ) ? $rule['qualifier'] : true; $matched = $this->match_rule( $rule['condition'], $rule['value'], $qualifier ); // break out of loop if we've already matched if( $comparision === 'any' && $matched ) { break; } // no need to continue if this rule didn't match if( $comparision === 'all' && ! $matched ) { break; } } // value of $matched at this point determines whether box should be loaded $load_box = $matched; /** * Filters whether a box should be loaded into the page HTML. * * The dynamic portion of the hook, `$box_id`, refers to the ID of the box. Return true if you want to output the box. * * @param boolean $load_box */ $load_box = apply_filters( 'boxzilla_load_box_' . $box_id, $load_box ); /** * Filters whether a box should be loaded into the page HTML. * * @param boolean $load_box * @param int $box_id */ $load_box = apply_filters( 'boxzilla_load_box', $load_box, $box_id ); // if matched, box should be loaded on this page if ( $load_box ) { $box_ids_to_load[] = $box_id; } } return $box_ids_to_load; } /** * Load plugin styles */ public function load_assets() { $pre_suffix = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min'; wp_enqueue_script('boxzilla', $this->plugin->url('/assets/js/script' . $pre_suffix . '.js' ), array(), $this->plugin->version(), true ); $this->pass_box_options(); do_action( 'boxzilla_load_assets', $this ); } /** * Get an array of Box objects. These are the boxes that will be loaded for the current request. * * @return Box[] */ public function get_matched_boxes() { static $boxes; if( is_null( $boxes ) ) { if( count( $this->box_ids_to_load ) === 0 ) { $boxes = array(); return $boxes; } // query Box posts $boxes = get_posts( array( 'post_type' => 'boxzilla-box', 'post_status' => 'publish', 'post__in' => $this->box_ids_to_load, 'numberposts' => -1 ) ); // create `Box` instances out of \WP_Post instances foreach ( $boxes as $key => $box ) { $boxes[ $key ] = new Box( $box ); } } return $boxes; } /** * Create array of Box options and pass it to JavaScript script. */ public function pass_box_options() { // create boxzilla_Global_Options object $plugin_options = $this->options; $boxes = $this->get_matched_boxes(); $data = array( 'testMode' => (bool) $plugin_options['test_mode'], 'boxes' => array_map( function(Box $box) { return $box->get_client_options(); }, $boxes ), ); wp_localize_script( 'boxzilla', 'boxzilla_options', $data ); } }