How to remove private posts from next / previous posts in WordPress? - wordpress

I have implemented Vue.js single page application in a WordPress theme (wue-theme.app) and want to navigate through the blog posts by next / previous links from within the single-post template. But I struggle to remove private posts from the links - there are no WP_Query options available. So how can I navigate through the posts with the status "publish" only?

Unfortunately I couldn't find any documented "official" ways to do this. So I "hacked" a bit the default SQL query which you can fortunately apply a filter on, and have removed the "private" post_status from the query. This is the full custom code for the "post" post type you have to place in your functions.php:
add_filter( 'rest_prepare_post', 'technomad_post_api_hook', 10, 3 );
function technomad_post_api_hook( $response ) {
if ( isset( $_REQUEST["slug"] ) ) {
add_filter( "get_next_post_where", function($query, $in_same_cat, $excluded_categories, $taxonomy, $post){
$query = str_replace("OR p.post_status = 'private'", "", $query);
return $query;
}, 100, 5 );
add_filter( "get_previous_post_where", function($query, $in_same_cat, $excluded_categories, $taxonomy, $post){
$query = str_replace("OR p.post_status = 'private'", "", $query);
return $query;
}, 100, 5 );
$next = get_adjacent_post( false, '', false );
$previous = get_adjacent_post( false, '', true );
if ( is_a( $next, 'WP_Post' ) ) {
$response->data['next'] = array(
"id" => $next->ID,
"slug" => $next->post_name,
"title" => $next->post_title,
);
} else {
$response->data['next'] = null;
}
if ( is_a( $previous, 'WP_Post' ) ) {
$response->data['previous'] = array(
"id" => $previous->ID,
"slug" => $previous->post_name,
"title" => $previous->post_title,
);
} else {
$response->data['previous'] = null;
}
}
}
You can also just return your own custom query instead of str_replace():
return $wpdb->prepare( "WHERE p.post_title > %s AND p.post_type = %s AND ( p.post_status = 'publish' )", $post->post_title, $post->post_type );

Related

Using meta_query with custom query vars leads to 404 on pagination with Timber

I have a CPT that I'm filtering with custom query vars associated with ACF fields. Pagination pages lead to 404 whenever the meta_query is active. Any assistance would be appreciated.
On search.php
if ( $post_type == 'funded_grants' ) {
$program_type = get_query_var('program_type');
$contributor = get_query_var('contributor');
$researchers = get_query_var('researchers');
$area_of_research = get_query_var('area_of_research');
$disease_areas = get_query_var('disease_areas');
$province = get_query_var('province');
$start_date = get_query_var('start_date');
$end_date = get_query_var('end_date');
$meta_query = bc_create_meta_query();
$args = [
's' => $text_query,
'meta_query' => $meta_query,
];
$context['posts'] = new Timber\PostQuery($args);
Timber::render(['archive-funded_grants.twig'], $context);
}
In functions.php
function filterFundedGrantsSearch($query) {
if ($query->is_search && $query->get('post_type') == 'funded_grants' && !is_admin() ) {
$query->set( 'posts_per_page', 5 );
$query->set( 'paged', (get_query_var('paged')) ? get_query_var('paged') : 1 );
}
return $query;
}
add_filter('pre_get_posts','filterFundedGrantsSearch');
I've tried every possible solution I can find in the docs.

Woocommerce searchable product custom field

