\WP_Stager_Integration\Calendar::render_calendar(array $args = array()): void

Description

Renders a calendar.

This method should not be called directly and is used internally by all other render_* methods. To render a calendar, use the available render_* methods.

Parameters:

ParameterTypeDescription
$args['scope'] string What to render: 'event', 'day', 'week', 'month', 'year'.
$args['class'] string (Optional) A space-separated string of classes to add to the calendar element. Default value: ''.
$args['start'] int|null (Optional) Unix timestamp of start of range.
$args['end'] int|null (Optional) Unix timestamp of end of range.
$args['view'] string|null (Optional) View style: 'calendar' (default), 'list' or a custom value. Adds a view-{$view} class to the calendar element for adding custom styles.
$args['events'] array|null (Optional) Array of preloaded event arrays (raw Stager data).
$args['clip'] bool|null (Optional) Whether to clip the calendar view to the specified range. Default value: false, don't clip.
$args['async'] bool|null (Optional) Whether to render the calendar asynchronously using AJAX. Default value is true if the "Asynchrounous rendering" setting is enabled, false otherwise.
$args['view'] string|null (Optional) The type of view to render, used to add a view-$view class to the calendar, for modifying the styling and layout of the calendar.
$args['show_label_day'] bool|null (Optional) Whether to show the day label. Default value: true, show label.
$args['show_label_week'] bool|null (Optional) Whether to show the week label. Default value: true, show label.
$args['show_label_month'] bool|null (Optional) Whether to show the month label. Default value: true, show label.
$args['show_label_year'] bool|null (Optional) Whether to show the year label. Default value: true, show label.
$args['show_label_event_time'] bool|null (Optional) Whether to show the event time label. Default value: true, show label.
$args['show_nav'] bool|null (Optional) Whether to show the navigation. Default value: true, show navigation.
$args['nav_label_next'] string|null (Optional) The button text of the "Next" button within the navigation. By default, the button text reflects the next date range (e.g. "Week 12", "September 12", "2026", etc)
$args['nav_label_previous'] string|null (Optional) The button text of the "Previous" button within the navigation. By default, the button text reflects the previous date range. (e.g. "Week 12", "September 12", "2026", etc)
$args['no_events_message'] string|null (Optional) The message to display when there are no events. Default value: "No events".
$args['context'] string|null (Optional) The context in which the calendar is being rendered. Used to add a context-{$context} class to the calendar element. Optional values: "block-editor", "default". Default value: "default".
$args['show_empty_days'] bool|null (Optional) Whether to show empty days in calendars with $view set to "list". Default value: true, show empty days.
$args['show_empty_weeks'] bool|null (Optional) Whether to show empty weeks in calendars with $view set to "list". Default value: true, show empty weeks.
$args['show_empty_months'] bool|null (Optional) Whether to show empty months in calendars with $view set to "list". Default value: true, show empty months.
$args['show_empty_years'] bool|null (Optional) Whether to show empty years in calendars with $view set to "list". Default value: true, show empty years.
$args['show_event_main_image'] bool|null (Optional) Whether to show the main image within event components. Default value: false, don't show image.
$args['overlay_text_on_image'] bool|null (Optional) Whether to overlay text on the event image. Default value: false, don't overlay text.
$args['show_past_events'] bool|null (Optional) Whether to include past events in the calendar. Default value: false, don't show past events.
$args['show_upcoming_events'] bool|null (Optional) Whether to include events that have not started yet (program start in the future). Default value: true, show upcoming events.
$args['enabled_filters'] string[]|null (Optional) An array of taxonomies to enable in the filter. Any field that is present in the Stager event data can be used to filter on, including nested fields. For example: 'tags', 'type', 'eventLabels.name', 'customerInterests.name', 'location.city', etc. Default value: array(), disable the filter.
$args['filter_appearance'] string|null (Optional) The appearance of the filter. Accepted values are: 'accordion', 'dropdown'. Default value: 'accordion'.
$args['query'] array|null (Optional) A query to filter the events on. Associative array of taxonomy keys (same as $enabled_filters, e.g. 'tags', 'type', 'eventLabels.name', etc) to arrays of allowed values. Only events matching all taxonomies are included. Applied on initial render, async render, and when fetching additional scopes via navigation. Default value: array(), no filter.

Returns:

void

Information

Fileclass-calendar.php line 1211
See also

Full Code

