PluginProbe
Polylang / trunk
Polylang vtrunk
3.8.9 3.8.8 3.8.7 3.8.6 3.8.5 3.8.4 3.8.3 2.7 2.7.0.1 2.7.1 2.7.2 2.7.3 2.7.4 2.8 2.8.1 2.8.2 2.8.3 2.8.4 2.9 2.9.1 2.9.2 3.0 3.0.1 3.0.2 3.0.3 All 233 releases
polylang / src / Widgets / Calendar.php

Calendar.php in Polylang trunk, at src/Widgets/Calendar.php

398 lines 11.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * @package Polylang
4 */
5
6 namespace WP_Syntex\Polylang\Widgets;
7
8 if ( ! class_exists( 'WP_Widget_Calendar' ) ) {
9 require_once ABSPATH . '/wp-includes/default-widgets.php';
10 }
11
12 /**
13 * This classes rewrite the whole Calendar widget functionality as there is no filter on sql queries and only a filter on final output.
14 * Code last checked: WP 7.2.
15 *
16 * A request to add filters on SQL queries exists: http://core.trac.wordpress.org/ticket/15202, closed in favor of https://core.trac.wordpress.org/ticket/29319.
17 * Method used in 0.4.x: use of the get_calendar filter and overwrite the output of get_calendar function -> not very efficient (add 4 to 5 sql queries).
18 * Method used since 0.5: remove the WP widget and replace it by our own -> our language filter will not work if get_calendar is called directly by a theme.
19 *
20 * @since 0.5
21 */
22 class Calendar extends \WP_Widget_Calendar {
23 protected static $pll_instance = 0; // Can't use $instance of WP_Widget_Calendar as it's private :/.
24
25 /**
26 * Outputs the content for the current Calendar widget instance.
27 * Modified version of the parent function to call our own get_calendar() method.
28 *
29 * @since 0.5
30 *
31 * @param array $args Display arguments including 'before_title', 'after_title',
32 * 'before_widget', and 'after_widget'.
33 * @param array $instance The settings for the particular instance of the widget.
34 */
35 public function widget( $args, $instance ) {
36 $title = ! empty( $instance['title'] ) ? $instance['title'] : '';
37
38 /** This filter is documented in wp-includes/widgets/class-wp-widget-pages.php */
39 $title = apply_filters( 'widget_title', $title, $instance, $this->id_base );
40
41 echo $args['before_widget'];
42 if ( $title ) {
43 echo $args['before_title'] . $title . $args['after_title'];
44 }
45 if ( 0 === self::$pll_instance ) { #modified#
46 echo '<div id="calendar_wrap" class="calendar_wrap">';
47 } else {
48 echo '<div class="calendar_wrap">';
49 }
50 empty( PLL()->curlang ) ? get_calendar() : self::get_calendar(); #modified#
51 echo '</div>';
52 echo $args['after_widget'];
53
54 ++self::$pll_instance; #modified#
55 }
56
57 /**
58 * Modified version of the WP `get_calendar()` function to filter the queries.
59 *
60 * @since 0.5
61 * @since 3.8 New argument $args added, with backward compatibility (WP 6.8).
62 *
63 * @param array $args {
64 * Optional. Arguments for the `get_calendar` function.
65 *
66 * @type bool $initial Whether to use initial calendar names. Default true.
67 * @type bool $display Whether to display the calendar output. Default true.
68 * @type string $post_type Optional. Post type. Default 'post'.
69 * }
70 * @return string|void Calendar HTML when `$display` is false, null when the site has
71 * no posts. Nothing otherwise.
72 * @phpstan-return ( $args is array{ display: false|0|''|'0', ... } ? string|null : void )
73 */
74 public static function get_calendar( $args = array() ) {
75 global $wpdb, $m, $monthnum, $year, $wp_locale, $posts;
76
77 $defaults = array(
78 'initial' => true,
79 'display' => true,
80 'post_type' => 'post',
81 );
82
83 $original_args = func_get_args();
84 $args = array();
85
86 if ( ! empty( $original_args ) ) {
87 if ( ! is_array( $original_args[0] ) ) {
88 if ( isset( $original_args[0] ) && is_bool( $original_args[0] ) ) {
89 $defaults['initial'] = $original_args[0];
90 }
91 if ( isset( $original_args[1] ) && is_bool( $original_args[1] ) ) {
92 $defaults['display'] = $original_args[1];
93 }
94 } else {
95 $args = $original_args[0];
96 }
97 }
98
99 /** This filter is documented in wp-includes/general-template.php */
100 $args = apply_filters( 'get_calendar_args', wp_parse_args( $args, $defaults ) );
101
102 $args['lang'] = PLL()->curlang->slug; #added#
103
104 if ( ! post_type_exists( $args['post_type'] ) ) {
105 $args['post_type'] = 'post';
106 }
107
108 $w = 0;
109 if ( isset( $_GET['w'] ) ) {
110 $w = (int) $_GET['w'];
111 }
112
113 /*
114 * Normalize the cache key.
115 *
116 * The following ensures the same cache key is used for the same parameter
117 * and parameter equivalents. This prevents `post_type > post, initial > true`
118 * from generating a different key from the same values in the reverse order.
119 *
120 * `display` is excluded from the cache key as the cache contains the same
121 * HTML regardless of this function's need to echo or return the output.
122 *
123 * The global values contain data generated by the URL query string variables.
124 */
125 $cache_args = $args;
126 unset( $cache_args['display'] );
127
128 $cache_args['globals'] = array(
129 'm' => $m,
130 'monthnum' => $monthnum,
131 'year' => $year,
132 'week' => $w,
133 );
134
135 wp_recursive_ksort( $cache_args );
136 $key = md5( serialize( $cache_args ) );
137 $cache = wp_cache_get( 'get_calendar', 'calendar' );
138
139 if ( $cache && is_array( $cache ) && isset( $cache[ $key ] ) ) {
140 /** This filter is documented in wp-includes/general-template.php */
141 $output = apply_filters( 'get_calendar', $cache[ $key ], $args );
142
143 if ( $args['display'] ) {
144 echo $output;
145 return;
146 }
147
148 return $output;
149 }
150
151 if ( ! is_array( $cache ) ) {
152 $cache = array();
153 }
154
155 $post_type = $args['post_type'];
156
157 // Quick check. If we have no posts at all, abort!
158 if ( ! $posts ) {
159 $gotsome = $wpdb->get_var(
160 $wpdb->prepare(
161 "SELECT 1 as test
162 FROM $wpdb->posts
163 WHERE post_type = %s
164 AND post_status = 'publish'
165 LIMIT 1",
166 $post_type
167 )
168 );
169
170 if ( ! $gotsome ) {
171 $cache[ $key ] = '';
172 wp_cache_set( 'get_calendar', $cache, 'calendar' );
173 return null;
174 }
175 }
176
177 // week_begins = 0 stands for Sunday.
178 $week_begins = (int) get_option( 'start_of_week' );
179
180 // Read the current date.
181 list( $current_year, $current_month, $current_day ) = array_map( 'intval', explode( '-', current_time( 'Y-m-j' ) ) );
182
183 // Let's figure out when we are.
184 if ( ! empty( $monthnum ) && ! empty( $year ) ) {
185 $thismonth = (int) $monthnum;
186 $thisyear = (int) $year;
187 } elseif ( ! empty( $w ) ) {
188 // We need to get the month from MySQL.
189 $thisyear = (int) substr( $m, 0, 4 );
190 // It seems MySQL's weeks disagree with PHP's.
191 $d = ( ( $w - 1 ) * 7 ) + 6;
192 $thismonth = (int) $wpdb->get_var(
193 $wpdb->prepare(
194 "SELECT DATE_FORMAT((DATE_ADD('%d0101', INTERVAL %d DAY) ), '%%m')",
195 $thisyear,
196 $d
197 )
198 );
199 } elseif ( ! empty( $m ) ) {
200 $thisyear = (int) substr( $m, 0, 4 );
201 if ( strlen( $m ) < 6 ) {
202 $thismonth = 1;
203 } else {
204 $thismonth = (int) substr( $m, 4, 2 );
205 }
206 } else {
207 $thisyear = $current_year;
208 $thismonth = $current_month;
209 }
210
211 $unixmonth = mktime( 0, 0, 0, $thismonth, 1, $thisyear );
212 $last_day = gmdate( 't', $unixmonth );
213
214 $join_clause = PLL()->model->post->join_clause(); #added#
215 $where_clause = PLL()->model->post->where_clause( PLL()->curlang ); #added#
216
217 // Get the next and previous month and year with at least one post.
218 $previous = $wpdb->get_row(
219 $wpdb->prepare(
220 "SELECT MONTH(post_date) AS month, YEAR(post_date) AS year
221 FROM $wpdb->posts $join_clause
222 WHERE post_date < '%d-%d-01'
223 AND post_type = %s AND post_status = 'publish' $where_clause
224 ORDER BY post_date DESC
225 LIMIT 1",
226 $thisyear,
227 zeroise( $thismonth, 2 ),
228 $post_type
229 )
230 ); #modified#
231
232 $next = $wpdb->get_row(
233 $wpdb->prepare(
234 "SELECT MONTH(post_date) AS month, YEAR(post_date) AS year
235 FROM $wpdb->posts $join_clause
236 WHERE post_date > '%d-%d-%d 23:59:59'
237 AND post_type = %s AND post_status = 'publish' $where_clause
238 ORDER BY post_date ASC
239 LIMIT 1",
240 $thisyear,
241 zeroise( $thismonth, 2 ),
242 $last_day,
243 $post_type
244 )
245 ); #modified#
246
247 /* translators: Calendar caption: 1: Month name, 2: 4-digit year. */
248 $calendar_caption = _x( '%1$s %2$s', 'calendar caption' );
249 $calendar_output = '<table id="wp-calendar" class="wp-calendar-table">
250 <caption>' . sprintf(
251 $calendar_caption,
252 $wp_locale->get_month( $thismonth ),
253 gmdate( 'Y', $unixmonth )
254 ) . '</caption>
255 <thead>
256 <tr>';
257
258 $myweek = array();
259
260 for ( $wdcount = 0; $wdcount <= 6; $wdcount++ ) {
261 $myweek[] = $wp_locale->get_weekday( ( $wdcount + $week_begins ) % 7 );
262 }
263
264 foreach ( $myweek as $wd ) {
265 $day_name = $args['initial'] ? $wp_locale->get_weekday_initial( $wd ) : $wp_locale->get_weekday_abbrev( $wd );
266 $wd = esc_attr( $wd );
267 $calendar_output .= "\n\t\t<th scope=\"col\" aria-label=\"$wd\">$day_name</th>";
268 }
269
270 $calendar_output .= '
271 </tr>
272 </thead>
273 <tbody>
274 <tr>';
275
276 $daywithpost = array();
277
278 // Get days with posts.
279 $dayswithposts = $wpdb->get_results(
280 $wpdb->prepare(
281 "SELECT DISTINCT DAYOFMONTH(post_date)
282 FROM $wpdb->posts $join_clause WHERE post_date >= '%d-%d-01 00:00:00'
283 AND post_type = %s AND post_status = 'publish'
284 AND post_date <= '%d-%d-%d 23:59:59' $where_clause",
285 $thisyear,
286 zeroise( $thismonth, 2 ),
287 $post_type,
288 $thisyear,
289 zeroise( $thismonth, 2 ),
290 $last_day
291 ),
292 ARRAY_N
293 ); #modified#
294
295 if ( $dayswithposts ) {
296 foreach ( (array) $dayswithposts as $daywith ) {
297 $daywithpost[] = (int) $daywith[0];
298 }
299 }
300
301 // See how much we should pad in the beginning.
302 $pad = calendar_week_mod( (int) gmdate( 'w', $unixmonth ) - $week_begins );
303 if ( $pad > 0 ) {
304 $calendar_output .= "\n\t\t" . '<td colspan="' . esc_attr( $pad ) . '" class="pad">&nbsp;</td>';
305 }
306
307 $newrow = false;
308 $daysinmonth = (int) gmdate( 't', $unixmonth );
309
310 for ( $day = 1; $day <= $daysinmonth; ++$day ) {
311 if ( $newrow ) {
312 $calendar_output .= "\n\t</tr>\n\t<tr>\n\t\t";
313 }
314
315 $newrow = false;
316
317 if ( $current_day === $day
318 && $current_month === $thismonth
319 && $current_year === $thisyear
320 ) {
321 $calendar_output .= '<td id="today">';
322 } else {
323 $calendar_output .= '<td>';
324 }
325
326 if ( in_array( $day, $daywithpost, true ) ) {
327 // Any posts today?
328 $date_format = gmdate( _x( 'F j, Y', 'daily archives date format' ), strtotime( "{$thisyear}-{$thismonth}-{$day}" ) );
329 /* translators: Post calendar label. %s: Date. */
330 $label = sprintf( __( 'Posts published on %s' ), $date_format );
331 $calendar_output .= sprintf(
332 '<a href="%s" aria-label="%s">%s</a>',
333 get_day_link( $thisyear, $thismonth, $day ),
334 esc_attr( $label ),
335 $day
336 );
337 } else {
338 $calendar_output .= $day;
339 }
340
341 $calendar_output .= '</td>';
342
343 if ( 6 === (int) calendar_week_mod( (int) gmdate( 'w', mktime( 0, 0, 0, $thismonth, $day, $thisyear ) ) - $week_begins ) ) {
344 $newrow = true;
345 }
346 }
347
348 $pad = 7 - calendar_week_mod( (int) gmdate( 'w', mktime( 0, 0, 0, $thismonth, $day, $thisyear ) ) - $week_begins );
349 if ( 0 < $pad && $pad < 7 ) {
350 $calendar_output .= "\n\t\t" . '<td class="pad" colspan="' . esc_attr( $pad ) . '">&nbsp;</td>';
351 }
352
353 $calendar_output .= "\n\t</tr>\n\t</tbody>";
354
355 $calendar_output .= "\n\t</table>";
356
357 $calendar_output .= '<nav aria-label="' . __( 'Previous and next months' ) . '" class="wp-calendar-nav">';
358
359 if ( $previous ) {
360 $calendar_output .= "\n\t\t" . sprintf(
361 '<span class="wp-calendar-nav-prev"><a href="%1$s">&laquo; %2$s</a></span>',
362 get_month_link( $previous->year, $previous->month ),
363 $wp_locale->get_month_abbrev( $wp_locale->get_month( $previous->month ) )
364 );
365 } else {
366 $calendar_output .= "\n\t\t" . '<span class="wp-calendar-nav-prev">&nbsp;</span>';
367 }
368
369 $calendar_output .= "\n\t\t" . '<span class="pad">&nbsp;</span>';
370
371 if ( $next ) {
372 $calendar_output .= "\n\t\t" . sprintf(
373 '<span class="wp-calendar-nav-next"><a href="%1$s">%2$s &raquo;</a></span>',
374 get_month_link( $next->year, $next->month ),
375 $wp_locale->get_month_abbrev( $wp_locale->get_month( $next->month ) )
376 );
377 } else {
378 $calendar_output .= "\n\t\t" . '<span class="wp-calendar-nav-next">&nbsp;</span>';
379 }
380
381 $calendar_output .= '
382 </nav>';
383
384 $cache[ $key ] = $calendar_output;
385 wp_cache_set( 'get_calendar', $cache, 'calendar' );
386
387 /** This filter is documented in wp-includes/general-template.php */
388 $calendar_output = apply_filters( 'get_calendar', $calendar_output, $args );
389
390 if ( $args['display'] ) {
391 echo $calendar_output;
392 return;
393 }
394
395 return $calendar_output;
396 }
397 }
398