my products have two important codes which are the EAN-13 and its model number. I wrote the EAN-13 in the SKU and the products model number in a custom field. The SKU is perfectly searchable but the data is not.
Is there any way to make a product custom field searchable from the back-end office?
Please help! Thanks in advance!
When I was searching for almost similar problem I've found solution here https://dominykasgel.com/extend-search-custom-post-types-wordpress-admin/, here's my code for functions.php in child theme:
function extend_admin_search( $query ) {
$custom_fields = array(
"_file_name",
);
if( ! is_admin() )
return;
$search_term = $query->query_vars['s'];
$query->query_vars['s'] = '';
$query->set('_meta_or_title', $search_term);
if ( $search_term != '' ) {
$meta_query = array( 'relation' => 'OR' );
foreach( $custom_fields as $custom_field ) {
array_push( $meta_query, array(
'key' => $custom_field,
'value' => $search_term,
'compare' => 'LIKE'
));
}
$query->set( 'meta_query', $meta_query );
};
}
add_action( 'pre_get_posts', 'extend_admin_search', 6, 2);
add_action( 'pre_get_posts', function( $q )
{
if( $title = $q->get( '_meta_or_title' ) )
{
add_filter( 'get_meta_sql', function( $sql ) use ( $title )
{
global $wpdb;
// Only run once:
static $nr = 0;
if( 0 != $nr++ ) return $sql;
// Modified WHERE
$sql['where'] = sprintf(
" AND ( %s OR %s ) ",
$wpdb->prepare( "{$wpdb->posts}.post_title like '%%%s%%'", $title),
mb_substr( $sql['where'], 5, mb_strlen( $sql['where'] ) )
);
return $sql;
}, 12, 1);
}
}, 12, 1);
DISCLAIMER: it was used a few years ago, so I can't be sure if it works fine for now.

Add custom field as item meta data on the Order

I have added a custom field to the woocommerce products, it is working well with the following code. But my problem is how to add it to the cart_item_data
// Display Text in Admin Panel
add_action('woocommerce_product_options_general_product_data', 'product_custom_text_field');
function product_custom_text_field()
{
// Custom Product Text Field ( para Tex Area -> woocommerce_wp_textarea_input )
woocommerce_wp_text_input(
array(
'id' => '_optional_text_field',
'label' => __('Customize title', 'woocommerce'),
'placeholder' => '',
'desc_tip' => 'true',
'description' => __('Customizable title for the field that the user must fill out.', 'woocommerce')
)
);
}
Save Fields
add_action('woocommerce_process_product_meta', 'product_custom_text_field_save');
function product_custom_text_field_save($post_id)
{
if (!empty($_POST['_optional_text_field'])) {
update_post_meta($post_id, '_optional_text_field', esc_attr($_POST['_optional_text_field']));
}
}
Display The Text in Product Page
add_action('woocommerce_single_variation', 'display_text_field');
function display_text_field()
{
global $post;
if (get_post_meta($post->ID, '_optional_text_field', true)) {
echo "<div class='titulo-c'><label>";
echo get_post_meta($post->ID, '_optional_text_field', true);
echo "</label></div>";
return;
}
echo __('FREE LOCAL SHIPPING', 'woocommerce');
}
So far it works without problems, now I want to add it to the cart_item_data, with the following code, but it does not work for me.
add_filter( 'woocommerce_add_cart_item_data', 'add_cart_item_data', 25, 2 );
function add_cart_item_data( $cart_item_meta, $product_id ) {
$custom_data = array() ;
$custom_data[ "_optional_text_field"] = isset( $_POST['_optional_text_field'] ) ? sanitize_text_field ( $_POST['_optional_text_field'] ) : "" ;
$cart_item_meta ['custom_data'] = $custom_data ;
return $cart_item_meta;
}
Display custom data on cart and checkout page.
add_filter( 'woocommerce_get_item_data', 'get_item_data' , 25, 2 );
function get_item_data ( $other_data, $cart_item ) {
if ( isset( $cart_item [ 'custom_data' ] ) ) {
$custom_data = $cart_item [ 'custom_data' ];
$other_data[] = array( 'name' => 'Title',
'display' => $custom_data['_optional_text_field'] );
}
return $other_data;
}
As a result, it only appears to me: Title: ________
I will appreciate any suggestions to make my code work.
Make the following changes to your code as below, this should work.
/**
* Add data to cart item
*/
add_filter( 'woocommerce_add_cart_item_data', 'add_cart_item_data', 25, 2 );
function add_cart_item_data( $cart_item_meta, $product_id ) {
global $woocommerce;
$mytitle_form_data = get_post_meta($product_id, '_optional_text_field', true);
$custom_data = array() ;
$custom_data[ "_optional_text_field"] = $mytitle_form_data;
$cart_item_meta ['custom_data'] = $custom_data ;
return $cart_item_meta;
}

Custom permalink with dynamic rewrite tags on a Wordpress custom post type