/**
 * Renders a calendar.
 *
 * This method should not be called directly and is used internally by all other <code>render_*</code> methods. To render a calendar,
 * use the available <code>render_*</code> methods.
 *
 * @param array $args {
 *     Arguments to define what to render.
 *
 *     @type string         $scope                  What to render: <code>'event'</code>, <code>'day'</code>, <code>'week'</code>, <code>'month'</code>, <code>'year'</code>.
 *     @type string         $class                  (Optional) A space-separated string of classes to add to the calendar element. Default value: ''.
 *     @type int|null       $start                  (Optional) Unix timestamp of start of range.
 *     @type int|null       $end                    (Optional) Unix timestamp of end of range.
 *     @type string|null    $view                   (Optional) View style: <code>'calendar'</code> (default), <code>'list'</code> or a custom value. Adds a <code>view-{$view}</code> class to the calendar element for adding custom styles.
 *     @type array|null     $events                 (Optional) Array of preloaded event arrays (raw Stager data).
 *     @type bool|null      $clip                   (Optional) Whether to clip the calendar view to the specified range. Default value: false, don't clip.
 *     @type bool|null      $async                  (Optional) Whether to render the calendar asynchronously using AJAX. Default value is true if the "Asynchrounous rendering" setting is enabled, false otherwise.
 *     @type string|null    $view                   (Optional) The type of view to render, used to add a <code>view-$view</code> class to the calendar, for modifying the styling and layout of the calendar.
 *     @type bool|null      $show_label_day         (Optional) Whether to show the day label. Default value: true, show label.
 *     @type bool|null      $show_label_week        (Optional) Whether to show the week label. Default value: true, show label.
 *     @type bool|null      $show_label_month       (Optional) Whether to show the month label. Default value: true, show label.
 *     @type bool|null      $show_label_year        (Optional) Whether to show the year label. Default value: true, show label.
 *     @type bool|null      $show_label_event_time  (Optional) Whether to show the event time label. Default value: true, show label.
 *     @type bool|null      $show_nav               (Optional) Whether to show the navigation. Default value: true, show navigation.
 *     @type string|null    $nav_label_next         (Optional) The button text of the "Next" button within the navigation. By default, the button text reflects the next date range (e.g. "Week 12", "September 12", "2026", etc)
 *     @type string|null    $nav_label_previous     (Optional) The button text of the "Previous" button within the navigation. By default, the button text reflects the previous date range.  (e.g. "Week 12", "September 12", "2026", etc)
 *     @type string|null    $no_events_message      (Optional) The message to display when there are no events. Default value: "No events".
 *     @type string|null    $context                (Optional) The context in which the calendar is being rendered. Used to add a <code>context-{$context}</code> class to the calendar element. Optional values: "block-editor", "default". Default value: "default".
 *     @type bool|null      $show_empty_days        (Optional) Whether to show empty days in calendars with <code>$view</code> set to "list". Default value: true, show empty days.
 *     @type bool|null      $show_empty_weeks       (Optional) Whether to show empty weeks in calendars with <code>$view</code> set to "list". Default value: true, show empty weeks.
 *     @type bool|null      $show_empty_months      (Optional) Whether to show empty months in calendars with <code>$view</code> set to "list". Default value: true, show empty months.
 *     @type bool|null      $show_empty_years       (Optional) Whether to show empty years in calendars with <code>$view</code> set to "list". Default value: true, show empty years.
 *     @type bool|null      $show_event_main_image  (Optional) Whether to show the main image within event components. Default value: <code>false</code>, don't show image.
 *     @type bool|null      $overlay_text_on_image  (Optional) Whether to overlay text on the event image. Default value: <code>false</code>, don't overlay text.
 *     @type bool|null      $show_past_events       (Optional) Whether to include past events in the calendar. Default value: <code>false</code>, don't show past events.
 *     @type bool|null      $show_upcoming_events   (Optional) Whether to include events that have not started yet (program start in the future). Default value: <code>true</code>, show upcoming events.
 *     @type string[]|null  $enabled_filters        (Optional) An array of taxonomies to enable in the filter. Any field that is present in the Stager event data can be used to filter on, including nested fields. For example: <code>'tags'</code>, <code>'type'</code>, <code>'eventLabels.name'</code>, <code>'customerInterests.name'</code>, <code>'location.city'</code>, etc. Default value: <code>array()</code>, disable the filter.
 *     @type string|null    $filter_appearance      (Optional) The appearance of the filter. Accepted values are: <code>'accordion'</code>, <code>'dropdown'</code>. Default value: <code>'accordion'</code>.
 *     @type array|null     $query                  (Optional) A query to filter the events on. Associative array of taxonomy keys (same as <code>$enabled_filters</code>, e.g. <code>'tags'</code>, <code>'type'</code>, <code>'eventLabels.name'</code>, etc) to arrays of allowed values. Only events matching all taxonomies are included. Applied on initial render, async render, and when fetching additional scopes via navigation. Default value: <code>array()</code>, no filter.
 * }
 * @return void
 *
 * @see \WP_Stager_Integration\Calendar::render_currently_happening() to render ongoing events
 * @see \WP_Stager_Integration\Calendar::render_event() to render a single event
 * @see \WP_Stager_Integration\Calendar::render_day() to render a day calendar/list
 * @see \WP_Stager_Integration\Calendar::render_week() to render a week calendar/list
 * @see \WP_Stager_Integration\Calendar::render_month() to render a month calendar/list
 * @see \WP_Stager_Integration\Calendar::render_year() to render a year calendar/list
 * @see \WP_Stager_Integration\Calendar::render_range() to render a calendar with a specified start/end date
 */
