+ $query = "SELECT p.ID FROM $wpdb->posts AS p $join $where $sort";
+ $query_key = 'adjacent_post_' . md5($query);
+ $result = wp_cache_get($query_key, 'counts');
+ if ( false !== $result ) {
+ if ( $result )
+ $result = get_post( $result );
+ return $result;
+ }
+
+ $result = $wpdb->get_var( $query );
+ if ( null === $result )
+ $result = '';
+
+ wp_cache_set($query_key, $result, 'counts');
+
+ if ( $result )
+ $result = get_post( $result );
+
+ return $result;
+}
+
+/**
+ * Get adjacent post relational link.
+ *
+ * Can either be next or previous post relational link.
+ *
+ * @since 2.8.0
+ *
+ * @param string $title Optional. Link title format.
+ * @param bool $in_same_cat Optional. Whether link should be in a same category.
+ * @param array|string $excluded_categories Optional. Array or comma-separated list of excluded category IDs.
+ * @param bool $previous Optional, default is true. Whether to display link to previous or next post.
+ * @return string
+ */
+function get_adjacent_post_rel_link($title = '%title', $in_same_cat = false, $excluded_categories = '', $previous = true) {
+ if ( $previous && is_attachment() && $post = get_post() )
+ $post = get_post( $post->post_parent );
+ else
+ $post = get_adjacent_post( $in_same_cat, $excluded_categories, $previous );
+
+ if ( empty($post) )
+ return;
+
+ $post_title = the_title_attribute( array( 'echo' => false, 'post' => $post ) );
+
+ if ( empty( $post_title ) )
+ $post_title = $previous ? __('Previous Post') : __('Next Post');
+
+ $date = mysql2date(get_option('date_format'), $post->post_date);
+
+ $title = str_replace('%title', $post_title, $title);
+ $title = str_replace('%date', $date, $title);
+
+ $link = $previous ? "<link rel='prev' title='" : "<link rel='next' title='";
+ $link .= esc_attr( $title );
+ $link .= "' href='" . get_permalink($post) . "' />\n";
+
+ $adjacent = $previous ? 'previous' : 'next';
+ return apply_filters( "{$adjacent}_post_rel_link", $link );
+}
+
+/**
+ * Display relational links for the posts adjacent to the current post.
+ *
+ * @since 2.8.0
+ *
+ * @param string $title Optional. Link title format.
+ * @param bool $in_same_cat Optional. Whether link should be in a same category.
+ * @param array|string $excluded_categories Optional. Array or comma-separated list of excluded category IDs.
+ */
+function adjacent_posts_rel_link($title = '%title', $in_same_cat = false, $excluded_categories = '') {
+ echo get_adjacent_post_rel_link($title, $in_same_cat, $excluded_categories = '', true);
+ echo get_adjacent_post_rel_link($title, $in_same_cat, $excluded_categories = '', false);
+}
+
+/**
+ * Display relational links for the posts adjacent to the current post for single post pages.
+ *
+ * This is meant to be attached to actions like 'wp_head'. Do not call this directly in plugins or theme templates.
+ * @since 3.0.0
+ *
+ */
+function adjacent_posts_rel_link_wp_head() {
+ if ( !is_singular() || is_attachment() )
+ return;
+ adjacent_posts_rel_link();
+}
+
+/**
+ * Display relational link for the next post adjacent to the current post.
+ *
+ * @since 2.8.0
+ *
+ * @param string $title Optional. Link title format.
+ * @param bool $in_same_cat Optional. Whether link should be in a same category.
+ * @param array|string $excluded_categories Optional. Array or comma-separated list of excluded category IDs.
+ */
+function next_post_rel_link($title = '%title', $in_same_cat = false, $excluded_categories = '') {
+ echo get_adjacent_post_rel_link($title, $in_same_cat, $excluded_categories = '', false);
+}
+
+/**
+ * Display relational link for the previous post adjacent to the current post.
+ *
+ * @since 2.8.0
+ *
+ * @param string $title Optional. Link title format.
+ * @param bool $in_same_cat Optional. Whether link should be in a same category.
+ * @param array|string $excluded_categories Optional. Array or comma-separated list of excluded category IDs.
+ */
+function prev_post_rel_link($title = '%title', $in_same_cat = false, $excluded_categories = '') {
+ echo get_adjacent_post_rel_link($title, $in_same_cat, $excluded_categories = '', true);
+}
+
+/**
+ * Retrieve boundary post.
+ *
+ * Boundary being either the first or last post by publish date within the constraints specified
+ * by $in_same_cat or $excluded_categories.
+ *
+ * @since 2.8.0
+ *
+ * @param bool $in_same_cat Optional. Whether returned post should be in a same category.
+ * @param array|string $excluded_categories Optional. Array or comma-separated list of excluded category IDs.
+ * @param bool $start Optional. Whether to retrieve first or last post.
+ * @return object
+ */
+function get_boundary_post( $in_same_cat = false, $excluded_categories = '', $start = true ) {
+ $post = get_post();
+ if ( ! $post || ! is_single() || is_attachment() )
+ return null;
+
+ $cat_array = array();
+ if( ! is_array( $excluded_categories ) )
+ $excluded_categories = explode( ',', $excluded_categories );
+
+ if ( $in_same_cat || ! empty( $excluded_categories ) ) {
+ if ( $in_same_cat )
+ $cat_array = wp_get_object_terms( $post->ID, 'category', array( 'fields' => 'ids' ) );
+
+ if ( ! empty( $excluded_categories ) ) {
+ $excluded_categories = array_map( 'intval', $excluded_categories );
+ $excluded_categories = array_diff( $excluded_categories, $cat_array );
+
+ $inverse_cats = array();
+ foreach ( $excluded_categories as $excluded_category )
+ $inverse_cats[] = $excluded_category * -1;
+ $excluded_categories = $inverse_cats;
+ }
+ }
+
+ $categories = implode( ',', array_merge( $cat_array, $excluded_categories ) );
+
+ $order = $start ? 'ASC' : 'DESC';
+
+ return get_posts( array('numberposts' => 1, 'category' => $categories, 'order' => $order, 'update_post_term_cache' => false, 'update_post_meta_cache' => false) );
+}
+
+/*
+ * Get previous post link that is adjacent to the current post.
+ *
+ * @since 3.7.0
+ *
+ * @param string $format Optional. Link anchor format.
+ * @param string $link Optional. Link permalink format.
+ * @param bool $in_same_cat Optional. Whether link should be in same category.
+ * @param string $excluded_categories Optional. Excluded categories IDs.
+ * @return string
+ */
+function get_previous_post_link( $format = '« %link', $link = '%title', $in_same_cat = false, $excluded_categories = '' ) {
+ return get_adjacent_post_link( $format, $link, $in_same_cat, $excluded_categories, true );
+}
+
+/**
+ * Display previous post link that is adjacent to the current post.
+ *
+ * @since 1.5.0
+ * @uses get_previous_post_link()
+ *
+ * @param string $format Optional. Link anchor format.
+ * @param string $link Optional. Link permalink format.
+ * @param bool $in_same_cat Optional. Whether link should be in a same category.
+ * @param array|string $excluded_categories Optional. Array or comma-separated list of excluded category IDs.
+ */
+function previous_post_link( $format = '« %link', $link = '%title', $in_same_cat = false, $excluded_categories = '' ) {
+ echo get_previous_post_link( $format, $link, $in_same_cat, $excluded_categories );