get_action_keys() as $key ) { $key_underscore = self::_key( $key ); add_action( 'wp_ajax_' . $this->action_string( $key ), array( $this, 'action_' . $key_underscore ) ); } } /** * Create a new WOMeta instance if necessary. * * @return WOMeta */ public function meta() { if ( ! $this->wo_meta ) { $this->wo_meta = new WOMeta( AdCommander::ns() ); } return $this->wo_meta; } /** * Get necessary action keys, which will be used to create wp_ajax hooks. * * @return array */ private function get_action_keys() { return array( 'load-conditions', 'load-ac-results', ); } /** * Creates an array of all of the necessary actions. * * @return array */ public function get_ajax_actions() { $actions = array(); foreach ( $this->get_action_keys() as $key ) { $actions[ self::_key( $key ) ] = array( 'action' => $this->action_string( $key ), 'security' => wp_create_nonce( $this->nonce_string( $key ) ), ); } return $actions; } /** * Enqueue admin targeting scripts. * * @return string */ public function enqueue() { wp_enqueue_script( 'jquery' ); wp_enqueue_script( 'jquery-ui-core' ); $repeater_handle = $this->meta()->repeater_enqueue(); $handle = Util::ns( 'targeting' ); wp_register_script( $handle, AdCommander::assets_url() . 'js/targeting.js', array( 'jquery', 'jquery-ui-core', $repeater_handle, ), AdCommander::version(), array( 'in_footer' => true ) ); wp_enqueue_script( $handle ); Util::enqueue_script_data( $handle, array( 'ajaxurl' => admin_url( 'admin-ajax.php' ), 'actions' => $this->get_ajax_actions(), 'page_ac_placeholder' => self::page_autocomplete_placeholder_text(), 'notfound' => self::no_items_text(), ) ); return $handle; } /** * Respond to an ajax request for loading conditions. * * @return void */ public function action_load_conditions() { $action = 'load-conditions'; check_ajax_referer( $this->nonce_string( $action ), 'security' ); if ( ! current_user_can( AdCommander::capability() ) ) { wp_die(); } if ( ! isset( $_REQUEST['target'] ) || ! isset( $_REQUEST['targeting_type'] ) ) { wp_die(); } $target = isset( $_REQUEST['target'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['target'] ) ) : null; $type = isset( $_REQUEST['targeting_type'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['targeting_type'] ) ) : null; $args = array( 'action' => $action, 'conditions' => self::get_comparisons( $target ), ); if ( $type === 'content' ) { if ( in_array( $target, array_keys( TargetingMeta::allowed_content_targets() ) ) ) { $args['value_type'] = TargetingContent::value_type( $target ); $args['values'] = TargetingContent::values( $target ); $args['args'] = TargetingContent::args( $target ); } } elseif ( $type === 'visitor' ) { if ( in_array( $target, array_keys( TargetingMeta::allowed_visitor_targets() ) ) ) { $args['value_type'] = TargetingVisitor::value_type( $target ); if ( is_array( $args['value_type'] ) ) { $args['values'] = array(); foreach ( $args['value_type'] as $key => $value_type ) { $args['values'][ $key ] = TargetingVisitor::values( $target . '_' . $key ); } $args['value_type_labels'] = TargetingVisitor::value_type_labels( $target ); } else { $args['values'] = TargetingVisitor::values( $target ); } $args['args'] = TargetingVisitor::args( $target ); } } wp_send_json_success( $args ); wp_die(); } /** * Respond to an ajax request for loading autocomplete results. * * @return void */ public function action_load_ac_results() { $action = 'load-ac-results'; check_ajax_referer( $this->nonce_string( $action ), 'security' ); if ( ! current_user_can( AdCommander::capability() ) ) { wp_die(); } if ( ! isset( $_REQUEST['target'] ) || ! isset( $_REQUEST['search_term'] ) ) { wp_die(); } $search_term = isset( $_REQUEST['search_term'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['search_term'] ) ) : null; $target = isset( $_REQUEST['target'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['target'] ) ) : null; if ( $search_term && $target ) { switch ( $target ) { case 'parent_page': $results = $this->get_pages_by_id_or_title( $search_term ); break; case 'specific_post': $results = $this->get_all_posts_by_id_or_title( $search_term ); break; } wp_send_json_success( array( 'action' => $action, 'results' => $results, ) ); } wp_die(); } /** * Query pages that match the given search term (text or ID). * * @param string|int $search_term The search term from autocomplete. * * @return array */ private function get_pages_by_id_or_title( $search_term ) { return $this->get_posts_by_id_or_title( $search_term, 'page' ); } /** * Query any post type that matches the given search term (text or ID). * * @param string|int $search_term The search term from autocomplete. * * @return array */ private function get_all_posts_by_id_or_title( $search_term ) { return $this->get_posts_by_id_or_title( $search_term, array_filter( get_post_types( array( 'public' => true ) ), function ( $post_type ) { return ! in_array( $post_type, array( AdCommander::posttype_ad(), AdCommander::posttype_placement() ) ); } ) ); } /** * Query specified post type that matches the given search term (text or ID). * * @param string|int $search_term The search term from autocomplete. * @param mixed $post_types The post types to query. * * @return array */ private function get_posts_by_id_or_title( $search_term, $post_types ) { if ( is_numeric( $search_term ) ) { $results = Query::by_id( $search_term, Util::any_post_status(), $post_types ); } else { $results = Query::by_title( $search_term, Util::any_post_status(), $post_types ); } return $this->format_results_for_json( $results ); } /** * Format WP_Query results to send as json response. * * @param array $results The results of a query. * @param string $key The object key to use as the array result key. * @param string $value The object key to use as the array result value. * * @return array */ private function format_results_for_json( $results, $key = 'ID', $value = 'post_title' ) { $formatted = array(); if ( $results && ! empty( $results ) && ! is_wp_error( $results ) ) { $results = Util::arrayify( $results ); foreach ( $results as $result ) { if ( ! isset( $result->$key ) || ! isset( $result->$value ) ) { continue; } $formatted[] = array( 'id' => $result->ID, 'title' => $result->$value, ); } } return $formatted; } /** * Input placeholder text for autocompletes. * * @return string */ public static function page_autocomplete_placeholder_text() { return __( 'page id or title', 'ad-commander' ); } /** * Text when no checks are available. * * @return string */ public static function no_items_text() { return __( 'No items found', 'ad-commander' ); } /** * Returns the allowed conditions for a specific content target. * * @param string $target The current target. * * @return array */ public static function get_comparisons( $target ) { $conditions = array(); foreach ( TargetingMeta::allowed_comparisons() as $key => $label ) { $conditions[ $key ] = array( $key => $label ); } switch ( $target ) { case 'content_age': return array_merge( $conditions['older_than'], $conditions['newer_than'] ); break; case 'pagination': return array_merge( $conditions['equals'], $conditions['less_than'], $conditions['greater_than'] ); break; case 'geolocation_radius': case 'browser_width': case 'site_impressions': return array_merge( $conditions['less_than'], $conditions['greater_than'] ); break; case 'max_impressions': case 'max_clicks': return $conditions['less_than']; break; case 'post_content': case 'url': case 'session_referrer_url': case 'browser_user_agent': return array_merge( $conditions['contains'], $conditions['does_not_contain'], $conditions['starts_with'], $conditions['does_not_start_with'], $conditions['ends_with'], $conditions['does_not_end_with'] ); break; default: return array_merge( $conditions['is'], $conditions['is_not'] ); break; } } /** * Make the condition values for display in the admin. * * @param string $value_type The type of values to display. * @param string $key The key for this field. * @param mixed $values The values for this field. * @param mixed $current_condition_values The currently selected values. * @param array $args The arguments for this field. * * @return string */ private function make_condition_values( $value_type, $key, $values = '', $current_condition_values = '', $args = array() ) { $condition_values = ''; if ( $value_type === 'select' ) { $condition_values = $this->meta()->select( $key, $values, $current_condition_values, array_merge( $args, array( 'display' => false ) ) ); } elseif ( $value_type === 'checkgroup' ) { $condition_values = $this->meta()->checkgroup( $key, $values, $current_condition_values, array_merge( $args, array( 'display' => false ) ) ); if ( ! $condition_values ) { $condition_values = '' . self::no_items_text() . ''; } } elseif ( $value_type === 'number' || $value_type === 'text' ) { $condition_values = $this->meta()->input( $key, $current_condition_values, $value_type, array_merge( $args, array( 'display' => false ) ) ); } elseif ( $value_type === 'words' ) { $condition_values = '' . esc_html( $values ) . ''; } elseif ( $value_type === 'autocomplete' ) { $condition_values = $this->meta()->input( $key, '', 'text', array_merge( $args, array( 'classes' => 'init-ac', 'placeholder' => $this->page_autocomplete_placeholder_text(), 'display' => false, ) ) ); if ( $current_condition_values && ! empty( $current_condition_values ) ) { if ( ! is_array( $current_condition_values ) ) { $current_condition_values = array( $current_condition_values ); } $current_selected_autocomplete_ids = implode( ',', array_map( 'intval', $current_condition_values ) ); $condition_values .= $this->meta()->input( str_replace( '[values]', '[selected_post_ids]', $key ), $current_selected_autocomplete_ids, 'hidden', array( 'display' => false, ) ); $condition_values .= '