public static function render_calendar( $args = array() ) {

    if ( ! Options::is_license_active() ) {
        self::render_calendar_component( 'inactive' );
        return;
    }

    // Type casting
    $args = (array) $args;

    // Parse args coming from shortcodes (always strings)
    // Loop over $value
    foreach ( $args as $key => &$value ) {

        switch ( $value ) {

            case '1':
                $value = 1;
                break;

            case '0':
                $value = 0;
                break;

            case 'true':
                $value = true;
                break;

            case 'false':
                $value = false;
                break;
        }
    }

    // Get data
    $scope                 = (string) ($args[ 'scope' ] ?? 'all');
    $class                 = (string) ($args[ 'class' ] ?? '');
    $start                 = (int) ($args[ 'start' ] ?? 0);
    $end                   = (int) ($args[ 'end' ] ?? 0);
    $clip                  = (bool) ($args[ 'clip' ] ?? false);
    $view                  = (string) ($args[ 'view' ] ?? Options::get_calendar_default_view());
    $show_label_day        = (bool) ($args[ 'show_label_day' ] ?? Options::is_enabled_calendar_label_day());
    $show_label_week       = (bool) ($args[ 'show_label_week' ] ?? Options::is_enabled_calendar_label_week());
    $show_label_month      = (bool) ($args[ 'show_label_month' ] ?? Options::is_enabled_calendar_label_month());
    $show_label_year       = (bool) ($args[ 'show_label_year' ] ?? Options::is_enabled_calendar_label_year());
    $show_label_event_time = (bool) ($args[ 'show_label_event_time' ] ?? Options::is_enabled_calendar_label_event_time());
    $show_nav              = (bool) ($args[ 'show_nav' ] ?? true);
    $nav_label_next        = (string) ($args[ 'nav_label_next' ] ?? '');
    $nav_label_previous    = (string) ($args[ 'nav_label_previous' ] ?? '');
    $context               = (string) ($args[ 'context' ] ?? 'default');
    $show_empty_days       = (bool) ($args[ 'show_empty_days' ] ?? false);
    $show_empty_weeks      = (bool) ($args[ 'show_empty_weeks' ] ?? false);
    $show_empty_months     = (bool) ($args[ 'show_empty_months' ] ?? false);
    $show_empty_years      = (bool) ($args[ 'show_empty_years' ] ?? false);
    $show_event_main_image = (bool) ($args[ 'show_event_main_image' ] ?? true);
    $overlay_text_on_image = (bool) ($args[ 'overlay_text_on_image' ] ?? true);
    $show_past_events      = (bool) ($args[ 'show_past_events' ] ?? false);
    $show_upcoming_events  = (bool) ($args[ 'show_upcoming_events' ] ?? true);
    $enabled_filters       = (array) ($args[ 'enabled_filters' ] ?? array());
    $filter_appearance     = (string) ($args[ 'filter_appearance' ] ?? 'accordion');

    // Type casting
    $query = (array) ($args[ 'query' ] ?? array());

    // Loop over all taxonomies
    foreach ( $query as $taxonomy => $values ) {

        // Type casting
        $values = is_array( $values ) ? $values : array( $values );

        // Normalize values
        $values             = array_map( 'strval', $values );
        $values             = array_map( 'trim', $values );
        $values             = array_filter( $values );
        $values             = array_values( $values );
        $query[ $taxonomy ] = $values;
    }

    // Filter empty taxonomies
    $query = array_filter( $query );

    // Parse data
    $start = $start ?: time();

    // Set $async to false if it is disabled globally
    if ( ! Options::is_enabled_async_rendering() ) {
        $async = false;
    } else {
        $async = (bool) ($args[ 'async' ] ?? true);
    }

    // Disable nav for specific scopes
    if ( 'event' === $scope || 'all' === $scope ) {
        $show_nav = false;
    }

    // Check whether events were explicitly provided
    if ( isset( $args[ 'events' ] ) ) {

        // Use provided array of events
        $events = (array) $args[ 'events' ];
    } else {

        // Load events if not provided
        if ( 'all' === $scope ) {
            // For 'all' scope, load all events
            $events = (array) (Event::get_all_events( $show_past_events, -1 ) ?: array());
        } else {
            // For other scopes, load events within date range
            $events = (array) (Event::get_events_within_date_range( $start, $end ) ?: array());
        }
    }

    // Apply back-end query filter (so async and scope fetches also get filtered results)
    if ( ! empty( $query ) ) {
        $events = (array) (self::apply_query_filter( $events, $query ) ?: array());
    }

    // Exclude events that have not started yet when requested
    $events = (array) (self::filter_events_by_show_upcoming( $events, $show_upcoming_events ) ?: array());

    // Handle past events filtering
    if ( ! $show_past_events && $start < time() ) {
        $start = self::calculate_timestamp_day_start( time() );
    }

    // Define component args
    $component_args = array(
        'scope'                 => $scope,
        'clip'                  => $clip,
        'start'                 => $start,
        'end'                   => $end,
        'async'                 => $async,
        'view'                  => $view,
        'show_label_year'       => $show_label_year,
        'show_label_month'      => $show_label_month,
        'show_label_week'       => $show_label_week,
        'show_label_day'        => $show_label_day,
        'show_label_event_time' => $show_label_event_time,
        'nav_label_next'        => $nav_label_next,
        'nav_label_previous'    => $nav_label_previous,
        'show_empty_days'       => $show_empty_days,
        'show_empty_weeks'      => $show_empty_weeks,
        'show_empty_months'     => $show_empty_months,
        'show_empty_years'      => $show_empty_years,
        'show_event_main_image' => $show_event_main_image,
        'overlay_text_on_image' => $show_event_main_image ? $overlay_text_on_image : false,
        'show_past_events'      => $show_past_events,
        'show_upcoming_events'  => $show_upcoming_events,
        'enabled_filters'       => $enabled_filters,
        'filter_appearance'     => $filter_appearance,
        'query'                 => $query,
        'block'                 => 'calendar',
    );

    // Only include no events message if it is set and not empty
    if ( isset( $args[ 'no_events_message' ] ) ) {
        $component_args[ 'no_events_message' ] = $args[ 'no_events_message' ];
    }

    // Render async component and bail if async rendering
    if ( $async ) {
        self::render_calendar_component( 'async', array_merge( $args, $component_args ) );
        return;
    }

    // Build hierarchy once
    $hierarchy = (array) (self::build_event_hierarchy( $events ) ?: array());

    // Define classes
    $classes = array(
        'wpstager-calendar',
        $class,
        $clip ? 'wpstager-calendar--clip' : '',
        $show_nav ? 'wpstager-calendar--has-nav' : '',
        "wpstager-calendar--view-{$view}",
        "wpstager-calendar--context-{$context}",
        "wpstager-calendar--scope-{$scope}",
        $show_empty_days ? 'wpstager-calendar--empty-days-shown' : 'wpstager-calendar--empty-days-hidden',
        $show_empty_weeks ? 'wpstager-calendar--empty-weeks-shown' : 'wpstager-calendar--empty-weeks-hidden',
        $show_empty_months ? 'wpstager-calendar--empty-months-shown' : 'wpstager-calendar--empty-months-hidden',
        $show_empty_years ? 'wpstager-calendar--empty-years-shown' : 'wpstager-calendar--empty-years-hidden',
        $enabled_filters ? 'wpstager-calendar--has-filters-enabled' : 'wpstager-calendar--no-filters-enabled',
        $enabled_filters ? "wpstager-calendar--filter-appearance-{$filter_appearance}" : '',
    );

    echo '<div class="' . esc_attr( implode( ' ', array_filter( $classes ) ) ) . '">';

    if ( $enabled_filters ) {

        // Add events to $args
        $args[ 'events' ] = $hierarchy;

        self::component_filter( array_merge( $args, $component_args ) );
    }

    echo '<div class="wpstager-calendar__inner">';

    if ( $enabled_filters ) {

        $no_results_message = 'year' === $scope ? __( 'No events were found matching the current filter selection.', 'wp-stager-integration' ) : __( 'No events were found matching the current filter selection within the current date range.', 'wp-stager-integration' );

        echo '<div class="wpstager-calendar-filter-no-results-message">' . wp_kses_post( $no_results_message ) . ' <button class="wpstager-calendar-filter__clear-button">' . esc_html__( 'Clear filters', 'wp-stager-integration' ) . '</button></div>';
    }

    if ( $show_nav ) {
        self::render_calendar_component( 'nav', array_merge( $args, $component_args ) );
    }

    echo '<div class="wpstager-calendar__scope-wrapper" data-scope="' . esc_attr( $scope ) . '">';

    // Cards view with nav: render a single scope wrapper and flat cards list via component
    if ( 'cards' === $view && in_array( $scope, array( 'day', 'week', 'month', 'year' ), true ) ) {

        self::component_cards_scope( array(
            'start'                => $start,
            'end'                  => $end,
            'scope'                => $scope,
            'events'               => $events,
            'component_args'       => $component_args,
            'show_past_events'     => $show_past_events,
            'show_upcoming_events' => $show_upcoming_events,
            'clip'                 => true,
        ) );
    } else {

        // Add events to $component_args
        $component_args[ 'events' ] = $events;

        // Switch rendering by scope
        switch ( $scope ) {

            case 'event':
            case 'all':
                self::component_events( $events, $component_args );
                break;

            case 'day':
                // Loop every day in the range
                $cursor = self::calculate_timestamp_day_start( $start );
                $last   = self::calculate_timestamp_day_start( $end );

                while ( $cursor <= $last ) {
                    $year  = wp_date( 'Y', $cursor );
                    $month = self::get_month_number( $cursor );
                    $day   = self::get_day_number( $cursor );

                    self::component_day( $year, $month, $day, $hierarchy, $component_args );

                    // Offset cursor by 1 day
                    $cursor = self::calculate_offset_timestamp( $cursor, 'P1D' );
                }
                break;

            case 'week':
                // Loop every week in the range
                $cursor = self::calculate_timestamp_week_start(
                                self::get_year_number( $start ),
                                self::get_week_number( $start )
                );
                $last   = self::calculate_timestamp_week_end(
                                self::get_year_number( $end ),
                                self::get_week_number( $end )
                );

                while ( $cursor <= $last ) {
                    $year = self::get_year_number( $cursor );
                    $week = self::get_week_number( $cursor );

                    self::component_week( $year, $week, $hierarchy, $component_args );

                    // Offset cursor by 1 week
                    $cursor = self::calculate_offset_timestamp( $cursor, 'P1W' );
                }
                break;

            case 'month':
                // Loop every month in the range
                $start_month = self::get_month_number( $start );
                $start_year  = wp_date( 'Y', $start );
                $end_month   = self::get_month_number( $end );
                $end_year    = wp_date( 'Y', $end );
                $cursor      = self::calculate_timestamp_month_start( $start_year, $start_month );
                $last        = self::calculate_timestamp_month_end( $end_year, $end_month );

                while ( $cursor <= $last ) {
                    $year  = wp_date( 'Y', $cursor );
                    $month = self::get_month_number( $cursor );

                    self::component_month( $year, $month, $hierarchy, $component_args );

                    // Offset cursor by 1 month
                    $cursor = self::calculate_offset_timestamp( $cursor, 'P1M' );
                }
                break;

            case 'year':
                // Loop every year in the range
                $cursor = self::calculate_timestamp_year_start( wp_date( 'Y', $start ) );
                $last   = self::calculate_timestamp_year_start( wp_date( 'Y', $end ) );

                while ( $cursor <= $last ) {

                    $year = wp_date( 'Y', $cursor );

                    self::component_year( $year, $hierarchy, $component_args );

                    // Offset cursor by 1 year
                    $cursor = self::calculate_offset_timestamp( $cursor, 'P1Y' );
                }
                break;
        }
    }

    echo '</div>'; // .wpstager-calendar__scope-wrapper

    if ( $show_nav ) {
        self::render_calendar_component( 'nav', array_merge( $args, $component_args ) );
    }

    echo '</div>'; // .wpstager-calendar__inner
    echo '</div>'; // .wpstager-calendar
}

💡 If you ever get stuck or have a question, please check our FAQs, our Free Integration Service, our paid Full Integration Service, or reach out to us!

Get WP Stager Integration

🎁 Limited offer: Use code WELCOME26 to get your first month for free!