'] = $operand; } if ( ! empty( $arguments['exclude-tag'] ) || ! empty( $arguments['exclude-category'] ) ) { $operand = 'AND'; // Makes sure tax query exists. if ( empty( $repository_args['tax_query'] ) ) { $repository_args['tax_query'] = []; } $items = [ 'exclude-tag' => 'post_tag', 'exclude-category' => TEC::TAXONOMY, ]; foreach ( $items as $key => $taxonomy ) { if ( empty( $arguments[ $key ] ) ) { continue; } $repo = tribe_events(); $repo->by( 'term_not_in', $taxonomy, $arguments[ $key ] ); $built_query = $repo->build_query(); if ( ! empty( $built_query->query_vars['tax_query'] ) ) { $repository_args['tax_query'] = Arr::merge_recursive_query_vars( $repository_args['tax_query'], $built_query->query_vars['tax_query'] ); } } $repository_args['tax_query']['relation'] = $operand; } if ( isset( $arguments['date'] ) ) { // The date can be used in many ways, so we juggle a bit here. $date_filters = tribe_events()->get_date_filters(); $date_keys = array_filter( $repository_args, static function ( $key ) use ( $date_filters ) { return in_array( $key, $date_filters, true ); }, ARRAY_FILTER_USE_KEY ); if ( count( $date_keys ) === 1 ) { $date_indices = array_keys( $date_keys ); $date_index = reset( $date_indices ); $date_key = $date_keys[ $date_index ]; if ( $date_key === $arguments['date'] ) { // Let's only set it if we are sure. $repository_args[ $date_index ] = $arguments['date']; } else { $repository_args[ $date_index ] = $date_key; } } } if ( ! empty( $arguments['author'] ) ) { if ( ! is_numeric( $arguments['author'] ) ) { $author = get_user_by( 'login', $arguments['author'] ); } else { $author = get_user_by( 'id', $arguments['author'] ); } if ( empty( $author->ID ) ) { // -1, 0, and strings all prevent excluding posts by author. Using PHP_INT_MAX appropriately causes the filter to function. $repository_args['author'] = PHP_INT_MAX; } else { $repository_args['author'] = $author->ID; } } if ( ! empty( $arguments['organizer'] ) ) { if ( ! is_numeric( $arguments['organizer'] ) ) { $organizer_id = tribe_organizers() ->where( 'title', $arguments['organizer'] ) ->per_page( 1 ) ->fields( 'ids' ) ->first(); if ( empty( $organizer_id ) ) { $organizer_id = tribe_organizers() ->where( 'name', $arguments['organizer'] ) ->per_page( 1 ) ->fields( 'ids' ) ->first(); } } else { $organizer_id = $arguments['organizer']; } if ( empty( $organizer_id ) ) { $repository_args['organizer'] = -1; } else { $repository_args['organizer'] = $organizer_id; } } if ( ! empty( $arguments['venue'] ) ) { if ( ! is_numeric( $arguments['venue'] ) ) { $venue_id = tribe_venues() ->where( 'title', $arguments['venue'] ) ->per_page( 1 ) ->fields( 'ids' ) ->first(); if ( empty( $venue_id ) ) { $venue_id = tribe_venues() ->where( 'name', $arguments['venue'] ) ->per_page( 1 ) ->fields( 'ids' ) ->first(); } } else { $venue_id = $arguments['venue']; } if ( empty( $venue_id ) ) { $repository_args['venue'] = -1; } else { $repository_args['venue'] = $venue_id; } } if ( isset( $arguments['featured'] ) ) { $repository_args['featured'] = tribe_is_truthy( $arguments['featured'] ); } if ( isset( $arguments['past'] ) && tribe_is_truthy( $arguments['past'] ) ) { $repository_args['past'] = tribe_is_truthy( $arguments['past'] ); $repository_args['ends_before'] = tribe_end_of_day( current_time( 'mysql' ) ); // Make sure this isn't set to avoid logic conflicts. unset( $repository_args['starts_after'] ); } return $repository_args; } /** * Alters the context of the view based on the shortcode params stored in the database based on the ID. * * @since 5.0.0 * * @param Context $view_context Context for this request. * @param string $view_slug Slug of the view we are building. * @param View $instance Which view instance we are dealing with. * * @return Context Altered version of the context ready for shortcodes. */ public function filter_view_context( Context $view_context, $view_slug, $instance ) { if ( ! $shortcode_id = $view_context->get( 'shortcode' ) ) { return $view_context; } $arguments = $this->get_database_arguments( $shortcode_id ); if ( empty( $arguments ) ) { return $view_context; } /* Week view/widget only. */ if ( false !== stripos( $view_slug, \Tribe\Events\Pro\Views\V2\Views\Week_View::get_view_slug() ) ) { $offset = $this->get_argument( 'week_offset' ); if ( tribe_is_truthy( $offset ) && empty( $view_context->get( 'eventDate' ) ) ) { $start_date = $this->get_argument( 'date', 'now' ); $start_date = Dates::build_date_object( $start_date ); $is_negative = '-' === substr( $offset, 0, 1 ); // Set up for negative weeks. $interval = ( $is_negative ) ? substr( $offset, 1, 1 ) : $offset; $di = Dates::interval( "P{$interval}W" ); $di->invert = absint( $is_negative ); $start_date->add( $di ); $arguments['date'] = $start_date->format( Dates::DBDATEFORMAT ); } elseif ( ! empty( $view_context->get( 'eventDate' ) ) ) { $start_date = Dates::build_date_object( $view_context->get( 'eventDate' ) ); $arguments['date'] = $start_date->format( Dates::DBDATEFORMAT ); } $arguments['week_events_per_day'] = $this->get_argument( 'week_events_per_day' ); } elseif ( false !== stripos( $view_slug, \Tribe\Events\Views\V2\Views\Day_View::get_view_slug() ) ) { /* Day view/widget only. */ $event_date = $view_context->get( 'eventDate' ); if ( ! empty( $event_date ) ) { $arguments['date'] = $event_date; } } else { // Works for month view. $arguments['date'] = $view_context->get( 'tribe-bar-date' ); } return $this->alter_context( $view_context, $arguments ); } /** * Filters the default view in the views manager for shortcodes navigation. * * @since 4.7.9 * * @param string $view_class Fully qualified class name for default view. * * @return string Fully qualified class name for default view of the shortcode in question. */ public function filter_default_url( $view_class ) { if ( tribe_context()->doing_php_initial_state() ) { return $view_class; } // Use the global context here as we should be in the context of an AJAX shortcode request. $shortcode_id = tribe_context()->get( 'shortcode', false ); if ( false === $shortcode_id ) { // If we're not in the context of an AJAX shortcode request, bail. return $view_class; } $shortcode_args = $this->get_database_arguments( $shortcode_id ); if ( ! $shortcode_args['view'] ) { return $view_class; } return tribe( Views_Manager::class )->get_view_class_by_slug( $shortcode_args['view'] ); } /** * Filters the View HTML classes to add some related to PRO features. * * @since 5.0.0 * * @param array $html_classes The current View HTML classes. * @param string $slug The View registered slug. * @param View_Interface $view The View currently rendering. * * @return array The filtered HTML classes. */ public function filter_view_html_classes( $html_classes, $slug, $view ) { $context = $view->get_context(); if ( ! $context instanceof Context ) { return $html_classes; } $shortcode = $context->get( 'shortcode', false ); if ( ! $shortcode ) { return $html_classes; } $shortcode_args = $this->get_database_arguments( $shortcode ); $html_classes[] = 'tribe-events-view--shortcode'; $html_classes[] = 'tribe-events-view--shortcode-' . $shortcode; $container_classes = Arr::get( $shortcode_args, 'container-classes', '' ); if ( ! empty( $container_classes ) ) { $html_classes = array_merge( $html_classes, $container_classes ); } return $html_classes; } /** * Cleans up an array of values as html classes. * * @since 5.5.0 * * @param mixed $value Which classes we are cleaning up. * * @return array Resulting clean html classes. */ public static function validate_array_html_classes( $value ) { if ( ! is_array( $value ) ) { $value = explode( ' ', $value ); } return array_map( 'sanitize_html_class', (array) $value ); } /** * Filters the View data attributes to add some related to PRO features. * * @since 5.0.0 * * @param array $data The current View data attributes classes. * @param string $slug The View registered slug. * @param View_Interface $view The View currently rendering. * * @return array The filtered data attributes. */ public function filter_view_data( $data, $slug, $view ) { if ( ! $view instanceof View_Interface ) { return $data; } $context = $view->get_context(); if ( ! $context instanceof Context ) { return $data; } if ( $shortcode = $context->get( 'shortcode', false ) ) { $data['shortcode'] = $shortcode; } return $data; } /** * Filters the View URL to add the shortcode query arg, if required. * * @since 4.7.9 * @since 5.5.0 Moved this from deprecated Shortcodes\Manager. * * @param string $url The View current URL. * @param bool $canonical Whether to return the canonical version of the URL or the normal one. * @param View_Interface $view This view instance. * * @return string The URL for the view shortcode. */ public function filter_view_url( $url, $canonical, View_Interface $view ) { $context = $view->get_context(); if ( empty( $url ) ) { return $url; } if ( ! $context instanceof Context ) { return $url; } $shortcode_id = $context->get( 'shortcode', false ); if ( false === $shortcode_id ) { return $url; } return add_query_arg( [ 'shortcode' => $shortcode_id ], $url ); } /** * Filters the query arguments array and add the Shortcodes. * * @since 4.7.9 * @since 5.5.0 Moved this from deprecated Shortcodes\Manager. * * @param array $query Arguments used to build the URL. * @param string $view_slug The current view slug. * @param View_Interface $view The current View object. * * @return array Filtered the query arguments for shortcodes. */ public function filter_view_url_query_args( array $query, $view_slug, View_Interface $view ) { $context = $view->get_context(); if ( ! $context instanceof Context ) { return $query; } $shortcode = $context->get( 'shortcode', false ); if ( false === $shortcode ) { return $query; } $query['shortcode'] = $shortcode; return $query; } /** * Filter the breakpoints for the week view widget based on layout. * * @since 5.6.0 * * @param array $breakpoints All breakpoints available. * @param View $view The current View instance being rendered. * * @return array Modified array of available breakpoints. */ public function filter_week_view_breakpoints( $breakpoints, $view ) { $context = $view->get_context(); $widget = $context->get( 'is-widget', false ); $shortcode = $context->get( 'shortcode', false ); if ( false === $widget ) { return $breakpoints; } if ( false === $shortcode ) { return $breakpoints; } $shortcode_args = $this->get_database_arguments( $shortcode ); if ( ! $shortcode_args ) { return $breakpoints; } if ( 'vertical' === $shortcode_args['layout'] ) { // Remove all breakpoints to remain in "mobile view". return []; } elseif ( 'horizontal' === $shortcode_args['layout'] ) { // Simplify breakpoints to remain in "desktop view". unset( $breakpoints['xsmall'] ); $breakpoints['medium'] = 0; return $breakpoints; } // Fallback and space for "auto". return $breakpoints; } /** * Modify the Week events per day of a given view based on arguments from Shortcode. * * @since 5.6.0 * * @param int|string $events_per_day Number of events per day. * @param View $view Current view being rendered. * * @return mixed */ public function filter_week_events_per_day( $events_per_day, $view ) { $context = $view->get_context(); $shortcode = $context->get( 'shortcode', false ); if ( false === $shortcode ) { return $events_per_day; } $shortcode_args = $this->get_database_arguments( $shortcode ); if ( ! $shortcode_args || ! isset( $shortcode_args['count'] ) ) { return $events_per_day; } return $shortcode_args['count']; } /** * Modify the events repository query for the fast-forward link. * * @since 5.14.2 * * @param Tribe__Repository__Interface $next_event Current instance of the events repository class. * @param View_Interface $view The View currently rendering. * * @return Tribe__Repository__Interface $next_event The modified repository instance. */ public function filter_ff_link_next_event( $next_event, $view ) { $shortcode = $view->get_context()->get( 'shortcode' ); if ( empty( $shortcode ) ) { return $next_event; } $args = $this->get_database_arguments( $shortcode ); if ( ! empty( $args['category'] ) ) { $next_event = $next_event->where( 'category', (array) $args['category'] ); } if ( ! empty( $args['tag'] ) ) { $next_event = $next_event->where( 'tag', (array) $args['tag'] ); } if ( ! empty( $args['exclude-category'] ) ) { $next_event = $next_event->where( 'category_not_in', (array) $args['exclude-category'] ); } if ( ! empty( $args['exclude-tag'] ) ) { $next_event = $next_event->where( 'tag__not_in', (array) $args['exclude-tag'] ); } if ( ! empty( $args['author'] ) ) { $next_event = $next_event->where( 'author', $args['author'] ); } if ( ! empty( $args['organizer'] ) ) { $next_event = $next_event->where( 'organizer', $args['organizer'] ); } if ( ! empty( $args['venue'] ) ) { $next_event = $next_event->where( 'venue', $args['venue'] ); } return $next_event; } /** * Allows the user to specify that they want to skip empty views. * * @since 6.5.1 * * @param bool $skip Whether to skip empty views. * * @return bool Whether to skip empty views. */ public function filter_skip_empty( $skip ): bool { $arguments = $this->get_arguments(); if ( ! isset( $arguments['skip-empty'] ) ) { return $skip; } return tribe_is_truthy( $arguments['skip-empty'] ); } }