Ticket #16173: calendar.2.diff
File calendar.2.diff, 18.7 KB (added by , 14 years ago) |
---|
-
wp-includes/default-widgets.php
327 327 class WP_Widget_Calendar extends WP_Widget { 328 328 329 329 function WP_Widget_Calendar() { 330 $widget_ops = array( 'classname' => 'widget_calendar', 'description' => __( 'A calendar of your site’s posts') );331 $this->WP_Widget( 'calendar', __('Calendar'), $widget_ops);330 $widget_ops = array( 'classname' => 'widget_calendar', 'description' => __( 'A calendar of your site’s posts' ) ); 331 $this->WP_Widget( 'calendar', __( 'Calendar' ), $widget_ops ); 332 332 } 333 333 334 334 function widget( $args, $instance ) { 335 extract($args); 336 $title = apply_filters('widget_title', empty($instance['title']) ? ' ' : $instance['title'], $instance, $this->id_base); 335 extract( $args ); 336 337 $title = apply_filters( 'widget_title', empty( $instance['title'] ) ? ' ' : $instance['title'], $instance, $this->id_base ); 338 $instance_post_type = $this->_get_instance_post_type( $instance ); 339 337 340 echo $before_widget; 341 338 342 if ( $title ) 339 echo $before_title . $title . $after_title; 340 echo '<div id="calendar_wrap">'; 341 get_calendar(); 342 echo '</div>'; 343 echo $before_title . $title . $after_title; ?> 344 345 <div id="calendar_wrap"> 346 347 <?php get_calendar( true, true, $instance_post_type ); ?> 348 349 </div> 350 351 <?php 352 343 353 echo $after_widget; 344 354 } 345 355 346 356 function update( $new_instance, $old_instance ) { 347 $instance = $old_instance; 348 $instance['title'] = strip_tags($new_instance['title']); 357 $instance = $old_instance; 358 $instance['title'] = strip_tags( $new_instance['title'] ); 359 $instance['post_type'] = stripslashes( $new_instance['post_type'] ); 349 360 350 361 return $instance; 351 362 } 352 363 353 364 function form( $instance ) { 354 $instance = wp_parse_args( (array) $instance, array( 'title' => '' ) ); 355 $title = strip_tags($instance['title']); 356 ?> 357 <p><label for="<?php echo $this->get_field_id('title'); ?>"><?php _e('Title:'); ?></label> 358 <input class="widefat" id="<?php echo $this->get_field_id('title'); ?>" name="<?php echo $this->get_field_name('title'); ?>" type="text" value="<?php echo esc_attr($title); ?>" /></p> 359 <?php 365 $instance = wp_parse_args( (array) $instance, array( 'title' => '' ) ); 366 $title = strip_tags( $instance['title'] ); 367 $instance_post_type = $this->_get_instance_post_type( $instance ); 368 ?> 369 370 <p> 371 <label for="<?php echo $this->get_field_id( 'title' ); ?>"><?php _e( 'Title:' ); ?></label> 372 <input class="widefat" id="<?php echo $this->get_field_id( 'title' ); ?>" name="<?php echo $this->get_field_name( 'title' ); ?>" type="text" value="<?php echo esc_attr( $title ); ?>" /> 373 </p> 374 375 <p> 376 <label for="<?php echo $this->get_field_id( 'post_type' ); ?>"><?php _e( 'Post Type:' ); ?></label> 377 <select class="widefat" id="<?php echo $this->get_field_id( 'post_type' ); ?>" name="<?php echo $this->get_field_name('post_type'); ?>"> 378 379 <?php foreach ( get_post_types( array(), 'objects' ) as $post_type ) : 380 if ( !post_type_supports( $post_type->name, 'calendar' ) ) 381 continue; 382 ?> 383 384 <option value="<?php echo esc_attr( $post_type->name ); ?>"<?php selected( $post_type->name, $instance_post_type ); ?>><?php echo $post_type->label; ?></option> 385 386 <?php endforeach; ?> 387 388 </select> 389 </p> 390 391 <?php 360 392 } 393 394 function _get_instance_post_type( $instance ) { 395 if ( !empty( $instance['post_type'] ) && post_type_exists( $instance['post_type'] ) && post_type_supports( $instance['post_type'], 'calendar' ) ) 396 return $instance['post_type']; 397 398 return 'post'; 399 } 361 400 } 362 401 363 402 /** -
wp-includes/general-template.php
1071 1071 * 1072 1072 * @param bool $initial Optional, default is true. Use initial calendar names. 1073 1073 * @param bool $echo Optional, default is true. Set to false for return. 1074 * @param string $post_type Optional, default is 'post'. Use to get a calendar 1075 * of a custom post type 1074 1076 */ 1075 function get_calendar( $initial = true, $echo = true) {1077 function get_calendar( $initial = true, $echo = true, $post_type = 'post' ) { 1076 1078 global $wpdb, $m, $monthnum, $year, $wp_locale, $posts; 1077 1079 1080 // Check if the post type exists 1081 if ( empty( $post_type ) || !post_type_exists( $post_type ) || !post_type_supports( $post_type, 'calendar' ) ) 1082 return false; 1083 1078 1084 $cache = array(); 1079 1085 $key = md5( $m . $monthnum . $year ); 1080 1086 if ( $cache = wp_cache_get( 'get_calendar', 'calendar' ) ) { … … 1088 1094 } 1089 1095 } 1090 1096 1091 if ( !is_array( $cache) )1097 if ( !is_array( $cache ) ) 1092 1098 $cache = array(); 1093 1099 1094 1100 // Quick check. If we have no posts at all, abort! 1095 1101 if ( !$posts ) { 1096 $gotsome = $wpdb->get_var( "SELECT 1 as test FROM $wpdb->posts WHERE post_type = 'post' AND post_status = 'publish' LIMIT 1");1102 $gotsome = $wpdb->get_var( $wpdb->prepare( "SELECT 1 as test FROM $wpdb->posts WHERE post_type = %s AND post_status = 'publish' LIMIT 1", $post_type ) ); 1097 1103 if ( !$gotsome ) { 1098 $cache[ $key] = '';1104 $cache[$key] = ''; 1099 1105 wp_cache_set( 'get_calendar', $cache, 'calendar' ); 1100 1106 return; 1101 1107 } 1102 1108 } 1103 1109 1104 if ( isset( $_GET['w']) )1105 $w = ''.intval( $_GET['w']);1110 if ( isset( $_GET['w'] ) ) 1111 $w = ''.intval( $_GET['w'] ); 1106 1112 1107 1113 // week_begins = 0 stands for Sunday 1108 $week_begins = intval( get_option('start_of_week'));1114 $week_begins = intval( get_option( 'start_of_week' ) ); 1109 1115 1110 1116 // Let's figure out when we are 1111 if ( !empty( $monthnum) && !empty($year) ) {1112 $thismonth = ''.zeroise(intval($monthnum), 2);1113 $thisyear = ''.intval($year);1114 } elseif ( !empty( $w) ) {1117 if ( !empty( $monthnum ) && !empty( $year ) ) { 1118 $thismonth = (string) zeroise( intval( $monthnum ), 2 ); 1119 $thisyear = (string) intval( $year ); 1120 } elseif ( !empty( $w ) ) { 1115 1121 // We need to get the month from MySQL 1116 $thisyear = ''.intval(substr($m, 0, 4));1117 $d = ( ($w - 1) * 7) + 6; //it seems MySQL's weeks disagree with PHP's1118 $thismonth = $wpdb->get_var( "SELECT DATE_FORMAT((DATE_ADD('{$thisyear}0101', INTERVAL $d DAY) ), '%m')");1119 } elseif ( !empty( $m) ) {1120 $thisyear = ''.intval(substr($m, 0, 4));1121 if ( strlen( $m) < 6 )1122 1122 $thisyear = (string) intval( substr( $m, 0, 4 ) ); 1123 $d = ( ( $w - 1 ) * 7 ) + 6; //it seems MySQL's weeks disagree with PHP's 1124 $thismonth = $wpdb->get_var( $wpdb->prepare( "SELECT DATE_FORMAT((DATE_ADD(%s, INTERVAL $d DAY) ), '%m')", $thisyear . '0101' ) ); 1125 } elseif ( !empty( $m ) ) { 1126 $thisyear = (string) intval( substr( $m, 0, 4 ) ); 1127 if ( strlen( $m ) < 6 ) 1128 $thismonth = '01'; 1123 1129 else 1124 $thismonth = ''.zeroise(intval(substr($m, 4, 2)), 2);1130 $thismonth = (string) zeroise( intval( substr( $m, 4, 2 ) ), 2 ); 1125 1131 } else { 1126 $thisyear = gmdate('Y', current_time('timestamp'));1127 $thismonth = gmdate( 'm', current_time('timestamp'));1132 $thisyear = gmdate( 'Y', current_time( 'timestamp' ) ); 1133 $thismonth = gmdate( 'm', current_time( 'timestamp' ) ); 1128 1134 } 1129 1135 1130 $unixmonth = mktime( 0, 0 , 0, $thismonth, 1, $thisyear);1131 $last_day = date('t', $unixmonth);1136 $unixmonth = mktime( 0, 0 , 0, $thismonth, 1, $thisyear ); 1137 $last_day = date( 't', $unixmonth ); 1132 1138 1133 1139 // Get the next and previous month and year with at least one post 1134 $previous = $wpdb->get_row( "SELECT MONTH(post_date) AS month, YEAR(post_date) AS year1135 FROM $wpdb->posts1136 WHERE post_date < '$thisyear-$thismonth-01'1137 AND post_type = 'post' AND post_status = 'publish'1138 ORDER BY post_date DESC1139 LIMIT 1");1140 $next = $wpdb->get_row("SELECT MONTH(post_date) AS month, YEAR(post_date) AS year1141 FROM $wpdb->posts1142 WHERE post_date > '$thisyear-$thismonth-{$last_day} 23:59:59'1143 AND post_type = 'post' AND post_status = 'publish'1144 ORDER BY post_date ASC1145 LIMIT 1");1140 $previous = $wpdb->get_row( 1141 $wpdb->prepare( 1142 "SELECT MONTH(post_date) AS month, YEAR(post_date) AS year 1143 FROM $wpdb->posts 1144 WHERE post_date < %s 1145 AND post_type = %s AND post_status = 'publish' 1146 ORDER BY post_date DESC 1147 LIMIT 1", 1148 $thisyear . '-' . $thismonth . '-01', 1149 $post_type 1150 ) 1151 ); 1146 1152 1153 $next = $wpdb->get_row( 1154 $wpdb->prepare( 1155 "SELECT MONTH(post_date) AS month, YEAR(post_date) AS year 1156 FROM $wpdb->posts 1157 WHERE post_date > %s 1158 AND post_type = %s AND post_status = 'publish' 1159 ORDER BY post_date ASC 1160 LIMIT 1", 1161 $thisyear . '-' . $thismonth . '-' . $last_day . ' 23:59:59', 1162 $post_type 1163 ) 1164 ); 1165 1147 1166 /* translators: Calendar caption: 1: month name, 2: 4-digit year */ 1148 $calendar_caption = _x( '%1$s %2$s', 'calendar caption');1149 $calendar_output = '<table id="wp-calendar" summary="' . esc_attr__('Calendar') . '">1150 <caption>' . sprintf( $calendar_caption, $wp_locale->get_month($thismonth), date('Y', $unixmonth)) . '</caption>1167 $calendar_caption = _x( '%1$s %2$s', 'calendar caption' ); 1168 $calendar_output = '<table id="wp-calendar" summary="' . esc_attr__( 'Calendar' ) . '"> 1169 <caption>' . sprintf( $calendar_caption, $wp_locale->get_month( $thismonth ), date( 'Y', $unixmonth ) ) . '</caption> 1151 1170 <thead> 1152 1171 <tr>'; 1153 1172 1154 1173 $myweek = array(); 1155 1174 1156 for ( $wdcount=0; $wdcount <=6; $wdcount++ ) {1157 $myweek[] = $wp_locale->get_weekday( ($wdcount+$week_begins)%7);1175 for ( $wdcount=0; $wdcount <= 6; $wdcount++ ) { 1176 $myweek[] = $wp_locale->get_weekday( ( $wdcount + $week_begins ) % 7 ); 1158 1177 } 1159 1178 1160 1179 foreach ( $myweek as $wd ) { 1161 $day_name = (true == $initial) ? $wp_locale->get_weekday_initial($wd) : $wp_locale->get_weekday_abbrev($wd);1162 $wd = esc_attr($wd);1180 $day_name = ( true == $initial ) ? $wp_locale->get_weekday_initial( $wd ) : $wp_locale->get_weekday_abbrev( $wd ); 1181 $wd = esc_attr( $wd ); 1163 1182 $calendar_output .= "\n\t\t<th scope=\"col\" title=\"$wd\">$day_name</th>"; 1164 1183 } 1165 1184 … … 1170 1189 <tfoot> 1171 1190 <tr>'; 1172 1191 1173 if ( $previous ) { 1174 $calendar_output .= "\n\t\t".'<td colspan="3" id="prev"><a href="' . get_month_link($previous->year, $previous->month) . '" title="' . esc_attr( sprintf(__('View posts for %1$s %2$s'), $wp_locale->get_month($previous->month), date('Y', mktime(0, 0 , 0, $previous->month, 1, $previous->year)))) . '">« ' . $wp_locale->get_month_abbrev($wp_locale->get_month($previous->month)) . '</a></td>'; 1175 } else { 1176 $calendar_output .= "\n\t\t".'<td colspan="3" id="prev" class="pad"> </td>'; 1177 } 1192 if ( $previous ) 1193 $calendar_output .= "\n\t\t" . '<td colspan="3" id="prev"><a href="' . get_month_link( $previous->year, $previous->month ) . '" title="' . esc_attr( sprintf(__( 'View posts for %1$s %2$s' ), $wp_locale->get_month( $previous->month ), date( 'Y', mktime( 0, 0 , 0, $previous->month, 1, $previous->year ) ) ) ) . '">« ' . $wp_locale->get_month_abbrev( $wp_locale->get_month( $previous->month ) ) . '</a></td>'; 1194 else 1195 $calendar_output .= "\n\t\t" . '<td colspan="3" id="prev" class="pad"> </td>'; 1178 1196 1179 $calendar_output .= "\n\t\t" .'<td class="pad"> </td>';1197 $calendar_output .= "\n\t\t" . '<td class="pad"> </td>'; 1180 1198 1181 if ( $next ) { 1182 $calendar_output .= "\n\t\t".'<td colspan="3" id="next"><a href="' . get_month_link($next->year, $next->month) . '" title="' . esc_attr( sprintf(__('View posts for %1$s %2$s'), $wp_locale->get_month($next->month), date('Y', mktime(0, 0 , 0, $next->month, 1, $next->year))) ) . '">' . $wp_locale->get_month_abbrev($wp_locale->get_month($next->month)) . ' »</a></td>'; 1183 } else { 1184 $calendar_output .= "\n\t\t".'<td colspan="3" id="next" class="pad"> </td>'; 1185 } 1199 if ( $next ) 1200 $calendar_output .= "\n\t\t" . '<td colspan="3" id="next"><a href="' . get_month_link( $next->year, $next->month ) . '" title="' . esc_attr( sprintf( __( 'View posts for %1$s %2$s' ), $wp_locale->get_month( $next->month ), date( 'Y', mktime( 0, 0 , 0, $next->month, 1, $next->year ) ) ) ) . '">' . $wp_locale->get_month_abbrev( $wp_locale->get_month( $next->month ) ) . ' »</a></td>'; 1201 else 1202 $calendar_output .= "\n\t\t" . '<td colspan="3" id="next" class="pad"> </td>'; 1186 1203 1187 1204 $calendar_output .= ' 1188 1205 </tr> … … 1192 1209 <tr>'; 1193 1210 1194 1211 // Get days with posts 1195 $dayswithposts = $wpdb->get_results("SELECT DISTINCT DAYOFMONTH(post_date) 1196 FROM $wpdb->posts WHERE post_date >= '{$thisyear}-{$thismonth}-01 00:00:00' 1197 AND post_type = 'post' AND post_status = 'publish' 1198 AND post_date <= '{$thisyear}-{$thismonth}-{$last_day} 23:59:59'", ARRAY_N); 1212 $dayswithposts = $wpdb->get_results( 1213 $wpdb->prepare( 1214 "SELECT DISTINCT DAYOFMONTH(post_date) 1215 FROM $wpdb->posts WHERE post_date >= %s 1216 AND post_type = %s AND post_status = 'publish' 1217 AND post_date <= %s", 1218 $thisyear . '-' . $thismonth . '-01 00:00:00', 1219 $post_type, 1220 $thisyear . '-' . $thismonth . '-' . $last_day . ' 23:59:59' 1221 ), 1222 ARRAY_N 1223 ); 1199 1224 if ( $dayswithposts ) { 1200 1225 foreach ( (array) $dayswithposts as $daywith ) { 1201 1226 $daywithpost[] = $daywith[0]; … … 1204 1229 $daywithpost = array(); 1205 1230 } 1206 1231 1207 if ( strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE') !== false || stripos($_SERVER['HTTP_USER_AGENT'], 'camino') !== false || stripos($_SERVER['HTTP_USER_AGENT'], 'safari') !== false)1232 if ( strpos( $_SERVER['HTTP_USER_AGENT'], 'MSIE' ) !== false || stripos( $_SERVER['HTTP_USER_AGENT'], 'camino' ) !== false || stripos( $_SERVER['HTTP_USER_AGENT'], 'safari' ) !== false ) 1208 1233 $ak_title_separator = "\n"; 1209 1234 else 1210 1235 $ak_title_separator = ', '; 1211 1236 1212 1237 $ak_titles_for_day = array(); 1213 $ak_post_titles = $wpdb->get_results("SELECT ID, post_title, DAYOFMONTH(post_date) as dom " 1214 ."FROM $wpdb->posts " 1215 ."WHERE post_date >= '{$thisyear}-{$thismonth}-01 00:00:00' " 1216 ."AND post_date <= '{$thisyear}-{$thismonth}-{$last_day} 23:59:59' " 1217 ."AND post_type = 'post' AND post_status = 'publish'" 1238 $ak_post_titles = $wpdb->get_results( 1239 $wpdb->prepare( 1240 "SELECT ID, post_title, DAYOFMONTH(post_date) as dom 1241 FROM $wpdb->posts 1242 WHERE post_date >= %s 1243 AND post_date <= %s 1244 AND post_type = %s AND post_status = 'publish'", 1245 $thisyear . '-' . $thismonth . '-01 00:00:00', 1246 $thisyear . '-' . $thismonth . '-' . $last_day . ' 23:59:59', 1247 $post_type 1248 ) 1218 1249 ); 1219 1250 if ( $ak_post_titles ) { 1220 1251 foreach ( (array) $ak_post_titles as $ak_post_title ) { 1221 1252 1222 1253 $post_title = esc_attr( apply_filters( 'the_title', $ak_post_title->post_title, $ak_post_title->ID ) ); 1223 1254 1224 if ( empty( $ak_titles_for_day['day_'.$ak_post_title->dom]) )1255 if ( empty( $ak_titles_for_day['day_'.$ak_post_title->dom] ) ) 1225 1256 $ak_titles_for_day['day_'.$ak_post_title->dom] = ''; 1226 if ( empty( $ak_titles_for_day["$ak_post_title->dom"]) ) // first one1257 if ( empty( $ak_titles_for_day["$ak_post_title->dom"] ) ) // first one 1227 1258 $ak_titles_for_day["$ak_post_title->dom"] = $post_title; 1228 1259 else 1229 1260 $ak_titles_for_day["$ak_post_title->dom"] .= $ak_title_separator . $post_title; … … 1232 1263 1233 1264 1234 1265 // See how much we should pad in the beginning 1235 $pad = calendar_week_mod( date('w', $unixmonth)-$week_begins);1266 $pad = calendar_week_mod( date( 'w', $unixmonth ) - $week_begins ); 1236 1267 if ( 0 != $pad ) 1237 $calendar_output .= "\n\t\t".'<td colspan="'. esc_attr( $pad) .'" class="pad"> </td>';1268 $calendar_output .= "\n\t\t".'<td colspan="'. esc_attr( $pad ) .'" class="pad"> </td>'; 1238 1269 1239 $daysinmonth = intval( date('t', $unixmonth));1270 $daysinmonth = intval( date( 't', $unixmonth ) ); 1240 1271 for ( $day = 1; $day <= $daysinmonth; ++$day ) { 1241 if ( isset( $newrow) && $newrow )1272 if ( isset( $newrow ) && $newrow ) 1242 1273 $calendar_output .= "\n\t</tr>\n\t<tr>\n\t\t"; 1243 1274 $newrow = false; 1244 1275 1245 if ( $day == gmdate( 'j', current_time('timestamp')) && $thismonth == gmdate('m', current_time('timestamp')) && $thisyear == gmdate('Y', current_time('timestamp')) )1276 if ( $day == gmdate( 'j', current_time( 'timestamp' ) ) && $thismonth == gmdate( 'm', current_time( 'timestamp' ) ) && $thisyear == gmdate( 'Y', current_time( 'timestamp' ) ) ) 1246 1277 $calendar_output .= '<td id="today">'; 1247 1278 else 1248 1279 $calendar_output .= '<td>'; 1249 1280 1250 if ( in_array( $day, $daywithpost) ) // any posts today?1251 $calendar_output .= '<a href="' . get_day_link( $thisyear, $thismonth, $day) . "\" title=\"" . esc_attr($ak_titles_for_day[$day]) . "\">$day</a>";1281 if ( in_array( $day, $daywithpost ) ) // any posts today? 1282 $calendar_output .= '<a href="' . get_day_link( $thisyear, $thismonth, $day ) . "\" title=\"" . esc_attr( $ak_titles_for_day[$day] ) . "\">$day</a>"; 1252 1283 else 1253 1284 $calendar_output .= $day; 1254 1285 $calendar_output .= '</td>'; 1255 1286 1256 if ( 6 == calendar_week_mod( date('w', mktime(0, 0 , 0, $thismonth, $day, $thisyear))-$week_begins) )1287 if ( 6 == calendar_week_mod( date( 'w', mktime( 0, 0 , 0, $thismonth, $day, $thisyear ) ) - $week_begins ) ) 1257 1288 $newrow = true; 1258 1289 } 1259 1290 1260 $pad = 7 - calendar_week_mod( date('w', mktime(0, 0 , 0, $thismonth, $day, $thisyear))-$week_begins);1291 $pad = 7 - calendar_week_mod( date( 'w', mktime( 0, 0 , 0, $thismonth, $day, $thisyear ) ) - $week_begins ); 1261 1292 if ( $pad != 0 && $pad != 7 ) 1262 $calendar_output .= "\n\t\t".'<td class="pad" colspan="' . esc_attr($pad) .'"> </td>';1293 $calendar_output .= "\n\t\t".'<td class="pad" colspan="' . esc_attr( $pad ) . '"> </td>'; 1263 1294 1264 1295 $calendar_output .= "\n\t</tr>\n\t</tbody>\n\t</table>"; 1265 1296 1266 $cache[ $key] = $calendar_output;1297 $cache[$key] = $calendar_output; 1267 1298 wp_cache_set( 'get_calendar', $cache, 'calendar' ); 1268 1299 1269 1300 if ( $echo ) 1270 echo apply_filters( 'get_calendar', 1301 echo apply_filters( 'get_calendar', $calendar_output ); 1271 1302 else 1272 return apply_filters( 'get_calendar', 1303 return apply_filters( 'get_calendar', $calendar_output ); 1273 1304 1274 1305 } 1275 1306 -
wp-includes/post.php
26 26 'hierarchical' => false, 27 27 'rewrite' => false, 28 28 'query_var' => false, 29 'supports' => array( 'title', 'editor', 'author', 'thumbnail', 'excerpt', 'trackbacks', 'custom-fields', 'comments', 'revisions', 'post-formats' ),29 'supports' => array( 'title', 'editor', 'author', 'thumbnail', 'excerpt', 'trackbacks', 'custom-fields', 'comments', 'revisions', 'post-formats', 'calendar' ), 30 30 ) ); 31 31 32 32 register_post_type( 'page', array( … … 1209 1209 * 1210 1210 * All features are directly associated with a functional area of the edit screen, such as the 1211 1211 * editor or a meta box: 'title', 'editor', 'comments', 'revisions', 'trackbacks', 'author', 1212 * 'excerpt', 'page-attributes', 'thumbnail', and 'custom-fields'.1212 * 'excerpt', 'page-attributes', 'thumbnail', 'calendar' and 'custom-fields'. 1213 1213 * 1214 1214 * Additionally, the 'revisions' feature dictates whether the post type will store revisions, 1215 * the 'calendar' feature will show the post type in the select box in the calendar widget, 1215 1216 * and the 'comments' feature dicates whether the comments count will show on the edit screen. 1216 1217 * 1217 1218 * @since 3.0.0