Ticket #26937: 26937.2.patch
File 26937.2.patch, 14.5 KB (added by , 7 years ago) |
---|
-
src/wp-includes/link-template.php
1128 1128 * @return mixed Post object if successful. Null if global $post is not set. Empty string if no corresponding post exists. 1129 1129 */ 1130 1130 function get_adjacent_post( $in_same_term = false, $excluded_terms = '', $previous = true, $taxonomy = 'category' ) { 1131 global $wpdb; 1132 1133 if ( ( ! $post = get_post() ) || ! taxonomy_exists( $taxonomy ) ) 1131 if ( ( ! $post = get_post() ) || ! taxonomy_exists( $taxonomy ) ) { 1134 1132 return null; 1133 } 1135 1134 1136 $current_post_date = $post->post_date;1135 $current_post_date = get_post_field( 'post_date', $post ); 1137 1136 1138 $join = ''; 1139 $posts_in_ex_terms_sql = ''; 1140 if ( $in_same_term || ! empty( $excluded_terms ) ) { 1141 $join = " INNER JOIN $wpdb->term_relationships AS tr ON p.ID = tr.object_id INNER JOIN $wpdb->term_taxonomy tt ON tr.term_taxonomy_id = tt.term_taxonomy_id"; 1137 // Query basics 1138 $args = array( 1139 'posts_per_page' => 1, 1140 'post_status' => 'publish', 1141 'post_type' => 'post', 1142 'orderby' => 'date', 1143 'order' => $previous ? 'DESC' : 'ASC', 1144 'no_found_rows' => true, 1145 'cache_results' => true, 1146 'suppress_filters' => false, 1147 'date_query' => array(), 1148 ); 1142 1149 1143 if ( $in_same_term ) { 1144 if ( ! is_object_in_taxonomy( $post->post_type, $taxonomy ) ) 1145 return ''; 1146 $term_array = wp_get_object_terms( $post->ID, $taxonomy, array( 'fields' => 'ids' ) ); 1147 if ( ! $term_array || is_wp_error( $term_array ) ) 1148 return ''; 1149 $join .= $wpdb->prepare( " AND tt.taxonomy = %s AND tt.term_id IN (" . implode( ',', array_map( 'intval', $term_array ) ) . ")", $taxonomy ); 1150 // Query pieces 1151 $tax_query = array(); 1152 1153 // Set up for requests limited to posts that share terms 1154 if ( $in_same_term ) { 1155 $terms = get_the_terms( get_the_ID(), $taxonomy ); 1156 1157 if ( is_array( $terms ) && ! empty( $terms ) ) { 1158 $terms = wp_list_pluck( $terms, 'term_id' ); 1159 $terms = array_values( $terms ); 1160 $terms = array_map( 'intval', $terms ); 1161 } else { 1162 unset( $terms ); 1150 1163 } 1164 } 1151 1165 1152 $posts_in_ex_terms_sql = $wpdb->prepare( "AND tt.taxonomy = %s", $taxonomy ); 1153 if ( ! empty( $excluded_terms ) ) { 1154 if ( ! is_array( $excluded_terms ) ) { 1155 // back-compat, $excluded_terms used to be $excluded_terms with IDs separated by " and " 1156 if ( false !== strpos( $excluded_terms, ' and ' ) ) { 1157 _deprecated_argument( __FUNCTION__, '3.3', sprintf( __( 'Use commas instead of %s to separate excluded terms.' ), "'and'" ) ); 1158 $excluded_terms = explode( ' and ', $excluded_terms ); 1159 } else { 1160 $excluded_terms = explode( ',', $excluded_terms ); 1161 } 1166 // Handle excluded terms 1167 if ( ! empty( $excluded_terms ) ) { 1168 if ( ! is_array( $excluded_terms ) ) { 1169 // back-compat, $excluded_terms used to be IDs separated by " and " 1170 if ( false !== strpos( $excluded_terms, ' and ' ) ) { 1171 _deprecated_argument( __FUNCTION__, '3.3', sprintf( __( 'Use commas instead of %s to separate excluded terms.' ), "'and'" ) ); 1172 $excluded_terms = explode( ' and ', $excluded_terms ); 1173 } else { 1174 $excluded_terms = explode( ',', $excluded_terms ); 1162 1175 } 1176 } 1163 1177 1164 1178 $excluded_terms = array_map( 'intval', $excluded_terms ); 1165 1179 1166 if ( ! empty( $term_array ) ) { 1167 $excluded_terms = array_diff( $excluded_terms, $term_array ); 1168 $posts_in_ex_terms_sql = ''; 1169 } 1180 $tax_query[] = array( 1181 'taxonomy' => $tax_query, 1182 'slugs' => $excluded_terms, 1183 'compare' => 'NOT IN', 1184 ); 1185 } 1170 1186 1171 if ( ! empty( $excluded_terms ) ) { 1172 $posts_in_ex_terms_sql = $wpdb->prepare( " AND tt.taxonomy = %s AND tt.term_id NOT IN (" . implode( $excluded_terms, ',' ) . ')', $taxonomy ); 1173 } 1187 // If requesting same term, ensure that excluded terms don't appear in term list list 1188 if ( isset( $terms ) ) { 1189 if ( isset( $excluded_terms ) && is_array( $excluded_terms ) ) { 1190 $terms = array_diff( $terms, $excluded_terms ); 1174 1191 } 1192 1193 if ( ! empty( $terms ) ) { 1194 $tax_query[] = array( 1195 'taxonomy' => $taxonomy, 1196 'terms' => $terms, 1197 ); 1198 } 1175 1199 } 1176 1200 1177 $adjacent = $previous ? 'previous' : 'next'; 1178 $op = $previous ? '<' : '>'; 1179 $order = $previous ? 'DESC' : 'ASC'; 1201 // If we have a tax query, add it to our query args 1202 if ( ! empty( $tax_query ) ) { 1203 $args['tax_query'] = $tax_query; 1204 } 1180 1205 1181 $join = apply_filters( "get_{$adjacent}_post_join", $join, $in_same_term, $excluded_terms ); 1182 $where = apply_filters( "get_{$adjacent}_post_where", $wpdb->prepare( "WHERE p.post_date $op %s AND p.post_type = %s AND p.post_status = 'publish' $posts_in_ex_terms_sql", $current_post_date, $post->post_type), $in_same_term, $excluded_terms ); 1183 $sort = apply_filters( "get_{$adjacent}_post_sort", "ORDER BY p.post_date $order LIMIT 1" ); 1206 // And now, the date constraint 1207 if ( $previous ) { 1208 $args['date_query'][] = array( 1209 'before' => $current_post_date, 1210 'inclusive' => true, 1211 ); 1212 } else { 1213 $args['date_query'][] = array( 1214 'after' => $current_post_date, 1215 'inclusive' => true, 1216 ); 1217 } 1184 1218 1185 $query = "SELECT p.ID FROM $wpdb->posts AS p $join $where $sort"; 1186 $query_key = 'adjacent_post_' . md5( $query ); 1187 $result = wp_cache_get( $query_key, 'counts' ); 1188 if ( false !== $result ) { 1189 if ( $result ) 1190 $result = get_post( $result ); 1191 return $result; 1219 // Ensure the current post isn't returned, since we're using an inclusive date query 1220 $args['post__not_in'] = array( get_the_ID() ); 1221 1222 /** 1223 * Let plugins modify the parameters for the adjacent post query 1224 * 1225 * @since 3.9.0 1226 * 1227 * @param array $args WP_Query arguments 1228 * @param array Arguments when `get_adjacent_post()` was called 1229 */ 1230 $args = apply_filters( 'get_adjacent_post_args', $args, compact( 'in_same_term', 'excluded_terms', 'previous', 'taxonony' ) ); 1231 1232 // Get the posts and return either the post object or null 1233 WP_Legacy_Adjacent_Post_Filters::add( $previous, $in_same_term, $excluded_terms ); 1234 $results = get_posts( $args ); 1235 WP_Legacy_Adjacent_Post_Filters::remove(); 1236 1237 if ( is_array( $results ) && ! empty( $results ) ) { 1238 return array_shift( $results ); 1239 } else { 1240 return ''; 1192 1241 } 1242 } 1193 1243 1194 $result = $wpdb->get_var( $query ); 1195 if ( null === $result ) 1196 $result = ''; 1244 /** 1245 * BACKWARDS COMPATIBILITY FOR FILTERS PREVIOUSLY IN `get_adjacent_post()` 1246 * 1247 * Before `get_adjacent_post()` was updated to use `WP_Query`, the SQL it produced was passed through various filters. 1248 * The following class applies those filters to the relevant clauses produced by `WP_Query` via the latter's filters. 1249 * The class also makes the data available that the previous filters provided. 1250 */ 1251 class WP_Legacy_Adjacent_Post_Filters { 1252 private static $adjacent; 1253 private static $in_same_term; 1254 private static $excluded_terms; 1197 1255 1198 wp_cache_set( $query_key, $result, 'counts' ); 1256 /** 1257 * Set up variables and filter the clauses for WP_Query 1258 * 1259 * @param bool $previous 1260 * @param bool $in_same_term 1261 * @param array $excluded_terms 1262 * @return null 1263 */ 1264 public static function add( $previous, $in_same_term, $excluded_terms ) { 1265 self::$adjacent = $previous ? 'previous' : 'next'; 1266 self::$in_same_term = $in_same_term; 1267 self::$excluded_terms = $excluded_terms; 1199 1268 1200 if ( $result )1201 $result = get_post( $result );1269 add_filter( 'posts_clauses', array( __CLASS__, 'filter' ) ); 1270 } 1202 1271 1203 return $result; 1272 /** 1273 * Clean up after ourselves 1274 * 1275 * @return null 1276 */ 1277 public static function remove() { 1278 remove_filter( 'posts_clauses', array( __CLASS__, 'filter' ) ); 1279 } 1280 1281 /** 1282 * Apply the legacy filters to WP_Query's clauses 1283 * 1284 * @param array $clauses 1285 * @filter post_clauses 1286 * @return array 1287 */ 1288 public static function filter( $clauses ) { 1289 // The `join` and `where` filters are identical in their parameters, so we can use the same approach for both 1290 foreach ( array( 'join', 'where', ) as $clause ) { 1291 if ( has_filter( 'get_' . self::$adjacent . '_post_' . $clause ) ) { 1292 $clauses[ $clause ] = apply_filters( 'get_' . self::$adjacent . '_post_' . $clause, $clauses[ $clause ], self::$in_same_term, self::$excluded_terms ); 1293 } 1294 } 1295 1296 // The legacy `sort` filter combined the ORDER BY and LIMIT clauses, while `WP_Query` does not, which makes this a bit complicated 1297 if ( has_filter( 'get_' . self::$adjacent . '_post_sort' ) ) { 1298 $sort = apply_filters( 'get_' . self::$adjacent . '_post_sort', 'ORDER BY ' . $clauses['orderby'] . ' ' . $clauses['limits'] ); 1299 1300 if ( ! empty( $sort ) ) { 1301 // The legacy filter could allow either clause to be removed, or their order inverted, so we need to know what we have and where. 1302 $has_order_by = stripos( $sort, 'order by' ); 1303 $has_limit = stripos( $sort, 'limit' ); 1304 1305 // Split the string of one or two clauses into their respective array keys 1306 if ( false !== $has_order_by && false !== $has_limit ) { 1307 // The LIMIT clause cannot appear before the ORDER BY clause in a valid query 1308 // However, since the legacy filter would allow a user to invert the order, we maintain that handling so the same errors are triggered. 1309 if ( $has_order_by < $has_limit ) { 1310 $clauses['orderby'] = trim( str_ireplace( 'order by', '', substr( $sort, 0, $has_limit ) ) ); 1311 $clauses['limits'] = trim( substr( $sort, $has_limit ) ); 1312 } else { 1313 $clauses['orderby'] = trim( str_ireplace( 'order by', '', substr( $sort, $has_order_by ) ) ); 1314 $clauses['limits'] = trim( substr( $sort, 0, $has_order_by ) ); 1315 } 1316 } elseif ( false !== $has_order_by ) { 1317 $clauses['orderby'] = trim( str_ireplace( 'order by', '', $sort ) ); 1318 $clauses['limits'] = ''; 1319 } elseif ( false !== $has_limit ) { 1320 $clauses['orderby'] = ''; 1321 $clauses['limits'] = trim( $sort ); 1322 } 1323 } 1324 } 1325 1326 return $clauses; 1327 } 1204 1328 } 1205 1329 1206 1330 /** -
tests/phpunit/tests/link.php
167 167 $this->assertEquals( array( $post_two ), get_boundary_post( true, '', true, 'post_tag' ) ); 168 168 $this->assertEquals( array( $post_four ), get_boundary_post( true, '', false, 'post_tag' ) ); 169 169 } 170 171 /** 172 * @ticket 26937 173 */ 174 function test_legacy_get_adjacent_post_filters() { 175 // Need some sample posts to test adjacency 176 $post_one = $this->factory->post->create_and_get( array( 177 'post_title' => 'First', 178 'post_date' => '2012-01-01 12:00:00' 179 ) ); 180 181 $post_two = $this->factory->post->create_and_get( array( 182 'post_title' => 'Second', 183 'post_date' => '2012-02-01 12:00:00' 184 ) ); 185 186 $post_three = $this->factory->post->create_and_get( array( 187 'post_title' => 'Third', 188 'post_date' => '2012-03-01 12:00:00' 189 ) ); 190 191 $post_four = $this->factory->post->create_and_get( array( 192 'post_title' => 'Fourth', 193 'post_date' => '2012-04-01 12:00:00' 194 ) ); 195 196 // Add some meta so we can join the postmeta table and query 197 add_post_meta( $post_three->ID, 'unit_test_meta', 'waffle' ); 198 199 // Test "where" filter for a previous post 200 add_filter( 'get_previous_post_where', array( $this, 'filter_previous_post_where' ) ); 201 $this->go_to( get_permalink( $post_three->ID ) ); 202 $this->assertEquals( $post_one->post_title, get_adjacent_post( false, null, true )->post_title ); 203 remove_filter( 'get_previous_post_where', array( $this, 'filter_previous_post_where' ) ); 204 205 // Test "where" filter for a next post 206 add_filter( 'get_next_post_where', array( $this, 'filter_next_post_where' ) ); 207 $this->go_to( get_permalink( $post_two->ID ) ); 208 $this->assertEquals( $post_four->post_title, get_adjacent_post( false, null, false )->post_title ); 209 remove_filter( 'get_next_post_where', array( $this, 'filter_next_post_where' ) ); 210 211 // Test "join" filter by joining the postmeta table and restricting by meta key 212 add_filter( 'get_next_post_join', array( $this, 'filter_next_post_join' ) ); 213 add_filter( 'get_next_post_where', array( $this, 'filter_next_post_where_with_join' ) ); 214 $this->go_to( get_permalink( $post_one->ID ) ); 215 $this->assertEquals( $post_three->post_title, get_adjacent_post( false, null, false )->post_title ); 216 remove_filter( 'get_next_post_join', array( $this, 'filter_next_post_join' ) ); 217 remove_filter( 'get_next_post_where', array( $this, 'filter_next_post_where_with_join' ) ); 218 219 // Test "sort" filter when modifying ORDER BY clause 220 add_filter( 'get_next_post_sort', array( $this, 'filter_next_post_sort' ) ); 221 $this->go_to( get_permalink( $post_one->ID ) ); 222 $this->assertEquals( $post_four->post_title, get_adjacent_post( false, null, false )->post_title ); 223 remove_filter( 'get_next_post_sort', array( $this, 'filter_next_post_sort' ) ); 224 225 // Test "sort" filter when modifying LIMIT clause 226 add_filter( 'get_next_post_sort', array( $this, 'filter_next_post_sort_limit' ) ); 227 $this->go_to( get_permalink( $post_one->ID ) ); 228 $this->assertEquals( $post_three->post_title, get_adjacent_post( false, null, false )->post_title ); 229 remove_filter( 'get_next_post_sort', array( $this, 'filter_next_post_sort_limit' ) ); 230 } 231 232 /** 233 * Filter callback for `test_legacy_get_adjacent_post_filters()` 234 */ 235 function filter_previous_post_where( $where ) { 236 $where .= " AND post_title !='Second'"; 237 return $where; 238 } 239 240 /** 241 * Filter callback for `test_legacy_get_adjacent_post_filters()` 242 */ 243 function filter_next_post_where( $where ) { 244 $where .= " AND post_title !='Third'"; 245 return $where; 246 } 247 248 /** 249 * Filter callback for `test_legacy_get_adjacent_post_filters()` 250 */ 251 function filter_next_post_join( $join ) { 252 global $wpdb; 253 254 $join .= " INNER JOIN {$wpdb->postmeta} ON {$wpdb->posts}.ID = {$wpdb->postmeta}.post_id"; 255 return $join; 256 } 257 258 /** 259 * Filter callback for `test_legacy_get_adjacent_post_filters()` 260 */ 261 function filter_next_post_where_with_join( $where ) { 262 global $wpdb; 263 264 $where .= " AND {$wpdb->postmeta}.meta_key = 'unit_test_meta'"; 265 return $where; 266 } 267 268 /** 269 * Filter callback for `test_legacy_get_adjacent_post_filters()` 270 */ 271 function filter_next_post_sort( $sort ) { 272 global $wpdb; 273 274 $sort = str_replace( $wpdb->posts . '.post_date', $wpdb->posts . '.post_title', $sort ); 275 return $sort; 276 } 277 278 /** 279 * Filter callback for `test_legacy_get_adjacent_post_filters()` 280 */ 281 function filter_next_post_sort_limit( $sort ) { 282 global $wpdb; 283 284 $sort = str_replace( 'LIMIT 0, 1', 'LIMIT 1, 2', $sort ); 285 return $sort; 286 } 170 287 } 288 No newline at end of file