config = srpp_default_config(); if ( $post ) { $post = get_post( $post ); } if ( $post && SRPP_CPT === $post->post_type ) { $this->id = (int) $post->ID; $this->title = $post->post_title; $this->status = $post->post_status; $stored = get_post_meta( $post->ID, self::META_KEY, true ); if ( is_array( $stored ) ) { $this->config = self::merge( $stored ); } } } /** * Merge stored config over defaults (one level deep for nested arrays). * * @param array $stored Stored config. * @return array */ public static function merge( $stored ) { $config = wp_parse_args( (array) $stored, srpp_default_config() ); $config['weights'] = wp_parse_args( isset( $stored['weights'] ) ? (array) $stored['weights'] : array(), srpp_default_weights() ); $config['style'] = wp_parse_args( isset( $stored['style'] ) ? (array) $stored['style'] : array(), srpp_default_style() ); return $config; } /** * Config getter with dot-free key. * * @param string $key Key. * @param mixed $default Default. * @return mixed */ public function opt( $key, $default = null ) { return isset( $this->config[ $key ] ) ? $this->config[ $key ] : $default; } /** * Is a field enabled for this Display? * * @param string $field Field key. * @return bool */ public function has_field( $field ) { return in_array( $field, (array) $this->opt( 'fields', array() ), true ); } /** * Is this Display active? * * @return bool */ public function is_active() { return 'publish' === $this->status; } /* --------------------------------------------------------------------- * Storage * ------------------------------------------------------------------ */ /** * Register the rules post type. */ public static function register_post_type() { register_post_type( SRPP_CPT, array( 'label' => __( 'Related Content Displays', 'advanced-css3-related-posts-widget' ), 'public' => false, 'show_ui' => false, 'show_in_menu' => false, 'exclude_from_search' => true, 'publicly_queryable' => false, 'hierarchical' => false, 'rewrite' => false, 'query_var' => false, 'can_export' => true, 'supports' => array( 'title', 'page-attributes' ), 'capability_type' => 'post', 'map_meta_cap' => true, ) ); } /** * Fetch one Display by ID. * * @param int|string $id Display ID. * @return SRPP_Display|null */ public static function get( $id ) { $post = get_post( absint( $id ) ); if ( ! $post || SRPP_CPT !== $post->post_type ) { return null; } return new self( $post ); } /** * Fetch all Displays. * * @param array $args Query overrides. * @return SRPP_Display[] */ public static function all( $args = array() ) { $args = wp_parse_args( $args, array( 'post_type' => SRPP_CPT, 'post_status' => array( 'publish', 'draft' ), 'posts_per_page' => 200, 'orderby' => array( 'menu_order' => 'ASC', 'date' => 'ASC' ), 'no_found_rows' => true, ) ); $posts = get_posts( $args ); $out = array(); foreach ( $posts as $post ) { $out[] = new self( $post ); } return $out; } /** * Active Displays only, cached per request. * * @return SRPP_Display[] */ public static function active() { static $cache = null; if ( null === $cache ) { $cache = self::all( array( 'post_status' => 'publish' ) ); } return $cache; } /** * Create or update a Display. * * @param int $id Display ID (0 to create). * @param array $data title, status, config, order. * @return int|WP_Error */ public static function save( $id, $data ) { $postarr = array( 'ID' => absint( $id ), 'post_type' => SRPP_CPT, 'post_title' => isset( $data['title'] ) ? sanitize_text_field( $data['title'] ) : __( 'Untitled display', 'advanced-css3-related-posts-widget' ), 'post_status' => ( isset( $data['status'] ) && 'draft' === $data['status'] ) ? 'draft' : 'publish', ); if ( isset( $data['order'] ) ) { $postarr['menu_order'] = (int) $data['order']; } if ( $postarr['ID'] ) { $result = wp_update_post( $postarr, true ); } else { unset( $postarr['ID'] ); $result = wp_insert_post( $postarr, true ); } if ( is_wp_error( $result ) ) { return $result; } if ( isset( $data['config'] ) ) { update_post_meta( $result, self::META_KEY, self::sanitize_config( $data['config'] ) ); } SRPP_Cache::flush_all(); return $result; } /** * Delete a Display. * * @param int $id Display ID. * @return bool */ public static function delete( $id ) { $post = get_post( absint( $id ) ); if ( ! $post || SRPP_CPT !== $post->post_type ) { return false; } wp_delete_post( $post->ID, true ); SRPP_Cache::flush_all(); return true; } /** * Duplicate a Display. * * @param int $id Display ID. * @return int|WP_Error */ public static function duplicate( $id ) { $display = self::get( $id ); if ( ! $display ) { return new WP_Error( 'srpp_missing', __( 'Display not found.', 'advanced-css3-related-posts-widget' ) ); } return self::save( 0, array( /* translators: %s: display name */ 'title' => sprintf( __( '%s (copy)', 'advanced-css3-related-posts-widget' ), $display->title ), 'status' => 'draft', 'config' => $display->config, ) ); } /* --------------------------------------------------------------------- * Sanitization * ------------------------------------------------------------------ */ /** * Sanitize a raw config array coming from the admin form. * * @param array $raw Raw input. * @return array */ public static function sanitize_config( $raw ) { $raw = (array) $raw; $defaults = srpp_default_config(); $out = array(); $slug_lists = array( 'target_types', 'relate_by', 'cond_types', 'fields', 'cond_devices' ); foreach ( $slug_lists as $key ) { $values = isset( $raw[ $key ] ) ? (array) $raw[ $key ] : $defaults[ $key ]; $out[ $key ] = array_values( array_unique( array_filter( array_map( 'sanitize_key', $values ) ) ) ); } $id_lists = array( 'manual_ids', 'cond_ids_include', 'cond_ids_exclude', 'ex_authors', 'ex_ids' ); foreach ( $id_lists as $key ) { $out[ $key ] = srpp_int_list( isset( $raw[ $key ] ) ? $raw[ $key ] : array() ); } $ints = array( 'items' => array( 1, 60 ), 'cols_d' => array( 1, 8 ), 'cols_t' => array( 1, 6 ), 'cols_m' => array( 1, 4 ), 'gap' => array( 0, 80 ), 'placement_value' => array( 0, 100 ), 'repeat_every' => array( 0, 50 ), 'repeat_max' => array( 1, 10 ), 'priority' => array( 1, 999 ), 'recency_days' => array( 0, 3650 ), 'excerpt_length' => array( 0, 80 ), 'cache_ttl' => array( 0, YEAR_IN_SECONDS ), ); foreach ( $ints as $key => $range ) { $value = isset( $raw[ $key ] ) ? (int) $raw[ $key ] : (int) $defaults[ $key ]; $out[ $key ] = max( $range[0], min( $range[1], $value ) ); } $bools = array( 'ex_current', 'ex_password', 'ex_private', 'ex_noindex', 'ex_outofstock', 'no_duplicates', 'cross_match' ); foreach ( $bools as $key ) { $out[ $key ] = ! empty( $raw[ $key ] ) ? 1 : 0; } $keys = array( 'selection_mode', 'engine', 'fallback', 'order', 'placement', 'layout', 'template', 'image_ratio', 'cond_logic', 'cond_users', 'heading_align' ); foreach ( $keys as $key ) { $out[ $key ] = isset( $raw[ $key ] ) ? sanitize_key( $raw[ $key ] ) : $defaults[ $key ]; } $out['heading'] = isset( $raw['heading'] ) ? sanitize_text_field( $raw['heading'] ) : $defaults['heading']; $out['button_text'] = isset( $raw['button_text'] ) ? sanitize_text_field( $raw['button_text'] ) : $defaults['button_text']; $tag = isset( $raw['heading_tag'] ) ? strtolower( sanitize_key( $raw['heading_tag'] ) ) : 'h3'; $out['heading_tag'] = in_array( $tag, array( 'h2', 'h3', 'h4', 'h5', 'div', 'span' ), true ) ? $tag : 'h3'; $out['link_target'] = ( isset( $raw['link_target'] ) && '_blank' === $raw['link_target'] ) ? '_blank' : '_self'; $out['cond_price_min'] = isset( $raw['cond_price_min'] ) && '' !== $raw['cond_price_min'] ? (float) $raw['cond_price_min'] : ''; $out['cond_price_max'] = isset( $raw['cond_price_max'] ) && '' !== $raw['cond_price_max'] ? (float) $raw['cond_price_max'] : ''; // Weights. $out['weights'] = array(); foreach ( srpp_default_weights() as $key => $default ) { $value = isset( $raw['weights'][ $key ] ) ? (int) $raw['weights'][ $key ] : $default; $out['weights'][ $key ] = max( 0, min( 100, $value ) ); } // Style tokens. $out['style'] = array(); foreach ( srpp_default_style() as $key => $default ) { $value = isset( $raw['style'][ $key ] ) ? $raw['style'][ $key ] : $default; if ( is_int( $default ) ) { $out['style'][ $key ] = max( 0, min( 120, (int) $value ) ); } elseif ( 0 === strpos( (string) $value, '#' ) ) { $out['style'][ $key ] = sanitize_hex_color( $value ); } else { $out['style'][ $key ] = sanitize_text_field( $value ); } } // Term conditions: [ ['taxonomy' => 'category', 'terms' => [1,2]] ]. foreach ( array( 'cond_include', 'cond_exclude', 'ex_terms' ) as $key ) { $out[ $key ] = self::sanitize_term_rules( isset( $raw[ $key ] ) ? $raw[ $key ] : array() ); } return $out; } /** * Sanitize taxonomy/term rule sets. * * @param mixed $raw Raw rules. * @return array */ private static function sanitize_term_rules( $raw ) { $out = array(); foreach ( (array) $raw as $rule ) { if ( empty( $rule['taxonomy'] ) ) { continue; } $terms = srpp_int_list( isset( $rule['terms'] ) ? $rule['terms'] : array() ); if ( empty( $terms ) ) { continue; } $out[] = array( 'taxonomy' => sanitize_key( $rule['taxonomy'] ), 'terms' => $terms, ); } return $out; } }