Make WordPress Core

Opened 10 years ago

Last modified 3 years ago

#29319 reviewing enhancement

filter dayswithposts in widget calendar

Reported by: kkarpieszuk's profile kkarpieszuk Owned by: sergeybiryukov's profile SergeyBiryukov
Milestone: Future Release Priority: normal
Severity: normal Version: 3.0
Component: Widgets Keywords: needs-unit-tests has-patch needs-refresh
Focuses: Cc:

Description

Hello

here is Konrad, WPML developer. We are fighting now with bug related WordPress calendar widget: it always displays all posts, and we want to filter its results regarding to language of post.

Steps to reproduce:

  1. install WPML
  2. publish some post in En language July 1st
  3. translate this post July 2nd to Polish language
  4. activate calendar widget

Result: calendar will show that there are posts in July 1st and 2nd.
Expected result: when user displays English posts, calendar should display only July 1st. When user switches to Polish language, calndar should display only July 2nd.

We want to fix this in WPML but we need filter inside of get_calendar() function.

Wordpress gets days with posts by this query inside of get_calendar():

$dayswithposts = $wpdb->get_results("SELECT DISTINCT DAYOFMONTH(post_date)
        FROM $wpdb->posts WHERE post_date >= '{$thisyear}-{$thismonth}-01 00:00:00'
        AND post_type = 'post' AND post_status = 'publish'
        AND post_date <= '{$thisyear}-{$thismonth}-{$last_day} 23:59:59'", ARRAY_N);

The result should be able to filter, then WPML will hook here.

I am attaching proposed diff file.

(I see that we can hook into get_calendar filter but this variable has full html output of calendar. This will unefficient to parse this HTML)

Attachments (3)

dayswithposts_filter.diff (568 bytes) - added by kkarpieszuk 10 years ago.
proposed diff file
general-template.diff (3.6 KB) - added by podpirate 10 years ago.
Filter where and join clause in get_calendar()
get_calendar-get_posts.diff (6.6 KB) - added by podpirate 9 years ago.
use get_posts() in get_calendar()

Download all attachments as: .zip

Change History (35)

@kkarpieszuk
10 years ago

proposed diff file

#1 @SergeyBiryukov
10 years ago

  • Type changed from feature request to enhancement

The filter needs to be documented as per the documentation standards.

As a workaround, you can use the 'query' filter to change that query.

#2 @podpirate
10 years ago

Hi Konrad,

I am struggeling with a similar issue in one of my plugins (WP Access Areas).
I would propose to filter the WHERE and JOIN clause similar to getarchives_where in function wp_get_archives(). I'll submit a patch instantly.

@podpirate
10 years ago

Filter where and join clause in get_calendar()

#3 @kkarpieszuk
10 years ago

For me this, what @podpirate proposed looks good and solving also my problem

#4 @SergeyBiryukov
10 years ago

  • Milestone changed from Awaiting Review to 4.1

#5 @wonderboymusic
9 years ago

  • Keywords needs-patch added

I was about to commit this, then realized: this can use date_query if we offer a more robust way to select fields as aliases.

If we keep it in WP_Query, the args can be filtered, instead of having to filter SQL fragments.

#6 @johnbillion
9 years ago

  • Milestone changed from 4.1 to Awaiting Review
  • Version changed from 4.0 to 3.0

I'm not a fan of introducing a bunch of SQL query filters for the calendar. I'd rather look into switching this over to a date query as wonderboymusic suggested.

@podpirate
9 years ago

use get_posts() in get_calendar()

#7 @podpirate
9 years ago

I just submitted a patch where get_calendar makes use of get_posts rather than custom built SQL. Works fine with my plugin out of the box (@kkarpieszuk and hopefully with WPML as well).

#8 @chriscct7
8 years ago

  • Keywords needs-unit-tests added

#9 @SergeyBiryukov
7 years ago

#15202 was marked as a duplicate.

#10 @SergeyBiryukov
7 years ago

#40059 was marked as a duplicate.

#11 @BackuPs
7 years ago

regarding https://core.trac.wordpress.org/ticket/40059

Hi

Thanks for the heads up on this. But that ticket is already open for more then 3 years. Is there any progress on this?

Looking forward to your reply on that.

#12 follow-up: @kkarpieszuk
7 years ago

Hi, sorry. Notification about this ticket didn't hit my mail inbox.

I see @podpirate prepared working patch. Could this be commtied?

#13 in reply to: ↑ 12 @BackuPs
7 years ago

Replying to kkarpieszuk:

Hi, sorry. Notification about this ticket didn't hit my mail inbox.

I see @podpirate prepared working patch. Could this be commtied?

Yes the @podpirate solution works fine. I unregistered the default wordpress calendar widget and added my own calling that function with his adjustments. It works just fine and is picking up any pre_get_post action.

It is unbelievable that the wordpress core team did not fix this issue for such a long time. But i see many other issues reported and acknowledged which also do not get fixed for even a longer period of time. I wonder what is withholding them for fixing this.

#14 @SergeyBiryukov
7 years ago

  • Keywords has-patch added; needs-patch removed
  • Milestone changed from Awaiting Review to 4.8

#15 @BackuPs
7 years ago

There is one flaw in @podpirate fix code fix. When there are no posts found the calendar does not show al all.

This code should be changed to have at least show the calendar with todays date

<?php
if ( ! $gotsome ) {
        //$cache[ $key ] = '';
        //wp_cache_set( 'theme_get_calendar', $cache, 'theme_calendar' );
        //return;
}

It is the same with the default wp calendar as it comes out of the box.

Last edited 7 years ago by BackuPs (previous) (diff)

This ticket was mentioned in Slack in #core by jeffpaul. View the logs.


7 years ago

#17 @SergeyBiryukov
7 years ago

  • Keywords needs-refresh added

This ticket was mentioned in Slack in #core by jeffpaul. View the logs.


7 years ago

#19 @jbpaul17
7 years ago

  • Milestone changed from 4.8 to 4.8.1

Punting to 4.8.1 per bug scrub earlier this week.

#20 @westonruter
7 years ago

  • Milestone changed from 4.8.1 to 4.9

This ticket was mentioned in Slack in #core by jeffpaul. View the logs.


7 years ago

This ticket was mentioned in Slack in #core by jeffpaul. View the logs.


6 years ago

#23 @westonruter
6 years ago

  • Milestone changed from 4.9 to Future Release

#24 @BackuPs
4 years ago

Hi

Since in wp.5.4 the calendar widget is made html 5.1 compatible can this also get resolved please?

Thank you

#25 @audrasjb
4 years ago

  • Milestone changed from Future Release to 5.5

Thanks for the heads up @BackuPs !

This should have been worked on during 5.4 alpha development cycle, but I'm milestoning this for 5.5.
It will need some work (patch refresh) to actually land in 5.5, so a new patch with unit tests would be welcome!

#26 @BackuPs
4 years ago

Here is the worked out solution using get_posts

<?php
        function get_calendar( $initial = true, $echo = true ) {
                global $wpdb, $m, $monthnum, $year, $wp_locale, $posts;

                $key = md5( $m . $monthnum . $year );
                $cache = wp_cache_get( 'get_calendar', 'calendar' );

                if ( $cache && is_array( $cache ) && isset( $cache[ $key ] ) ) {
                        /** This filter is documented in wp-includes/general-template.php */
                        $output = apply_filters( 'get_calendar', $cache[ $key ] );

                        if ( $echo ) {
                                echo $output;
                                return;
                        }

                        return $output;
                }

                if ( ! is_array( $cache ) ) {
                        $cache = array();
                }

                // Quick check. If we have no posts at all, abort!
                if ( ! $posts ) {
                        $gotsome = (bool) get_posts(array(
                                'fields'=> 'ids',
                                'suppress_filters'=> false,
                                'posts_per_page'=> 1,
                        ));
                        if ( ! $gotsome ) {
                                $nothing_found=__( 'No Calendar Posts Found !');
                                // Make $nothing_found empty to still show the calendar even if there are no posts
                                $nothing_found = apply_filters( 'get_calendar_nothing_found', $nothing_found );
                                if (!empty($nothing_found)) {
                                        $cache[ $key ] = $nothing_found;
                                        wp_cache_set( 'get_calendar', $cache, 'calendar' );
                                        if ( $echo ) {
                                                echo $nothing_found;
                                                return;
                                        } else   return $nothing_found;
                                }
                        }
                }

                if ( isset( $_GET['w'] ) ) {
                        $w = (int) $_GET['w'];
                }
                // week_begins = 0 stands for Sunday.
                $week_begins = (int) get_option( 'start_of_week' );
                $ts = current_time( 'timestamp' );

                // Let's figure out when we are.
                if ( ! empty( $monthnum ) && ! empty( $year ) ) {
                        $thismonth = zeroise( intval( $monthnum ), 2 );
                        $thisyear = (int) $year;
                } elseif ( ! empty( $w ) ) {
                        // We need to get the month from MySQL.
                        $thisyear = (int) substr( $m, 0, 4 );
                        //it seems MySQL's weeks disagree with PHP's.
                        $d = ( ( $w - 1 ) * 7 ) + 6;
                        $thismonth = $wpdb->get_var("SELECT DATE_FORMAT((DATE_ADD('{$thisyear}0101', INTERVAL $d DAY) ), '%m')");
                } elseif ( ! empty( $m ) ) {
                        $thisyear = (int) substr( $m, 0, 4 );
                        if ( strlen( $m ) < 6 ) {
                                $thismonth = '01';
                        } else {
                                $thismonth = zeroise( (int) substr( $m, 4, 2 ), 2 );
                        }
                } else {
                        $thisyear = current_time( 'Y' );
                        $thismonth = current_time( 'm' );
                }

                $unixmonth = mktime( 0, 0 , 0, $thismonth, 1, $thisyear );
                $last_day = gmdate( 't', $unixmonth );

                $previous_sheet_posts = get_posts( 
                        array(
                                'post_type' => 'post',
                                'post_status' => 'publish',
                                'orderby' => 'date',
                                'order' => 'DESC',
                                'posts_per_page' => 1,
                                'suppress_filters' => false,
                                'date_query'=> array(
                                        'before' => array(
                                                'month' =>$thismonth,
                                                'year'=>$thisyear,
                                        ),
                                ),
                        ) 
                );
                $next_sheet_posts = get_posts( 
                        array(
                                'post_type' => 'post',
                                'post_status' => 'publish',
                                'orderby' => 'date',
                                'order' => 'ASC',
                                'posts_per_page' => 1,
                                'suppress_filters' => false,
                                'date_query'=> array(
                                        'after' => array(
                                                'month' =>$thismonth,
                                                'year'=>$thisyear,
                                        ),
                                ),
                        ) 
                );
                /* translators: Calendar caption: 1: month name, 2: 4-digit year */
                $calendar_caption = _x('%1$s %2$s', 'calendar caption','');
                $calendar_output = '<table id="wp-calendar">
                <caption class="calendar-caption">' . sprintf(
                        $calendar_caption,
                        $wp_locale->get_month( $thismonth ),
                        gmdate( 'Y', $unixmonth )
                ) . '</caption>
                <thead class="calendar-head">
                <tr class="calendar-days">';

                $myweek = array();

                for ( $wdcount = 0; $wdcount <= 6; $wdcount++ ) {
                        $myweek[] = $wp_locale->get_weekday( ( $wdcount + $week_begins ) % 7 );
                }

                foreach ( $myweek as $wd ) {
                        $day_name = $initial ? $wp_locale->get_weekday_initial( $wd ) : $wp_locale->get_weekday_abbrev( $wd );
                        $wd = esc_attr( $wd );
                        $calendar_output .= "\n\t\t<th class=\"calendar-day\" scope=\"col\" title=\"$wd\">$day_name</th>";
                }

                $calendar_output .= '
                </tr>
                </thead>

                <tbody class="calendar-body">
                <tr>';

                $posts_of_month_titles = array();
                $posts_of_month = get_posts( 
                        array(
                                'post_type' => 'post',
                                'post_status' => 'publish',
                                'orderby' => 'date',
                                'order' => 'ASC',
                                'posts_per_page' => -1,
                                'suppress_filters' => false,
                                'date_query'=> array(
                                        'month' =>$thismonth,
                                        'year'=>$thisyear,
                                ),
                        )
                );
                
                if ( (bool) $posts_of_month ) {
                        foreach ( $posts_of_month as $post ) {
                                $post_date = date_parse($post->post_date);
                                $post_day = $post_date['day'];

                                /** This filter is documented in wp-includes/post-template.php */
                                $post_title = esc_attr( apply_filters( 'the_title', $post->post_title, $post->ID ) );

                                if ( ! isset( $posts_of_month_titles[ $post_day ] ) )
                                $posts_of_month_titles[ $post_day ] = array();

                                $posts_of_month_titles[ $post_day ][] = $post_title;
                        }
                }
                // See how much we should pad in the beginning.
                $pad = calendar_week_mod( gmdate( 'w', $unixmonth ) - $week_begins );
                if ( 0 != $pad ) {
                        $calendar_output .= "\n\t\t".'<td colspan="'. esc_attr( $pad ) .'" class="pad">&nbsp;</td>';
                }

                $newrow = false;
                $daysinmonth = (int) gmdate( 't', $unixmonth );

                for ( $day = 1; $day <= $daysinmonth; ++$day ) {
                        if ( isset($newrow) && $newrow ) {
                                $calendar_output .= "\n\t</tr>\n\t<tr>\n\t\t";
                        }
                        $newrow = false;

                        if ( current_time( 'j' ) == $day &&
                                current_time( 'm' ) == $thismonth &&
                                current_time( 'Y' ) == $thisyear ) {
                                if ( array_key_exists( $day , $posts_of_month_titles ) ) {
                                        $calendar_output .= '<td id="today" class="day-has-post">';
                                } else $calendar_output .= '<td id="today" class="calendar-is-today">';
                        } else {
                                if ( array_key_exists( $day , $posts_of_month_titles ) ) {
                                        $calendar_output .= '<td  class="day-has-post">';
                                } else $calendar_output .= '<td>';
                        }

                        if ( array_key_exists( $day , $posts_of_month_titles ) ) {
                                // any posts today?
                                $date_format = gmdate( _x( 'F j, Y', 'daily archives date format','' ), strtotime( "{$thisyear}-{$thismonth}-{$day}" ) );
                                /* translators: Post calendar label. %s: Date */
                                $label = sprintf( __( 'Posts published on %s','' ), $date_format);
                                $calendar_output .= sprintf(
                                        '<a href="%s" aria-label="%s">%s</a>',
                                        get_day_link( $thisyear, $thismonth, $day ),
                                        esc_attr( $label ),
                                        $day
                                );
                        } else {
                                $calendar_output .= $day;
                        }
                        $calendar_output .= '</td>';

                        if ( 6 == calendar_week_mod( gmdate( 'w', mktime(0, 0 , 0, $thismonth, $day, $thisyear ) ) - $week_begins ) ) {
                                $newrow = true;
                        }
                }

                $pad = 7 - calendar_week_mod( gmdate( 'w', mktime( 0, 0 , 0, $thismonth, $day, $thisyear ) ) - $week_begins );
                if ( 0 != $pad && 7 != $pad ) {
                        $calendar_output .= "\n\t\t".'<td class="pad" colspan="'. esc_attr( $pad ) .'">&nbsp;</td>';
                }
                $calendar_output .= "\n\t</tr>\n\t</tbody>";
                $calendar_output .= "\n\t</table>";
                
                $calendar_output .= '<nav aria-label="' . __( 'Previous and next months' ) . '" class="calendar-footer">';

                if ( (bool) $previous_sheet_posts ) {
                        $previous = (object) date_parse( $previous_sheet_posts[0]->post_date );
                        $calendar_output .= "\n\t\t" . '<span id="prev" class="calendar-prev-next-wrap"><a href="' . get_month_link( $previous->year, $previous->month ) . '" class="calendar-prev-next">&nbsp;&nbsp;&laquo; ' .
                                $wp_locale->get_month_abbrev( $wp_locale->get_month( $previous->month ) ) .
                        '</a></span>';
                } else {
                        $calendar_output .= "\n\t\t" . '<span id="prev calendar-prev-nex-wrapt">&nbsp;</span>';
                }

                $calendar_output .= "\n\t\t" . '<span class="pad">&nbsp;</span>';

                if ( (bool) $next_sheet_posts ) {
                        $next = (object) date_parse( $next_sheet_posts[0]->post_date );
                        $calendar_output .= "\n\t\t" . '<span id="next" class="calendar-prev-next-wrap"><a href="' . get_month_link( $next->year, $next->month ) . '" class="calendar-prev-next">' .
                                $wp_locale->get_month_abbrev( $wp_locale->get_month( $next->month ) ) .
                        ' &raquo;&nbsp;&nbsp;</a></span>';
                } else {
                        $calendar_output .= "\n\t\t" . '<span id="next" class="pad calendar-prev-next-wrap">&nbsp;</span>';
                }

                $calendar_output .= '
                </nav>';

                $cache[ $key ] = $calendar_output;
                wp_cache_set( 'get_calendar', $cache, 'calendar' );

                if ( $echo ) {
                        /**
                         * Filters the HTML calendar output.
                         *
                         * @since 3.0.0
                         *
                         * @param string $calendar_output HTML output of the calendar.
                         */
                        echo apply_filters( 'get_calendar', $calendar_output );
                        return;
                }
                /** This filter is documented in wp-includes/general-template.php */
                return apply_filters( 'get_calendar', $calendar_output );
        }
Last edited 4 years ago by BackuPs (previous) (diff)

#27 @SergeyBiryukov
4 years ago

  • Owner set to SergeyBiryukov
  • Status changed from new to reviewing

This ticket was mentioned in Slack in #core by david.baumwald. View the logs.


4 years ago

#29 @SergeyBiryukov
4 years ago

  • Milestone changed from 5.5 to 5.6

This ticket was mentioned in Slack in #core by hellofromtonya. View the logs.


3 years ago

#31 @hellofromTonya
3 years ago

Capturing notes from scrub.

Per Sergey:

I think these tests could also be converted to use a data provider instead of a bunch of separate functions.

I'll try to refresh and commit before beta or punt otherwise

#32 @helen
3 years ago

  • Milestone changed from 5.6 to Future Release

No updated patch, staring down beta 1 for 5.6, punting (again), sorry :(

Note: See TracTickets for help on using tickets.