I have a custom post type named location and I'm trying to set posts of this type as children of existing pages, to achieve a permalink structure like this:
example.com/alabama <-- Page with generic content
example.com/alabama/locations <-- Another page, child of a state page
example.com/alabama/locations/location-name <- Custom type, child of alabama/locations seen above and titled `Location Name`
The closest I've got to create hierarchical relationships between two distinct post types was through a meta box where I can assign a post ID as the post_parent of my custom type posts when saving them. However despite the page ID being indeed saved to the CPT post_parent field, it has no effect on the permalinks. They are coming as defined in the rewrite CPT option. But I don't know how to make the ['rewrite']['slug'] option dynamic, or if it's even possible.
This is how my post type is being defined:
add_action( 'init', function() {
register_post_type( 'location', [
'label' => 'Location',
'menu_icon' => 'dashicons-location-alt',
'supports' => [ 'title', 'editor', 'custom-fields' ],
'public' => true,
'hierarchical' => false,
'has_archive' => false,
'rewrite' => false,
] );
} );
How can I configure the rewrite rules for locations to get the permalinks I need?
I'm assuming that all the location posts will have a permalink structure that looks like this:
example.com/{STATE NAME}/locations/{CPT SLUG}
Sample URL addresses:
http://example.com/alabama/locations/location-1
http://example.com/alabama/locations/location-2
http://example.com/new-york/locations/location-3
So if that is correct, then:
• Use the add_rewrite_rule() function to add a custom rewrite rule for those permalinks.
• You don't need the /locations/ Page.
add_action( 'init', function(){
// Handles requests to `your-site-domain.com/{STATE NAME}/locations/{CPT SLUG}`
add_rewrite_rule(
'([^/]+)/locations/([^/]+)(?:/([0-9]+))?/?$',
'index.php?location=$matches[2]&page=$matches[3]&state_name=$matches[1]',
'top'
);
// Allows you to retrieve the `state_name`; for example using `get_query_var()`.
add_rewrite_tag( '%state_name%', '([\w\-]+)' );
} );
(You can change state_name to another name; it's up to you. And don't forget to flush the rewrite rules — go to the Permalink Settings page and click on the Save Changes button without having to make any changes.)
Next, when you create or edit a location post, set the value of the post_parent custom field to the ID of the 'state Page' — e.g. the /alabama/ Page.
And this code will filter the get_permalink() output, and returns the appropriate permalink for a location post:
add_filter( 'post_type_link', 'so51217355_post_type_link', 10, 2 );
function so51217355_post_type_link( $permalink, $post ) {
if ( 'location' === $post->post_type ) {
$page_id = get_post_meta( $post->ID, 'post_parent', true );
$state_name = ( is_numeric( $page_id ) && $page_id ) ?
get_post_field( 'post_name', $page_id ) : null;
// Make sure the post is associated to a valid 'state Page'.
if ( $state_name ) {
$permalink = $state_name . '/locations/' . $post->post_name;
$permalink = home_url( user_trailingslashit( $permalink ) );
}
}
return $permalink;
}
So for example, get_permalink( 123 ) would return http://example.com/alabama/locations/location-1, if the location post's slug is location-1, and its 'state Page' is /alabama/.
UPDATE
When the permalink is requested (i.e. users visit example.com/{STATE NAME}/locations/{CPT SLUG}), and you want to make sure the 'state Page' and location post both exist, and that the 'state Page' was indeed associated to the location post, then this code can help you:
// Validates the `state_name` of the current page/URL.
add_action( 'parse_request', 'so51217355_parse_request' );
function so51217355_parse_request( $wp ) {
if ( ! empty( $wp->query_vars['state_name'] ) &&
! empty( $wp->query_vars['location'] ) ) {
global $wpdb;
$page_id = $wpdb->get_var( $wpdb->prepare(
"SELECT ID FROM {$wpdb->posts} WHERE post_name = %s",
$wp->query_vars['state_name']
) );
if ( ! is_numeric( $page_id ) || ! $page_id ) {
$wp->query_vars['error'] = '404';
// Don't let WordPress finds a post with nearest match.
remove_action( 'template_redirect', 'redirect_canonical' );
return;
}
$post_id = $wpdb->get_var( $wpdb->prepare(
"SELECT ID FROM {$wpdb->posts} WHERE post_name = %s",
$wp->query_vars['location']
) );
$page_id2 = get_post_meta( $post_id, 'post_parent', true );
if ( (int) $page_id2 !== (int) $page_id ) {
$wp->query_vars['error'] = '404';
// Don't let WordPress finds a post with nearest match.
remove_action( 'template_redirect', 'redirect_canonical' );
}
}
}
UPDATE #2
Refer to the // Comment in the code below this image — see the _so51217355_admin_ajax_js() function.
add_action( 'wp_ajax_so51217355_admin_ajax', '_so51217355_admin_ajax_php' );
function _so51217355_admin_ajax_php() {
$post_id = filter_input( INPUT_POST, 'post_id' );
echo get_sample_permalink_html( $post_id );
wp_die();
}
add_action( 'admin_print_footer_scripts', '_so51217355_admin_ajax_js', 11 );
function _so51217355_admin_ajax_js() {
$screen = get_current_screen();
if ( 'location' === $screen->id ) :
?>
<script>
// This script will sync the Permalink under the big/main post title box on
// the Edit Post page; but only if and when editing or deleting the custom
// field as in `meta_key` below. Make sure to change it, if necessary.
jQuery( function( $ ){
var meta_key = 'post_parent';
function ajax() {
$.post( ajaxurl, {
action: 'so51217355_admin_ajax',
post_id: $( '#post_ID' ).val()
}, function( s ){
$( '#edit-slug-box' ).html( s );
} );
}
function _go( e, a ) {
var $input = $( a.target ),
mid, mkey;
if ( /^meta\-(\d+)\-submit$/.test( $input.attr( 'name' ) ) ||
/^deletemeta\[(\d+)\]$/.test( $input.attr( 'name' ) ) ) {
mid = RegExp.$1;
mkey = $( 'input[name="meta[' + mid + '][key]"]' ).val();
if ( meta_key === mkey ) {
ajax();
}
}
}
$( '#the-list' )
.on( 'wpListAddEnd', _go )
.on( 'wpListDelEnd', _go );
} );
</script>
<?php
endif;
}

