this could be an empty string if the View does not have a next. * * @return string The filtered previous URL. */ protected function filter_view_url( $canonical, $url ) { /** * Filters the URL returned for a View. * * @since 4.9.3 * * @param string $url The View current URL. * @param bool $canonical Whether the URL is a canonical one or not. * @param View_Interface $this This view instance. */ $url = apply_filters( 'tribe_events_views_v2_view_url', $url, $canonical, $this ); $view_slug = static::$view_slug; /** * Filters the URL returned for a specific View. * * @since 4.9.11 * * @param string $url The View current URL. * @param bool $canonical Whether the URL is a canonical one or not. * @param View_Interface $this This view instance. */ $url = apply_filters( "tribe_events_views_v2_view_{$view_slug}_url", $url, $canonical, $this ); return $url; } /** * Filters the previous (page, event, etc.) URL returned for a specific View. * * @since 4.9.3 * * @param bool $canonical Whether the normal or canonical version of the next URL is being requested. * @param string $url The previous URL, this could be an empty string if the View does not have a next. * * @return string The filtered previous URL. */ protected function filter_prev_url( $canonical, $url ) { /** * Filters the previous (page, event, etc.) URL returned for a View. * * @since 4.9.3 * * @param string $url The View previous (page, event, etc.) URL. * @param bool $canonical Whether the URL is a canonical one or not. * @param View_Interface $this This view instance. */ $url = apply_filters( 'tribe_events_views_v2_view_prev_url', $url, $canonical, $this ); $view_slug = static::$view_slug; /** * Filters the previous (page, event, etc.) URL returned for a specific View. * * @since 4.9.11 * * @param string $url The View previous (page, event, etc.) URL. * @param bool $canonical Whether the URL is a canonical one or not. * @param View_Interface $this This view instance. */ $url = apply_filters( "tribe_events_views_v2_view_{$view_slug}_prev_url", $url, $canonical, $this ); return $url; } /** * Filters the next (page, event, etc.) URL returned for a specific View. * * @since 4.9.3 * * @param bool $canonical Whether the normal or canonical version of the next URL is being requested. * @param string $url The next URL, this could be an empty string if the View does not have a next. * * @return string The filtered next URL. */ protected function filter_next_url( $canonical, $url ) { /** * Filters the next (page, event, etc.) URL returned for a View. * * @since 4.9.3 * * @param string $url The View next (page, event, etc.) URL. * @param bool $canonical Whether the URL is a canonical one or not. * @param View_Interface $this This view instance. */ $url = apply_filters( 'tribe_events_views_v2_view_next_url', $url, $canonical, $this ); $view_slug = static::$view_slug; /** * Filters the next (page, event, etc.) URL returned for a specific View. * * @since 4.9.11 * * @param string $url The View next (page, event, etc.) URL. * @param bool $canonical Whether the URL is a canonical one or not. * @param View_Interface $this This view instance. */ $url = apply_filters( "tribe_events_views_v2_view_{$view_slug}_next_url", $url, $canonical, $this ); return $url; } /** * {@inheritDoc} */ public function found_post_ids() { $events = $this->repository->get_ids(); if ( $this->has_next_event( $events ) ) { array_pop( $events ); } return $events; } /** * {@inheritDoc} */ public static function is_publicly_visible() { return static::$publicly_visible; } /** * Sets the has_next_event boolean flag, which determines if we have events in the next page. * * This flag is required due to being required to optimize the determination of whether * there are future events, we increased events_per_page by +1 during setup_repository_args. Because of that * if the number of events returned are greater than events_per_page, we need to * pop an element off the end and set a boolean. * * @since 5.0.0 * * @param boolean $value Which value will be set to has_next_event, will be casted as boolean. * * @return mixed Value passed after being saved and casted as boolean. */ public function set_has_next_event( $value ) { return $this->has_next_event = (bool) $value; } /** * Determines from a given array of events if we have next events or not. * * @since 5.0.0 * * @param array $events Array that will be counted to verify if we have events. * @param boolean $overwrite_flag If we should overwrite the flag when we discover the result. * * @return mixed Weather the array of events has a next page. */ public function has_next_event( array $events, $overwrite_flag = true ) { $has_next_events = count( $events ) > $this->get_context()->get( 'events_per_page', 12 ); if ( (bool) $overwrite_flag ) { $this->set_has_next_event( $has_next_events ); } return $has_next_events; } /** * Get if we have events in the next page. * * @since 5.16.0 * * @return boolean Weather the View has events in the next page. */ public function get_has_next_event() { return (boolean) $this->has_next_event; } /** * Sets up the View template variables. * * @since 4.9.4 * @since 5.2.1 Add the `rest_method` to the template variables. * * @return array An array of Template variables for the View Template. */ protected function setup_template_vars() { if ( empty( $this->repository_args ) ) { $this->repository_args = $this->get_repository_args(); $this->repository->by_args( $this->repository_args ); } // Pre build query, so we can determine the unique hash for this event query. $this->repository->build_query(); $repository_query_hash = $this->repository->hash(); // Check if we memoized this query and if memoize was enabled. $memoize_key = tribe_cache()->make_key( $this->context->to_array(), __METHOD__ ) . $repository_query_hash; $vars = tribe_cache()->get( $memoize_key, Tribe__Cache_Listener::TRIGGER_SAVE_POST, null, Tribe__Cache::NON_PERSISTENT ); if ( ! empty( $vars ) ) { return $vars; } $events = (array) $this->repository->all(); $events = array_filter( $events, static function ( $event ) { return $event instanceof \WP_Post; } ); Taxonomy::prime_term_cache( $events ); Event::prime_cache( $events ); /** * Action triggered right after pulling all the Events from the DB, allowing cache to be primed correctly. * * @since 6.0.0 * * @param array $events Which events were just selected. * @param self $view Which view we are dealing with. */ do_action( 'tec_events_views_v2_after_get_events', $events, $this ); $is_paginated = isset( $this->repository_args['posts_per_page'] ) && - 1 !== $this->repository_args['posts_per_page']; /* * To optimize the determination of whether there are future events, we * increased events_per_page by +1 during setup_repository_args. Because of that * if the number of events returned is greater than events_per_page, we need to * pop an element off the end and set a boolean. * * @since 5.0.0 */ if ( $is_paginated && $this->has_next_event( $events ) ) { array_pop( $events ); } $this->setup_messages( $events ); $today_url = $this->get_today_url( true ); $today = $this->context->get( 'today', 'today' ); // The "Today" button title and aria-label text. $today_title = _x( 'Click to select today\'s date', 'The default title text for the today button.', 'the-events-calendar' ); /** * Allows filtering of the "Today" button title and aria-label. * * @since 6.0.2 * * @param string $today_title The title string. * @param \Tribe\Events\Views\V2\View_Interface $view The View currently rendering. */ $today_title = apply_filters( 'tec_events_today_button_title', $today_title, $this ); $view_slug = static::$view_slug; /** * Allows filtering of the "Today" button title and aria-label. * * @since 6.0.2 * * @param string $today_title The title string. * @param \Tribe\Events\Views\V2\View_Interface $view The View currently rendering. */ $today_title = apply_filters( "tec_events_view_{$view_slug}_today_button_title", $today_title, $this ); $today_label = tec_events_get_today_button_label( $this ); $event_date = $this->context->get( 'event_date', false ); // Set the URL event date only if it's not empty or "now": both are implicit, default, date selections. $url_event_date = ( ! empty( $event_date ) && 'now' !== $event_date ) ? Dates::build_date_object( $event_date )->format( Dates::DBDATEFORMAT ) : false; /** @var Rest_Endpoint $endpoint */ $endpoint = tribe( Rest_Endpoint::class ); $template_vars = [ 'title' => $this->get_title( $events ), 'events' => $events, 'url' => $this->get_url( true ), 'prev_url' => $this->prev_url( true ), 'next_url' => $this->next_url( true ), 'url_event_date' => $url_event_date, 'bar' => [ 'keyword' => $this->context->get( 'keyword', '' ), 'date' => $this->context->get( 'event_date', '' ), ], 'today' => $today, 'now' => $this->context->get( 'now', 'now' ), 'request_date' => Dates::build_date_object( $this->context->get( 'event_date', $today ) ), 'rest_url' => $endpoint->get_url(), 'rest_method' => $endpoint->get_method(), 'rest_nonce' => '', // For backwards compatibility in views. No longer used. 'should_manage_url' => $this->should_manage_url, 'today_url' => $today_url, 'today_title' => $today_title, 'today_label' => $today_label, 'prev_label' => $this->get_link_label( $this->prev_url( false ) ), 'next_label' => $this->get_link_label( $this->next_url( false ) ), 'date_formats' => (object) [ 'compact' => Dates::datepicker_formats( tribe_get_option( 'datepickerFormat' ) ), 'month_and_year_compact' => Dates::datepicker_formats( 'm' . tribe_get_option( 'datepickerFormat' ) ), 'month_and_year' => tribe_get_date_option( 'monthAndYearFormat', 'F Y' ), 'time_range_separator' => tribe_get_date_option( 'timeRangeSeparator', ' - ' ), 'date_time_separator' => tribe_get_date_option( 'dateTimeSeparator', ' @ ' ), ], 'messages' => $this->get_messages( $events ), 'start_of_week' => get_option( 'start_of_week', 0 ), 'header_title' => $this->get_header_title(), 'header_title_element' => $this->get_header_title_element(), 'content_title' => $this->get_content_title(), 'breadcrumbs' => $this->get_breadcrumbs(), 'before_events' => tribe( Advanced_Display::class )->get_before_events_html( $this ), 'after_events' => tribe( Advanced_Display::class )->get_after_events_html( $this ), 'display_events_bar' => $this->filter_display_events_bar( $this->display_events_bar ), /** * Allow filtering to determine whether or not to apply the `tribeDisableTribeBar` setting on the Events Manager page. * * @since 5.12.1 */ 'disable_event_search' => apply_filters( 'tec_events_views_v2_disable_tribe_bar', tribe_get_option( 'tribeDisableTribeBar', false ) ), 'live_refresh' => tribe_is_truthy( 'automatic' === tribe_get_option( 'liveFiltersUpdate', 'automatic' ) ), 'ical' => $this->get_ical_data(), 'container_classes' => $this->get_html_classes(), 'container_data' => $this->get_container_data(), 'is_past' => 'past' === $this->context->get( 'event_display_mode', false ), 'breakpoints' => $this->get_breakpoints(), 'breakpoint_pointer' => $this->get_breakpoint_pointer(), 'is_initial_load' => $this->context->doing_php_initial_state(), 'public_views' => $this->get_public_views( $url_event_date ), 'show_latest_past' => $this->should_show_latest_past_events_view(), ]; if ( ! $this->config->get( 'TEC_NO_MEMOIZE_VIEW_VARS' ) ) { tribe_cache()->set( $memoize_key, $template_vars, Tribe__Cache::NON_PERSISTENT, Tribe__Cache_Listener::TRIGGER_SAVE_POST ); } return $template_vars; } /** * Filters the repository arguments that will be used to set up the View repository instance. * * @since 4.9.5 * * @param array $repository_args The repository arguments that will be used to set up the View repository instance. * @param Context|null $context Either a specific Context or `null` to use the View current Context. * * @return array The filtered repository arguments. */ protected function filter_repository_args( array $repository_args, Context $context = null ) { $context = null !== $context ? $context : $this->context; /** * Filters the repository args for a View. * * @since 4.9.5 * * @param array $repository_args An array of repository arguments that will be set for all Views. * @param Context $context The current render context object. * @param View_Interface $this The View that will use the repository arguments. */ $repository_args = apply_filters( 'tribe_events_views_v2_view_repository_args', $repository_args, $context, $this ); $view_slug = static::$view_slug; /** * Filters the repository args for a specific View. * * @since 4.9.5 * * @param array $repository_args An array of repository arguments that will be set for a specific View. * @param Context $context The current render context object. * @param View_Interface $this The View that will use the repository arguments. */ $repository_args = apply_filters( "tribe_events_views_v2_view_{$view_slug}_repository_args", $repository_args, $context, $this ); return $repository_args; } /** * Returns the View request URI. * * This value can be used to set the `$_SERVER['REQUEST_URI']` global when rendering the View to make sure WordPress * functions relying on that value will work correctly. * * @since 4.9.5 * * @return string The View request URI, a value suitable to be used to set the `$_SERVER['REQUEST_URI']` value. */ protected function get_request_uri() { $plain_url = (string) $this->get_url(); $clean_url = $this->rewrite->get_clean_url( $plain_url ); $url_frags = wp_parse_url( $clean_url ); $path = isset( $url_frags['path'] ) ? trim( $url_frags['path'], '/' ) . '/' : ''; $query = isset( $url_frags['query'] ) ? '?' . $url_frags['query'] : ''; $fragment = isset( $url_frags['fragment'] ) ? '#' . $url_frags['fragment'] : ''; $request_uri = '/' . $path . $query . $fragment; /** * Allows filtering the Views request URI that will be used to set up the loop. * * @since 5.2.1 * * @param string $request_uri The parsed request URI. */ $request_uri = apply_filters( 'tribe_events_views_v2_request_uri', $request_uri ); return $request_uri; } /** * {@inheritDoc} */ public function get_template_slug() { if ( null !== $this->template_slug ) { return $this->template_slug; } return static::$view_slug; } /** * {@inheritDoc} */ public function set_template_slug( $template_slug ) { $this->template_slug = $template_slug; $this->template->set( 'slug', $template_slug ); } /** * {@inheritDoc} */ public function get_template_vars() { return $this->filter_template_vars( $this->setup_template_vars() ); } /** * {@inheritDoc} */ public function get_today_url( $canonical = false ) { $to_remove = [ 'tribe-bar-date', 'paged', 'page', 'eventDate', 'tribe_event_display' ]; // While we want to remove the date query vars, we want to keep any other query var. $query_args = $this->url->get_query_args(); // Handle the `eventDisplay` query arg due to its particular usage to indicate the mode too. $query_args['eventDisplay'] = static::$view_slug; $category = $this->context->get( 'event_category', false ); if ( is_array( $category ) ) { $category = Arr::to_list( reset( $category ) ); $query_args['tribe_events_cat'] = $category; } $query_args = $this->filter_query_args( $query_args, $canonical ); $ugly_url = add_query_arg( $query_args, $this->get_url( false ) ); $ugly_url = remove_query_arg( $to_remove, $ugly_url ); if ( ! $canonical ) { return $ugly_url; } return $this->rewrite->get_canonical_url( $ugly_url ); } /** * Builds the link label to use from the URL. * * This is usually used to build the next and prev link URLs labels. * Extending classes can customize the format of the the label by overriding the `get_label_format` method. * * @todo @bordoni move this method to a supporting class. * * @see View::get_label_format(), the method child classes should override to customize the link label format. * * @since 4.9.9 * * @param string $url The input URL to build the link label from. * * @return string The formatted and localized, but not HTML escaped, link label. */ public function get_link_label( $url ) { if ( empty( $url ) ) { return ''; } $url_query = parse_url( $url, PHP_URL_QUERY ); if ( empty( $url_query ) ) { return ''; } parse_str( $url_query, $args ); $date = Arr::get_first_set( $args, [ 'eventDate', 'tribe-bar-date' ], false ); if ( false === $date ) { return ''; } $date_object = Dates::build_date_object( $date ); $format = $this->get_label_format(); /** * Filters the `date` format that will be used to produce a View link label for a View. * * @since 4.9.11 * * @param string $format The label format the View will use to product a View link label; e.g. the * previous and next links. * @param \DateTime $date The date object that is being used to build the label. * @param View $view This View instance. */ $format = apply_filters( "tribe_events_views_v2_view_link_label_format", $format, $this, $date ); $view_slug = static::$view_slug; /** * Filters the `date` format that will be used to produce a View link label for a specific View. * * @since 4.9.11 * * @param string $format The label format the View will use to product a View link label; e.g. the * previous and next links. * @param \DateTime $date The date object that is being used to build the label. * @param View $view This View instance. */ $format = apply_filters( "tribe_events_views_v2_view_{$view_slug}_link_label_format", $format, $this, $date ); return date_i18n( $format, $date_object->getTimestamp() + $date_object->getOffset() ); } /** * Returns the date format, a valid PHP `date` function format, that should be used to build link labels. * * This format will, usually, apply to next and previous links. * * @todo @bordoni move this method to a supporting class. * * @see View::get_link_label(), the method using this method to build a link label. * @see date_i18n() as the formatted date will, then, be localized using this method. * * @since 4.9.9 * * @return string The date format, a valid PHP `date` function format, that should be used to build link labels. */ protected function get_label_format() { return 'Y-m-d'; } /** * Gets this View title, the one that will be set in the `title` tag of the page. * * @since 4.9.10 * * @param array $events An array of events to generate the title for. * * @return string The filtered view title. */ public function get_title( array $events = [] ) { if ( ! $this->context->doing_php_initial_state() ) { /** @var Title $title_filter */ $title_filter = static::$container->make( Title::class ) ->set_context( $this->context ) ->set_posts( $events ); add_filter( 'document_title_parts', [ $title_filter, 'filter_document_title_parts' ] ); // We disable the filter to avoid the double encoding that would come from our preparation of the data. add_filter( 'run_wptexturize', '__return_false' ); } $title = wp_get_document_title(); if ( isset( $title_filter ) ) { remove_filter( 'run_wptexturize', '__return_false' ); remove_filter( 'document_title_parts', [ $title_filter, 'filter_document_title_parts' ] ); } /** * Filters the title for all views. * * @since 4.9.11 * * @param string $title This view filtered title. * @param View $this This view object. */ $title = apply_filters( "tribe_events_views_v2_view_title", $title, $this ); $view_slug = static::$view_slug; /** * Filters the title for this view. * * @since 4.9.11 * * @param string $title This view filtered title. * @param View $this This view object. */ $title = apply_filters( "tribe_events_views_v2_view_{$view_slug}_title", $title, $this ); return html_entity_decode( $title, ENT_QUOTES ); } /** * Returns a collection of user-facing messages the View will display on the front-end. * * @since 4.9.11 * * @param array $events An array of the events found by the View that is currently rendering. * * @return Messages A collection of user-facing messages the View will display on the front-end. */ public function get_messages( array $events = [] ) { /** * Fires before the view "renders" the array of user-facing messages. * * Differently from the filters below this action allow manipulating the messages handler before the messages * render to, as an example, change rendering strategy and manipulate the message "ingredients". * * @since 4.9.11 * * @param Messages $messages The object instance handling the messages for the View. * @param array $events An array of the events found by the View that is currently rendering. * @param View $this The View instance currently rendering. */ do_action( 'tribe_events_views_v2_view_messages_before_render', $this->messages, $events, $this ); $messages = $this->messages->to_array(); /** * Filters the user-facing messages the View will print on the frontend. * * @since 4.9.11 * * @param array $messages An array of messages in the shape `[ => [ ... ] ]`. * @param View $this The current View instance being rendered. * @param Messages $messages_handler The messages handler object the View used to render the messages. */ $messages = apply_filters( 'tribe_events_views_v2_view_messages', $messages, $this, $this->messages ); $view_slug = static::$view_slug; /** * Filters the user-facing messages a specific View will print on the frontend. * * @since 4.9.11 * * @param array $messages An array of messages in the shape `[ => [ ... ] ]`. * @param array $events An array of the events found by the View that is currently rendering. * @param View $this The current View instance being rendered. * @param Messages $messages_handler The messages handler object the View used to render the messages. */ $messages = apply_filters( "tribe_events_views_v2_view_{$view_slug}_messages", $messages, $events, $this, $this->messages ); return $messages; } /** * Sets up the user-facing messages the View will print on the frontend. * * @since 4.9.11 * * @param array $events An array of the View events, if any. */ protected function setup_messages( array $events ) { if ( empty( $events ) ) { $keyword = $this->context->get( 'keyword', false ); if ( $keyword ) { $this->messages->insert( Messages::TYPE_NOTICE, Messages::for_key( 'no_results_found_w_keyword', esc_html( trim( $keyword ) ) ) ); } else { $message_key = $this->upcoming_events_count() ? 'no_results_found' : 'no_upcoming_events'; $this->messages->insert( Messages::TYPE_NOTICE, Messages::for_key( $message_key ) ); } } } /** * Returns whether the View page should be reset or not. * * The View page should be reset when the View or filtering parameters that are not the page change. * * @since 4.9.11 * * @return bool Whether the View page should be reset or not. */ protected function should_reset_page() { if ( null === $this->should_reset_page ) { $prev_url = $this->context->get( 'view_prev_url', '' ); $current_url = $this->context->get( 'view_url', '' ); $view_data = $this->context->get( 'view_data', [] ); $bar_data = array_filter( $view_data, static function ( $value, $key ) { return 0 === strpos( $key, 'tribe-bar-' ) && ! empty( $value ); }, ARRAY_FILTER_USE_BOTH ); if ( ! empty( $bar_data ) ) { $current_url = add_query_arg( $bar_data, $current_url ); } /** * Filters the ignored params for resetting page number the View will do when paginating via AJAX. * * @see Url::is_diff() * * @since 5.4.0 * * @param array $page_reset_ignored_params An array of params to be ignored. * @param View $this The current View instance being rendered. */ $page_reset_ignored_params = apply_filters( 'tribe_events_views_v2_view_page_reset_ignored_params', [ 'page', 'paged' ], $this ); $view_slug = static::$view_slug; /** * Filters the ignored params for resetting page number a specific View will do when paginating via AJAX. * * @see Url::is_diff() * * @since 5.4.0 * * @param array $page_reset_ignored_params An array of params to be ignored. * @param View $this The current View instance being rendered. */ $page_reset_ignored_params = apply_filters( "tribe_events_views_v2_view_{$view_slug}_page_reset_ignored_params", $page_reset_ignored_params, $this ); $this->should_reset_page = Url::is_diff( $prev_url, $current_url, $page_reset_ignored_params ); } return $this->should_reset_page; } /** * Acts on the View variables, properties and context when a page reset is required. * * By default this method will reset the page in the context, but extending classes can implement their own, * custom version. * * @since 4.9.11 */ protected function on_page_reset() { if ( ! isset( $this->context ) || ! $this->context instanceof Context ) { return; } $url = $this->context->get( 'url', home_url() ); $updated_url = remove_query_arg( [ 'paged', 'page' ], $url ); $view_data = $this->context->get( 'view_data', [] ); $alterations = [ 'page' => 1, 'paged' => 1, 'url' => $updated_url, ]; $alterations['view_data'] = array_merge( $view_data, $alterations ); $this->context = $this->context->alter( $alterations ); } /** * Returns the breadcrumbs data the View will display on the front-end. * * @since 4.9.11 * * @return array */ protected function get_breadcrumbs() { $context = $this->context; $breadcrumbs = []; $taxonomy = TEC::TAXONOMY; $context_tax = $context->get( $taxonomy, false ); if ( empty( $context_tax ) ) { $taxonomy = 'post_tag'; $context_tax = $context->get( $taxonomy, false ); } // Get term slug if taxonomy is not empty if ( ! empty( $context_tax ) ) { // Don't pass arrays to get_term_by()! if ( is_array( $context_tax ) ) { $context_tax = array_pop( $context_tax ); } $term = get_term_by( 'slug', $context_tax, $taxonomy ); if ( ! empty( $term->name ) ) { $label = $term->name; $breadcrumbs[] = [ 'link' => tribe_events_get_url(), 'label' => tribe_get_event_label_plural(), ]; $breadcrumbs[] = [ 'link' => '', 'label' => $label, ]; } } // Setup breadcrumbs for when it's featured. if ( $is_featured = tribe_is_truthy( $this->context->get( 'featured', false ) ) ) { $non_featured_link = tribe_events_get_url( [ 'featured' => 0 ] ); if ( empty( $context_tax ) ) { $breadcrumbs[] = [ 'link' => $non_featured_link, 'label' => tribe_get_event_label_plural(), ]; } $breadcrumbs[] = [ 'link' => '', 'label' => esc_html__( 'Featured', 'the-events-calendar' ), ]; } /** * Filters the breadcrumbs the View will print on the frontend. * * @since 4.9.11 * * @param array $breadcrumbs An array of breadcrumbs. * @param View $this The current View instance being rendered. */ $breadcrumbs = apply_filters( 'tribe_events_views_v2_view_breadcrumbs', $breadcrumbs, $this ); $view_slug = static::get_view_slug(); /** * Filters the breadcrumbs a specific View will print on the frontend. * * @since 4.9.11 * * @param array $breadcrumbs An array of breadcrumbs. * @param View $this The current View instance being rendered. */ $breadcrumbs = apply_filters( "tribe_events_views_v2_view_{$view_slug}_breadcrumbs", $breadcrumbs, $this ); return $breadcrumbs; } /** * Header Title Element, allowing better control over the title tag. * * @since 6.2.0 * * @return string */ protected function get_header_title_element(): string { /** * Filters the header title element the View will print on the frontend. * * @since 6.2.0 * * @param string $header_title_element The header title to be displayed. * @param View $this The current View instance being rendered. */ $header_title_element = (string) apply_filters( 'tec_events_views_v2_view_header_title_element', 'h1', $this ); $view_slug = static::get_view_slug(); /** * Filters the header title element a specific View will print on the frontend. * * @since 6.2.0 * * @param string $header_title_element The header title element to be displayed. * @param View $this The current View instance being rendered. */ return (string) apply_filters( "tec_events_views_v2_view_{$view_slug}_header_title_element", $header_title_element, $this ); } /** * Returns the header title the View will display on the front-end, normally above the breadcrumbs. * * @since 6.2.0 * * @return string */ protected function get_header_title(): string { $context = $this->get_context(); $header_title = ''; $taxonomy = TEC::TAXONOMY; $context_tax = $context->get( $taxonomy, false ); if ( empty( $context_tax ) ) { $taxonomy = 'post_tag'; $context_tax = $context->get( $taxonomy, false ); } // Get term slug if taxonomy is not empty if ( ! empty( $context_tax ) ) { // Don't pass arrays to get_term_by()! if ( is_array( $context_tax ) ) { $context_tax = array_pop( $context_tax ); } $term = get_term_by( 'slug', $context_tax, $taxonomy ); if ( ! empty( $term->name ) ) { $header_title = $term->name; } } // Setup breadcrumbs for when it's featured. if ( tribe_is_truthy( $this->context->get( 'featured', false ) ) ) { $header_title = esc_html__( 'Featured', 'the-events-calendar' ); } /** * Filters the header title the View will print on the frontend. * * @since 6.2.0 * * @param string $header_title The header title to be displayed. * @param View $this The current View instance being rendered. */ $header_title = (string) apply_filters( 'tec_events_views_v2_view_header_title', $header_title, $this ); $view_slug = static::get_view_slug(); /** * Filters the header title a specific View will print on the frontend. * * @since 6.2.0 * * @param string $header_title The header title to be displayed. * @param View $this The current View instance being rendered. */ return (string) apply_filters( "tec_events_views_v2_view_{$view_slug}_header_title", $header_title, $this ); } /** * Returns the content title the View will display on the front-end, normally above the date selector. * * @since 6.2.0 * * @return string */ protected function get_content_title(): string { /** * Filters the content title the View will print on the frontend. * * @since 6.2.0 * * @param string $content_title The content title to be displayed. * @param View $this The current View instance being rendered. */ $content_title = (string) apply_filters( 'tec_events_views_v2_view_content_title', '', $this ); $view_slug = static::get_view_slug(); /** * Filters the content title a specific View will print on the frontend. * * @since 6.2.0 * * @param string $content_title The content title to be displayed. * @param View $this The current View instance being rendered. */ return (string) apply_filters( "tec_events_views_v2_view_{$view_slug}_content_title", $content_title, $this ); } /** * Returns if the view should display the events bar. * * @since 4.9.11 * * @return array */ protected function filter_display_events_bar( $display ) { /** * Filters if the events bar should be displayed. * * @since 4.9.11 * * @param bool $display An bool saying if it should be displayed or not. * @param View $this The current View instance being rendered. */ $display = apply_filters( "tribe_events_views_v2_view_display_events_bar", $display, $this ); $view_slug = static::$view_slug; /** * Filters if the events bar should be displayed for the specific view. * * @since 4.9.11 * * @param bool $display An bool saying if it should be displayed or not. * @param View $this The current View instance being rendered. */ $display = apply_filters( "tribe_events_views_v2_view_{$view_slug}_display_events_bar", $display, $this ); return $display; } /** * Returns a boolean on whether to show the datepicker submit button. * * @since 4.9.13 * * @return bool */ protected function get_show_datepicker_submit() { $live_refresh = tribe_is_truthy( 'automatic' === tribe_get_option( 'liveFiltersUpdate', 'automatic' ) ); $disable_events_bar = tribe_is_truthy( tribe_get_option( 'tribeDisableTribeBar', false ) ); $show_datepicker_submit = empty( $live_refresh ) && ! empty( $disable_events_bar ); /** * Filters the show datepicker submit value. * * @since 5.0.0 * * @param object $show_datepicker_submit The show datepicker submit value. * @param View $this The current View instance being rendered. */ $show_datepicker_submit = apply_filters( "tribe_events_views_v2_view_show_datepicker_submit", $show_datepicker_submit, $this ); $view_slug = static::$view_slug; /** * Filters the show datepicker submit value for a specific view. * * @since 5.0.0 * * @param object $show_datepicker_submit The show datepicker submit value. * @param View $this The current View instance being rendered. */ $show_datepicker_submit = apply_filters( "tribe_events_views_v2_view_{$view_slug}_show_datepicker_submit", $show_datepicker_submit, $this ); return $show_datepicker_submit; } /** * Manipulates public views data, if necessary, and returns result. * * @since 5.0.0 * * @param string|bool $url_event_date The value, `Y-m-d` format, of the `eventDate` request variable to * append to the view URL, if any. * * @return array */ protected function get_public_views( $url_event_date ) { $public_views = tribe( Manager::class )->get_publicly_visible_views_data(); $query_args = wp_parse_url( $this->get_url(), PHP_URL_QUERY ); if ( ! empty( $url_event_date ) || ! empty( $query_args ) ) { // Each View expects the event date in a specific format, here we account for it. array_walk( $public_views, static function ( &$view_data ) use ( $url_event_date, $query_args ) { $view_instance = View::make( $view_data->view_class ); $view_data->view_url = $view_instance->url_for_query_args( $url_event_date, $query_args ); } ); } /** * Filters the public views. * * @since 5.0.0 * * @param object $public_views The public views. * @param View $this The current View instance being rendered. */ $public_views = apply_filters( "tribe_events_views_v2_view_public_views", $public_views, $this ); $view_slug = static::$view_slug; /** * Filters the public views for a specific view. * * @since 5.0.0 * * @param object $public_views The public views. * @param View $this The current View instance being rendered. */ $public_views = apply_filters( "tribe_events_views_v2_view_{$view_slug}_public_views", $public_views, $this ); return $public_views; } /** * {@inheritDoc} */ public function url_for_query_args( $date = null, $query_args = [] ) { if ( ! empty( $query_args ) && is_string( $query_args ) ) { $str_args = $query_args; $query_args = []; wp_parse_str( $str_args, $query_args ); } // For "dateless" queries (today). if ( empty( $date ) ) { $query_args = array_filter( array_merge( $query_args, [ 'eventDisplay' => static::$view_slug ] ) ); return tribe_events_get_url( $query_args ); } $event_date = Dates::build_date_object( $date )->format( $this->get_url_date_format() ); $url_query_args = array_filter( array_merge( $query_args, [ 'eventDisplay' => static::$view_slug, 'eventDate' => $event_date, ] ) ); if ( static::$date_in_url ) { unset( $url_query_args['tribe-bar-date'] ); // This is the case for Views that include the date in the "pretty" URL, e.g. Month, Day or Week. return tribe_events_get_url( $url_query_args ); } // This is the case for Views that don't include the date in the "pretty" URL, e.g. List. unset( $url_query_args['eventDate'] ); return add_query_arg( [ 'tribe-bar-date' => $event_date ], tribe_events_get_url( $url_query_args ) ); } /** * Returns the date format that should be used to format the date in the View URL. * * Extending Views cal override this to customize the URL output (e.g. Month View). * * @since 4.9.13 * * @return string The date format that should be used to format the date in the View URL. */ protected function get_url_date_format() { return Dates::DBDATEFORMAT; } /** * Returns the filtered container data attributes for the View top-level container. * * @since 5.0.0 * * @return array The filtered list of data attributes for the View top-level container. */ protected function get_container_data() { $view_slug = static::$view_slug; /** * Filters the data for a View top-level container. * * @since 5.0.0 * * @param array $data Associative array of data for the View top-level container. * @param string $view_slug The current view slug. * @param View $instance The current View object. */ $data = apply_filters( 'tribe_events_views_v2_view_container_data', [], $view_slug, $this ); /** * Filters the data for a specific View top-level container. * * @since 4.9.13 * * @param array $data Associative array of data for the View top-level container. * @param View $instance The current View object. */ $data = apply_filters( "tribe_events_views_v2_{$view_slug}_view_container_data", $data, $this ); return $data; } /** * Filters Whether the Latest Past Events Should Show for a specific View. * * @since 5.1.0 * * @return boolean If we should display Latest Past Events. */ protected function should_show_latest_past_events_view() { $show = $this->context->get( 'show_latest_past', true ); $view_slug = static::$view_slug; /** * Filters Whether the Latest Past Events Should Show for all Views. * * @since 5.1.0 * * @param boolean $show If we should display Latest Past Events. * @param string $view_slug The current view slug. * @param View $instance The current View object. */ $show = apply_filters( 'tribe_events_views_v2_show_latest_past_events_view', $show, $view_slug, $this ); /** * Filters Whether the Latest Past Events Should Show for a specific View. * * @since 5.1.0 * * @param boolean $show If we should display Latest Past Events. * @param View $instance The current View object. */ $show = apply_filters( "tribe_events_views_v2_{$view_slug}_show_latest_past_events_view", $show, $this ); return $show; } /** * Setup of Additional Views into another View. * * @since 5.1.0 * * @param array $events Array that will be counted to verify if we have events. * @param array $template_vars An associative array of variables that will be set, and exported, in the template. */ protected function setup_additional_views( array $events = [], array $template_vars = [] ) { $manager = tribe( Manager::class ); $default_slug = $manager->get_default_view_option(); // If the slug is `default`, get the slug another way. if ( 'default' === $default_slug ) { $default_class = $manager->get_default_view(); $default_slug = $manager->get_view_slug_by_class( $default_class ); } // Show Latest Past Events only on the default view. if ( static::$view_slug !== $default_slug ) { return; } // If doing a search, do not show. if ( $this->context->get( 'keyword', '' ) ) { return; } $now = $this->context->get( 'now', time() ); $latest = tribe_events_latest_date(); // If now is less then the latest event published, do not show. if ( $now < $latest ) { return; } // Checks to verify on the initial load of a view or if using today's date for the view. $today = $this->context->get( 'today' ); $view_date = $this->context->get( 'event_date', '' ); switch ( static::$view_slug ) { case Month_View::get_view_slug(): $today_formatted = Dates::build_date_object( $today )->format( Dates::DBYEARMONTHTIMEFORMAT ); $view_date_formatted = Dates::build_date_object( $view_date )->format( Dates::DBYEARMONTHTIMEFORMAT ); break; case 'week': [ $today_week_start, $today_week_end ] = Dates::get_week_start_end( $today, (int) $this->context->get( 'start_of_week', 0 ) ); [ $view_week_start, $view_week_end ] = Dates::get_week_start_end( $view_date, (int) $this->context->get( 'start_of_week', 0 ) ); $today_formatted = $today_week_start->format( Dates::DBDATEFORMAT ); $view_date_formatted = $view_week_start->format( Dates::DBDATEFORMAT ); break; default: $today_formatted = Dates::build_date_object( $today )->format( Dates::DBDATEFORMAT ); $view_date_formatted = Dates::build_date_object( $view_date )->format( Dates::DBDATEFORMAT ); } // If view date is not empty and today does not equal the view date, then do not show. if ( ! empty( $view_date ) && $today_formatted !== $view_date_formatted ) { return; } // Flatten Views such as Month and Week that have an array values. $first_value = reset( $events ); if ( is_array( $first_value ) ) { $events = array_unique( array_merge( ...array_values( $events ) ), SORT_REGULAR ); } /** * Filters The Threshold to Show The Latest Past Events. * Defaults to show when there are Zero Events. * * @since 5.1.0 * * @param int The threshold to show The Latest Past Events. * @param array $events Array that will be counted to verify if we have events. * @param array $template_vars An associative array of variables that will be set, and exported, in the template. * @param View $instance The current View object. */ $latest_past_threshold = apply_filters( 'tribe_events_views_v2_threshold_to_show_latest_past_events', absint( 0 ), $events, $template_vars, $this ); // If threshold is less than upcoming events, do not show Recent Past Events. if ( $latest_past_threshold < count( $events ) ) { return; } // If no events found, do not show. if ( 0 === tribe_events()->found() ) { return; } if ( ! empty( $template_vars['show_latest_past'] ) ) { $template_vars['show_latest_past'] = true; $latest_past_view = static::make( Latest_Past_View::get_view_slug() ); $latest_past_view->set_context( $this->context ); $latest_past_view->add_view_filters(); } } /** * Returns the number of upcoming events in relation to the "now" time. * * @since 5.2.0 * * @return int The number of upcoming events from "now". */ protected function upcoming_events_count() { $now = $this->context->get( 'now', Dates::build_date_object()->format( 'Y-m-d H:i:s' ) ); $from_date = tribe_beginning_of_day( $now ); return (int) tribe_events()->where( 'starts_after', $from_date )->found(); } /** * Returns the View current URL query arguments, parsed from the View `get_url()` method. * * Since there are a number of parties filtering each View URL arguments, this method will * parse a View URL query arguments from its filtered URL. This will include all the modifications * done to a View URL by other plugins and add-ons. * * @since 5.3.0 * * @return array The current View URL args or an empty array if the View URL is empty * or not valid.. */ public function get_url_args() { $view_url = $this->get_url( false ); $view_query_str = wp_parse_url( $view_url, PHP_URL_QUERY ); if ( empty( $view_query_str ) ) { // This might happen if the URL is too mangled to be parsed. return []; } parse_str( $view_query_str, $view_query_args ); return (array) $view_query_args; } /** * Initializes the View repository args, if required, and * applies them to the View repository instance. * * @since 4.6.0 */ protected function get_repository_args() { if ( ! empty( $this->repository_args ) ) { return $this->repository_args; } return $this->filter_repository_args( $this->setup_repository_args() ); } /** * Compiled the global repository args that should be applied to all events queried for this view. * * @since 6.0.5 * * @return array The global filtered repository args. */ protected function get_global_repository_args() { if ( ! is_array( $this->global_repository_args ) ) { /** * Will filter any repository args to be applied globally on the various repository queries on * this view. * * @since 6.0.5 * * @param array Events Repository args that will be applied globally to all event * repository queries. * @param View $this The View object being rendered. * * @return array The repository args to be applied. */ $this->global_repository_args = apply_filters( 'tec_events_views_v2_view_global_repository_args', [], $this ); $view_slug = static::$view_slug; /** * A view specific filter for repository args to be applied globally on the various repository * queries on this view. * * @since 6.0.5 * * @param array Events Repository args that will be applied globally to all * event repository queries. * @param View $this The View object being rendered. * * @return array The repository args to be applied. */ $this->global_repository_args = apply_filters( "tec_events_views_v2_{$view_slug}_view_global_repository_args", $this->global_repository_args, $this ); } return $this->global_repository_args; } /** * Sets up the View repository args to produce the correct list of Events * in the context of an iCalendar export. * * @since 4.6.0 * * @param int $per_page The number of events per page to show in the iCalendar * export. The value will override whatever events per page * setting the View might have. */ protected function setup_ical_repository_args( $per_page ) { if ( empty( $this->repository_args ) ) { $this->repository->by_args( $this->filter_ical_repository_args( $this->get_repository_args() ) ); } // Overwrites the amount of posts manually for ical. $this->repository->per_page( $per_page ); } /** * Filters the repository arguments that will be used to set up the View repository instance for iCal requests. * * @since 4.6.0 * * @param array $repository_args The repository arguments that will be used to set up the View repository instance. * * @return array The filtered repository arguments for ical requests. */ protected function filter_ical_repository_args( $repository_args ) { /** * Filters the repository args for a View on iCal requests. * * @since 4.6.0 * * @param array $repository_args An array of repository arguments that will be set for all Views. * @param View_Interface $this The View that will use the repository arguments. */ $repository_args = apply_filters( 'tribe_events_views_v2_view_ical_repository_args', $repository_args, $this ); $view_slug = static::$view_slug; /** * Filters the repository args for a specific View on iCal requests. * * @since 4.6.0 * * @param array $repository_args An array of repository arguments that will be set for a specific View. * @param View_Interface $this The View that will use the repository arguments. */ $repository_args = apply_filters( "tribe_events_views_v2_view_{$view_slug}_ical_repository_args", $repository_args, $this ); return $repository_args; } /** * Filters the current template current view which allows you to pull globally which view is currently being rendered. * * @since 6.0.0 * * @param View_Interface $view Which is the previous view. * * @return self */ public function filter_set_current_view( $view ) { return $this; } /** * {@inheritdoc} */ public function get_ical_ids( $per_page ) { $this->setup_ical_repository_args( $per_page ); $ids = $this->repository->get_ids(); // Reset the repository args to force a re-initialization of the repository on next run. $this->repository_args = null; return $ids; } /** * {@inheritdoc} */ public function set_url_object( Url $url_object ) { $this->url = $url_object; } /** * {@inheritdoc} */ public function disable_url_management() { $this->should_manage_url = false; return $this; } /** * Gets the base object for asset registration. * * @since 5.7.0 * * @return \stdClass $object Object to tie registered assets to. */ public static function get_asset_origin( $slug ) { $asset_registration_object = tribe( 'tec.main' ); /** * Filters the object used for registering assets. * * @since 5.7.0 * * @param \stdClass $origin_object Object used for asset registration. * @param string $slug View slug. */ $asset_registration_object = apply_filters( "tribe_events_views_v2_view_{$slug}_asset_origin_object", $asset_registration_object, $slug ); return $asset_registration_object; } /** * Registers assets for the view. * * Should be overridden if there are assets for the view. * * @since 5.7.0 * * @param \stdClass $object Object to tie registered assets to. */ public static function register_assets( $object ) { // Default to a no-op. } /** * {@inheritDoc} */ public function get_rewrite_slugs(): array { // This translation method relies on the slug being translated elsewhere. return [ static::$view_slug, translate( static::$view_slug, 'the-events-calendar' ) ]; } }