widget_slug = 'aiopod-widget-episodes'; $this->fields = $this->get_fields(); $this->defaults = $this->get_defaults(); parent::__construct( $this->widget_slug, __( 'AIOPOD - Episodes', 'aio-podcast-studio' ), array( 'classname' => $this->widget_slug, 'description' => __( 'Display a list of your episodes.', 'aio-podcast-studio' ) ) ); } /** * Outputs the content of the widget. * * @since 1.0.0 * @param array $args The array of form elements. * @param array $instance The current instance of the widget. */ public function widget( $args, $instance ) { // Merge incoming $instance array with $defaults if ( count( $instance ) ) { $attributes = array_merge( $this->defaults, $instance ); } else { $attributes = $this->defaults; } $attributes = $this->resolve_attributes( $attributes ); $widget_args = $args; // Enqueue dependencies wp_enqueue_style( AIOPOD_PLUGIN_SLUG . '-public' ); wp_enqueue_script( AIOPOD_PLUGIN_SLUG . '-public' ); // Define the query $args = array( 'post_type' => 'aiopod_episodes', 'posts_per_page' => ! empty( $attributes['limit'] ) ? (int) $attributes['limit'] : 100, 'post_status' => array( 'publish' ) ); if ( ! empty( $attributes['podcast'] ) ) { // Podcast ID $args['post_parent'] = (int) $attributes['podcast']; } if ( 'none' !== $attributes['pagination'] ) { // Pagination $args['paged'] = (int) $attributes['paged']; } // Taxonomy Parameters $tax_queries = array(); if ( ! empty( $attributes['tags'] ) ) { // Tags $tags = array_map( 'intval', ( is_array( $attributes['tags'] ) ? $attributes['tags'] : explode( ',', $attributes['tags'] ) ) ); $tags = array_filter( $tags ); if ( ! empty( $tags ) ) { $tax_queries[] = array( 'taxonomy' => 'aiopod_tags', 'field' => 'term_id', 'terms' => $tags, 'include_children' => false ); } } if ( ! empty( $attributes['participants'] ) ) { // Participants $participants = array_map( 'intval', ( is_array( $attributes['participants'] ) ? $attributes['participants'] : explode( ',', $attributes['participants'] ) ) ); $participants = array_filter( $participants ); if ( ! empty( $participants ) ) { $tax_queries[] = array( 'taxonomy' => 'aiopod_participants', 'field' => 'term_id', 'terms' => $participants, 'include_children' => false ); } } $count_tax_queries = count( $tax_queries ); if ( $count_tax_queries ) { $args['tax_query'] = ( $count_tax_queries > 1 ) ? array_merge( array( 'relation' => 'AND' ), $tax_queries ) : $tax_queries; } // Custom Field (post meta) Parameters $meta_queries = array(); if ( ! empty( $attributes['type'] ) ) { // Episode Type $allowed_types = aiopod_get_episode_types(); $allowed_types = array_keys( $allowed_types ); $types = is_array( $attributes['type'] ) ? $attributes['type'] : explode( ',', $attributes['type'] ); $types = array_map( 'sanitize_key', array_map( 'trim', $types ) ); $types = array_intersect( $types, $allowed_types ); if ( ! empty( $types ) && count( $types ) !== count( $allowed_types ) ) { $meta_queries[] = array( 'key' => 'episode_type', 'value' => $types, 'compare' => 'IN' ); $is_singular = false; } } if ( ! empty( $attributes['featured'] ) ) { // Featured $meta_queries[] = array( 'key' => 'is_featured', 'value' => 1, 'compare' => '=' ); } if ( ! empty( $attributes['season'] ) ) { // Season Number $season = absint( $attributes['season'] ); if ( $season > 0 ) { $meta_queries[] = array( 'key' => 'season_number', 'value' => $season, 'compare' => '=', 'type' => 'NUMERIC' ); } } $count_meta_queries = count( $meta_queries ); if ( $count_meta_queries ) { $args['meta_query'] = ( $count_meta_queries > 1 ) ? array_merge( array( 'relation' => 'AND' ), $meta_queries ) : $meta_queries; } // Order & Orderby Parameters $orderby = sanitize_text_field( $attributes['orderby'] ); $order = sanitize_text_field( $attributes['order'] ); switch ( $orderby ) { case 'popularity': $args['orderby'] = 'meta_value_num'; $args['meta_key'] = 'total_downloads_plays'; $args['order'] = $order; break; case 'downloads': $args['orderby'] = 'meta_value_num'; $args['meta_key'] = 'total_downloads'; $args['order'] = $order; break; case 'plays': $args['orderby'] = 'meta_value_num'; $args['meta_key'] = 'total_plays'; $args['order'] = $order; break; case 'rand': $seed = aiopod_get_orderby_rand_seed(); $args['orderby'] = 'RAND(' . $seed . ')'; break; default: $args['orderby'] = $orderby; $args['order'] = $order; } $args = apply_filters( 'aiopod_query_args', $args, $attributes ); $aiopod_query = new WP_Query( $args ); // Process output echo wp_kses_post( $widget_args['before_widget'] ); if ( ! empty( $instance['title'] ) ) { echo wp_kses_post( $widget_args['before_title'] . apply_filters( 'widget_title', $instance['title'] ) . $widget_args['after_title'] ); } if ( $aiopod_query->have_posts() ) { if ( ( is_front_page() && is_home() ) || 'none' === $attributes['pagination'] ) { $attributes['count'] = $aiopod_query->post_count; } else { $attributes['count'] = $aiopod_query->found_posts; } if ( 'none' !== $attributes['pagination'] ) { $attributes['max_num_pages'] = $aiopod_query->max_num_pages; } } include apply_filters( 'aiopod_load_template', AIOPOD_PLUGIN_DIR . 'public/templates/episodes/episodes-' . sanitize_file_name( $attributes['layout'] ) . '.php', $attributes ); echo wp_kses_post( $widget_args['after_widget'] ); } /** * Processes the widget's options to be saved. * * @since 1.0.0 * @param array $new_instance The new instance of values to be generated via the update. * @param array $old_instance The previous instance of values before the update. */ public function update( $new_instance, $old_instance ) { $instance = $old_instance; $instance['title'] = isset( $new_instance['title'] ) ? strip_tags( $new_instance['title'] ) : ''; foreach ( $this->fields['episodes']['sections'] as $section ) { foreach ( $section['fields'] as $field ) { $field_name = $field['name']; if ( 'number' === $field['type'] ) { if ( ! empty( $new_instance[ $field_name ] ) ) { $instance[ $field_name ] = ( false === strpos( $new_instance[ $field_name ], '.' ) ) ? (int) $new_instance[ $field_name ] : (float) $new_instance[ $field_name ]; } else { $instance[ $field_name ] = 0; } } elseif ( 'url' === $field['type'] ) { $instance[ $field_name ] = ! empty( $new_instance[ $field_name ] ) ? aiopod_sanitize_url( $new_instance[ $field_name ] ) : ''; } elseif ( 'checkbox' === $field['type'] ) { $instance[ $field_name ] = isset( $new_instance[ $field_name ] ) ? (int) $new_instance[ $field_name ] : 0; } elseif ( 'tags' === $field['type'] ) { $instance['tags'] = isset( $new_instance['tags'] ) ? array_map( 'intval', $new_instance['tags'] ) : array(); } elseif ( 'participants' === $field['type'] ) { $instance['participants'] = isset( $new_instance['participants'] ) ? array_map( 'intval', $new_instance['participants'] ) : array(); } else { $instance[ $field_name ] = ! empty( $new_instance[ $field_name ] ) ? sanitize_text_field( $new_instance[ $field_name ] ) : ''; } } } return $instance; } /** * Generates the administration form for the widget. * * @since 1.0.0 * @param array $instance The array of keys and values for the widget. */ public function form( $instance ) { // Parse incoming $instance into an array and merge it with $defaults $instance = wp_parse_args( (array) $instance, $this->defaults ); // Display the admin form if ( ! class_exists( 'Walker_Category_Checklist' ) ) { require_once ABSPATH . 'wp-admin/includes/template.php'; } require_once AIOPOD_PLUGIN_DIR . 'includes/walker-terms-checklist.php'; include AIOPOD_PLUGIN_DIR . 'widgets/forms/episodes.php'; } /** * Resolves attributes into a valid, final configuration. * * This method guarantees that the returned attributes array is safe, * consistent, and ready for query building and rendering. * * @since 1.0.0 * @param array $attributes Raw attributes merged from defaults and user input. * @return array Fully resolved attributes array. */ private function resolve_attributes( $attributes ) { // Title $attributes['title'] = ''; // Layout if ( ! in_array( $attributes['layout'], array( 'grid', 'list', 'compact', 'slider' ) ) ) { $attributes['layout'] = 'grid'; } // Player if ( 'compact' === $attributes['layout'] ) { $attributes['enable_player'] = 1; } if ( ! empty( $attributes['enable_player'] ) ) { $attributes['interactive_mode'] = 1; } // Pagination if ( 'loadmore' !== $attributes['pagination'] ) { $attributes['pagination'] = 'none'; } // Filters if ( empty( $attributes['filters_keyword'] ) && empty( $attributes['filters_tags'] ) && empty( $attributes['filters_season'] ) && empty( $attributes['filters_sort'] ) ) { $attributes['enable_filters'] = 0; } if ( ! empty( $attributes['enable_filters'] ) ) { // Search if ( ! empty( $attributes['filters_keyword'] ) ) { $search_query = isset( $_GET['q'] ) ? sanitize_text_field( wp_unslash( $_GET['q'] ) ) : ''; if ( ! empty( $search_query ) ) { $attributes['search_query'] = $search_query; } } // Tags if ( ! empty( $attributes['filters_tags'] ) ) { $tags = isset( $_GET['topic'] ) ? sanitize_text_field( wp_unslash( $_GET['topic'] ) ) : ''; if ( ! empty( $tags ) ) { $attributes['tags'] = $tags; } } // Season if ( ! empty( $attributes['filters_season'] ) ) { $season = isset( $_GET['season'] ) ? absint( wp_unslash( $_GET['season'] ) ) : 0; if ( $season > 0 ) { $attributes['season'] = $season; } } // Sort if ( ! empty( $attributes['filters_sort'] ) ) { $sort = isset( $_GET['sort'] ) ? sanitize_key( wp_unslash( $_GET['sort'] ) ) : ''; if ( ! empty( $sort ) ) { $sort = array_filter( array_map( 'trim', explode( '-', $sort ) ) ); if ( ! empty( $sort ) ) { $attributes['orderby'] = $sort[0]; if ( count( $sort ) > 1 ) { $attributes['order'] = $sort[1]; } } } } // Pagination if ( 'ajax' === $attributes['filters_mode'] ) { if ( ! empty( $attributes['pagination'] ) ) { $attributes['pagination'] = 'loadmore'; } } } // Return resolved array return $attributes; } /** * Get the form fields. * * @since 1.0.0 * @access private * @return array An associative array of fields. */ private function get_fields() { if ( empty( $this->fields ) ) { $this->fields = aiopod_get_shortcode_fields(); foreach ( $this->fields['episodes']['sections']['display']['fields'] as $index => $field ) { if ( 'pagination' === $field['name'] ) { $this->fields['episodes']['sections']['display']['fields'][ $index ] = array( 'name' => 'pagination', 'label' => __( 'More Button', 'aio-podcast-studio' ), 'description' => __( 'Show or hide the "Load More" button.', 'aio-podcast-studio' ), 'type' => 'select', 'options' => array( 'loadmore' => __( 'Show', 'aio-podcast-studio' ), 'none' => __( 'Hide', 'aio-podcast-studio' ) ), 'value' => 'loadmore' ); } } } return $this->fields; } /** * Get the default attribute values. * * @since 1.0.0 * @access private * @return array An associative array of attributes. */ private function get_defaults() { if ( empty( $this->defaults ) ) { $player_settings = aiopod_get_option( 'aiopod_player_settings' ); foreach ( $this->fields['episodes']['sections'] as $section ) { foreach ( $section['fields'] as $field ) { $this->defaults[ $field['name'] ] = $field['value']; } } $this->defaults['source'] = 'episodes'; $this->defaults['title'] = __( 'Episodes', 'aio-podcast-studio' ); $this->defaults['count'] = 0; $this->defaults['columns'] = 1; $this->defaults['limit'] = 4; $this->defaults['slides_per_view'] = 1; $this->defaults['paged'] = 1; $this->defaults['max_num_pages'] = 1; $this->defaults['include'] = ''; $this->defaults['exclude'] = ''; $this->defaults['type'] = ''; $this->defaults['season'] = ''; $this->defaults['interactive_mode'] = 0; $this->defaults['enable_player'] = 0; $this->defaults['player_autoadvance'] = $player_settings['autoadvance']; $this->defaults['player_loop'] = $player_settings['loop']; $this->defaults['player_hotkeys'] = $player_settings['hotkeys']; $this->defaults['pagination'] = 'loadmore'; $this->defaults['hide_if_empty'] = 0; } return $this->defaults; } }