Show Child-pages of Specific Parent on any page in Wordpress

My website is build with alot of parents and childs.
I am currently using the follow function: https://gist.github.com/mprahweb/3c94d9c65b32ec9e3b2908305efd77d5
However it doesnt achieve what i want. I can only use it under the parent.
I want to be able to use a shortcode to list a child-pages for a specfic parent on any page (mainly the homepage)
The dream would be that i could have a shortcode like this:
[wpb_childpages_xxx] where xxx equals the parent page
is this possible?
Here is the modified code:
function wpb_list_child_pages( $atts = array() ) {
$atts = shortcode_atts( array(
'id' => 0,
'slug' => '',
), $atts );
if ( $atts['slug'] ) {
global $wpdb;
$q = $wpdb->prepare( "SELECT ID FROM {$wpdb->posts} WHERE post_name = %s", $atts['slug'] );
$post_id = $wpdb->get_var( $q );
if ( ! $post_id ) {
return '';
}
} else {
$post_id = absint( $atts['id'] );
}
$post = get_post( $post_id ); // WP_Post on success.
$childpages = '';
if ( $post && is_post_type_hierarchical( $post->post_type ) ) {
$childpages = wp_list_pages( array(
'child_of' => $post->ID,
'title_li' => '',
'echo' => 0,
) );
}
if ( $childpages ) {
$childpages = '<ul>' . $childpages . '</ul>';
}
return $childpages;
}
add_shortcode( 'wpb_childpages', 'wpb_list_child_pages' );
Using the Shortcode — use either id or slug, but if you specify a slug, the id will be ignored:
To show child pages of the current page:
[wpb_childpages /]
To show child pages of any pages:
With the post ID: [wpb_childpages id="{POST_ID} /]
E.g. [wpb_childpages id="123" /]
With the post slug: [wpb_childpages slug="{SLUG} /]
E.g. [wpb_childpages slug="home" /]

Resources