How can I exclude certain posts from Wordpress RSS feeds? - wordpress

Is there a way to exclude a post from all of Wordpress' RSS feeds (the default one as well as tags, categories, and search feeds)?

Assuming you do not want to use plugins like THIS ONE or THIS, you have many ways :
One is to assign a category for the posts you would like to exclude and then you can just change the URL of the RSS link like so :
http://www.mydomain.com/feed?cat=-x,-y,-z
You can also FILTER with a function :
function o99_my_rss_filter($query) {
if ($query->is_feed) {
$query->set('cat','-7'); //Put category ID - here it is : 7
}
return $query;
}
add_filter('pre_get_posts','o99_my_rss_filter');
The point is that you will need to assign SOMETHING (tag, custom field, category) to identify what to exclude , assuming that you do not want always to add the ID to exclude from the query.
The easiest is by category as demonstrated above .
** Edit I **
Just for the sake of it - (I still recommend the cetegory method) r
Even if I dislike custom-fields when they are overused - this is another way that change the query by assigning a custom field :
function o99_my_rss_filter_by _field( $where, $wp_query = NULL ) {
global $wpdb;
if ( !$wp_query ) global $wp_query;if ( $wp_query->is_feed ) {
$posts = get_posts( array( 'meta_key' => 'norss' ) );
if ( $posts ) {
foreach( $posts as $post ) {
$exclude .= $post->ID . ',';
}
}
$exclude = substr( $exclude,0, strlen( $exclude )-1 );
$where .= ' AND $wpdb->posts.ID NOT IN ( ' . $exclude . ')';
}
return $where;
}
add_filter( 'posts_where', 'o99_my_rss_filter_by _field', 1, 4 );

Related

WP Custom url structure for categories

Update:
To clarify my answer.
Problem is that I have YoastSEO plugin and that plugin is responsible for generating sitemap. What I want is to redirect second and third level subcategories (listed in sitemap) to corresponding top level category with url structure as specified.
What I want to is to generate custom url for wp post categories.
Example:
Categories structure is as following:
Cat1
|_Cat2
|_ Cat3
Wordpress generates following url structure for categories:
//host/category/cat1/cat2/cat3
but what I want to achieve is following:\n
//host/category/cat1/?l2cat=cat2&l3cat=cat3
any help is welcome and appriciated
So the URL you're trying to achieve is actually going to the "cat1" page as far as WP is concerned. Adding Cat2 and Cat3 as query parameters means that you will have to handle these yourself when the page loads, you're not on the page that represents "cat3" anymore. In your first URL, "cat1" and "cat2" are just part of the URL - you're actually ON "cat3". You can certainly do that if that's what you're intending...
What you would want to do is add_query_var for cat2 and cat3. Then in the template that shows cat1 - you can change the content displayed by getting those terms.
I would caution against it... I'm not an SEO expert, but what you now have is a page with a single canonical URL that actually represents 3 completely different terms based on the query parameters.
Maybe I misunderstood the question? But the URL structure you outline above looks like what you're trying to achieve is some kind of archive page that filters by several categories, and not the actual URL to the cat3 term display, which your first URL represents.
function term_link_filter( $url, $term, $taxonomy ) {
$chunks = array_filter(explode('/', $url));
$cat = home_url() . '/' . $taxonomy . '/' . $chunks[4];
$cat = $chunks[5] ? add_query_arg( 'l2cat', $chunks[5], $cat ) : $cat;
$cat = $chunks[6] ? add_query_arg( 'l3cat', $chunks[6], $cat ) : $cat;
return $cat;
}
add_filter('term_link', 'term_link_filter', 10, 3);
Note:
Problem is that I have YoastSEO plugin and that plugin is responsible for generating sitemap. What I want is to redirect second and third level subcategories to corresponding top level category with url structure as specified.
solution. Not elegant.
/* Generate sub categories redirect logic */
function subcategories_redirect() {
/* Example
'/category/first_level_cateogry/second_level_category/third_level_category'
=> '/category/first_level_category?l2cat=second_level_category&l3cat=third_level_category',
*/
$get_top_categories = get_top_categories();
$category_array = array();
foreach( $get_top_categories as $category ) {
$args = array(
'hide_empty' => 0,
'child_of' => $category->term_id,
'taxonomy' => 'category'
);
$term_children = get_categories($args);
foreach ( $term_children as $child ) {
// Cheap and dirty
$category_ancestors = get_ancestors($child->term_id, 'category');
$level_2_category = $level_3_category = "";
if (count($category_ancestors) == 1 ) {
$level_2_category = '?l2cat=' . $child->slug;
} elseif (count($category_ancestors) == 2) {
$tmp = get_category($category_ancestors[0]);
$level_2_category = '?l2cat=' . $tmp->slug;
$level_3_category = '&l3cat=' . $child->slug;
}
$category_array[] = array( ($child->slug) => ('/category/' . $category->slug . '/' . $level_2_category . $level_3_category) );
}
}
$flat_category_array = array_merge(...$category_array);
foreach( $flat_category_array as $category => $url ){
if( is_category( $category ) ) {
$url = site_url( $url );
wp_safe_redirect( $url, 301 );
exit();
}
}
}
add_action('template_redirect', 'subcategories_redirect');

Woocommerce - Display single product attribute(s) with shortcodes in Frontend

i've read many Q/A's here during the last few days, but unfortunately none of them solved my issue.
I'm trying to fetch product attributes and display them on the frontend with a shortcode. I have managed to display ALL available attributes and display them in a list, but i need to select only one or two of them in different locations (thats why using shortcodes). For example like [shortcode_attribute name="brand"].
Any help is highly appreciated!
my code so far:
function tutsplus_list_attributes( $product ) {
global $product;
global $post;
$attributes = $product->get_attributes();
if ( ! $attributes ) {
return;
}
foreach ( $attributes as $attribute ) {
// Get the taxonomy.
$terms = wp_get_post_terms( $product->id, $attribute[ 'name' ], 'all' );
$taxonomy = $terms[ 0 ]->taxonomy;
// Get the taxonomy object.
$taxonomy_object = get_taxonomy( $taxonomy );
// Get the attribute label.
$attribute_label = $taxonomy_object->labels->name;
// Display the label followed by a clickable list of terms.
echo get_the_term_list( $post->ID, $attribute[ 'name' ] , '<div><li class="bullet-arrow">' . $attribute_label . ': ' , ', ', '</li></div>' );
}
}
add_action( 'woocommerce_product_meta_end', 'tutsplus_list_attributes' );
add_shortcode('display_attributes', 'tutsplus_list_attributes');
While I am still not 100% clear what you're after, what I'm going to create is a shortcode that creates a list of all terms in all attributes. To make it more flexible I'll add support for a shortcode parameter so you can create a list of specific attribute terms.
One major thing you need to alter from your code, is that shortcodes need to RETURN a string and not ECHO out html. Echoing out shortcodes can result in unexpected weirdness.
/**
* Attributes shortcode callback.
*/
function so_39394127_attributes_shortcode( $atts ) {
global $product;
if( ! is_object( $product ) || ! $product->has_attributes() ){
return;
}
// parse the shortcode attributes
$args = shortcode_atts( array(
'attributes' => array_keys( $product->get_attributes() ), // by default show all attributes
), $atts );
// is pass an attributes param, turn into array
if( is_string( $args['attributes'] ) ){
$args['attributes'] = array_map( 'trim', explode( '|' , $args['attributes'] ) );
}
// start with a null string because shortcodes need to return not echo a value
$html = '';
if( ! empty( $args['attributes'] ) ){
foreach ( $args['attributes'] as $attribute ) {
// get the WC-standard attribute taxonomy name
$taxonomy = strpos( $attribute, 'pa_' ) === false ? wc_attribute_taxonomy_name( $attribute ) : $attribute;
if( taxonomy_is_product_attribute( $taxonomy ) ){
// Get the attribute label.
$attribute_label = wc_attribute_label( $taxonomy );
// Build the html string with the label followed by a clickable list of terms.
// Updated for WC3.0 to use getters instead of directly accessing property.
$html .= get_the_term_list( $product->get_id(), $taxonomy, '<li class="bullet-arrow">' . $attribute_label . ': ' , ', ', '</li>' );
}
}
// if we have anything to display, wrap it in a <ul> for proper markup
// OR: delete these lines if you only wish to return the <li> elements
if( $html ){
$html = '<ul class="product-attributes">' . $html . '</ul>';
}
}
return $html;
}
add_shortcode( 'display_attributes', 'so_39394127_attributes_shortcode' );
Usage: This would be used in 1 of 2 ways.
First, to display specific attributes use:
[display_attributes attributes="color|material"]
Second, to display all attributes use:
[display_attributes]
Edit
Here's an always single display edition:
/**
* Attribute shortcode callback.
*/
function so_39394127_singular_attribute_shortcode( $atts ) {
global $product;
if( ! is_object( $product ) || ! $product->has_attributes() ){
return;
}
// parse the shortcode attributes
$args = shortcode_atts( array(
'attribute' => ''
), $atts );
// start with a null string because shortcodes need to return not echo a value
$html = '';
if( $args['attribute'] ){
// get the WC-standard attribute taxonomy name
$taxonomy = strpos( $args['attribute'], 'pa_' ) === false ? wc_attribute_taxonomy_name( $args['attribute'] ) : $args['attribute'];
if( taxonomy_is_product_attribute( $taxonomy ) ){
// Get the attribute label.
$attribute_label = wc_attribute_label( $taxonomy );
// Build the html string with the label followed by a clickable list of terms.
// Updated for WC3.0 to use getters instead of directly accessing property.
$html .= get_the_term_list( $product->get_id(), $taxonomy, $attribute_label . ': ' , ', ', '' );
}
}
return $html;
}
add_shortcode( 'display_attribute', 'so_39394127_singular_attribute_shortcode' );
This removes all HTML markup, and so you'd need to provide your own... which would happen if you are making a custom list.
<ul>
<li class="arrow">Static List Item</li>
<li class="arrow">[display_attribute attribute="color"]</li>
<ul>
EDIT: This code can be added to your theme's functions.php, but preferably either in a site-specific plugin or via the Code Snippets plugin.
If anyone else just wants to simply output the attribute value as text, replace the $html .= line with this:
$html .= strip_tags(get_the_term_list($product->get_id(), $taxonomy));
#helgatheviking There must be a more elegant way than this! :)
Needed this recently - found https://wordpress.org/plugins/wcpas-product-attributes-shortcode/ does the trick.
With this plugin you can set an "attribute" parameter to match one of the product attributes you have setup.
For example if you have a brand attribute you can use:
[wcpas_product_attributes attribute="brand"]
This will output a <ul> list containing all brands.
There are also a number of parameters you can use alongside the attribute including:
orderby (order the list by an orderby value)
order (order asc, desc, rand, etc)
hide_empty (hide empty terms)
show_counts (show the total number of products in the brand)
archive_links (use an archive based URL instead of a filter based URL)
min_price (When using archive_links = 0 you can have a price filter based URL off a minimum price)
max_price (When using archive_links = 0 you can have a price filter based URL off a maximum price)

Woocommerce sortable columns not working

I've added a couple of custom field columns to our Woocommerce orders list in the admin of WordPress using the methods below, but the sort is not working....
add_filter( 'manage_edit-shop_order_columns', 'my_wc_columns' );
function my_wc_columns($columns){
$new_columns = (is_array($columns)) ? $columns : array();
unset( $new_columns['order_actions'] );
$new_columns['program_id'] = 'Program';
$new_columns['constituent_id'] = 'Constituent ID';
$new_columns['order_actions'] = $columns['order_actions'];
return $new_columns;
}
add_action( 'manage_shop_order_posts_custom_column', 'my_wc_column_values', 2 );
function my_wc_column_values($column){
global $post;
if ( $column == 'program_id' ) {
$program = get_post_meta( $post->ID, '_program_id', true );
$program_title = get_the_title($program);
$column_val = (isset($program) && $program>0 ? $program_title : 'All');
echo '<span>' . my_programs_get_name( $column_val ) . ' (' . $program . ')</span>';
}
if ( $column == 'constituent_id' ) {
$consid = get_post_meta( $post->ID, 'constituent_id', true );
$column_val = (isset($consid) && $consid != "") ? $consid : "";
echo '<span>' . $column_val . '</span>';
}
}
// Make column sortable
add_filter( "manage_edit-shop_order_sortable_columns", 'my_wc_column_sort' );
function my_wc_column_sort( $columns ) {
$custom = array(
'program_id' => '_program_id',
'constituent_id' => 'constituent_id',
);
return wp_parse_args( $custom, $columns );
}
I expected to have an issue perhaps with the program name, since it is an id that needs to be translated via a custom function to a name, but neither column is sorting properly. The records change order after clicking their column titles, but I cannot tell how the sort is being done. The program is not sorting on name or ID and both are seem random but consistent. Keep in mind both fields are custom fields that may or may not have a value defined. How can I make this sortable?
Here's a good tutorial on custom sortable columns. After you register the column, you need to handle the actual sorting. Sadly, that part doesn't happen automagically. Untested, but adapted from the above tutorial:
add_action( 'pre_get_posts', 'manage_wp_posts_be_qe_pre_get_posts', 1 );
function manage_wp_posts_be_qe_pre_get_posts( $query ) {
/**
* We only want our code to run in the main WP query
* AND if an orderby query variable is designated.
*/
if ( $query->is_main_query() && ( $orderby = $query->get( 'orderby' ) ) ) {
switch( $orderby ) {
// If we're ordering by 'program_id'
case 'program_id':
// set our query's meta_key, which is used for custom fields
$query->set( 'meta_key', '_program_id' );
/**
* Tell the query to order by our custom field/meta_key's
* value
*
* If your meta value are numbers, change 'meta_value'
* to 'meta_value_num'.
*/
$query->set( 'orderby', 'meta_value' );
break;
}
}
}

Wordpress next_posts_link Linking to wrong URL

I have next_posts_link setup on the single.php, and it generates the following URL:
http://mywebsite.com/news/article1/page/2
However, this url would be redirected to
http://mywebsite.com/news/article1
Any way to get to the second page?
It seems that it's an issue with Wordpress permalinks. I currently use a custom permalink structure
/%category%/%postname%/
Setting the permalinks as default fixes this issue, but this project needs to have the custom permalinks.
You should be using next_post_link() which is meant to navigate between single posts. next_posts_link naviugate between pages
If you however need to navigate a paged post (using <--nextpage-->), then you should make use of wp_link_pages
EDIT
I have recently did the same exact thing on WPSE. As I explain in that post, the structure you need is not available for any permalink structure outside the default permalink structure.
Just a note before I paste that answer, I have done that for the /%postname%/ permalink structure. Just change all instances of /%postname%/ to the appropriate structure
POST FROM WPSE
As I said, this whole setup you are after is not possible natively with pretty permalinks. Your setup probably works with default permalink structure as both queries (the main query and your custom query) read these permalinks in the same way. When you switch to pretty permalinks, the two queries on the single page interpret the URL differently causing one or the other to fail when you try to paginate your custom query
Single pages was never meant to be paginated in this manner, specially using pretty permalinks. I have gone and played around with a couple of ideas, and the best way to accomplish this is
To write your own pagination functions that can read the page number from the URL
Write your own function that can append the page number to the URL, something like adding /2/ to the URL of single pages.
I must stress, if you are paginating single post with <!--nextpage-->, your post will also paginate together with your custom query. Also, if your permalink structure is not set to /%postname%/, the code will fail and display an error message through wp_die()
THE CODE
Here is the code that will get the next/previous page and also add the pagenumber to the URL.
function get_single_pagination_link( $pagenum = 1 ) {
global $wp_rewrite;
if( is_singular() && $wp_rewrite->permalink_structure == '/%postname%/') {
$pagenum = (int) $pagenum;
$post_id = get_queried_object_id();
$request = get_permalink( $post_id );
if ( $pagenum > 1 ) {
$request = trailingslashit( $request ) . user_trailingslashit( $pagenum );
}
return esc_url( $request );
}else{
wp_die( '<strong>The function get_single_pagination_link() requires that your permalinks are set to /%postname%/</strong>' );
}
}
You can use this function as follow to get the link for any page in the single page when paginating your custom query
get_single_pagination_link( 'pagenumber_of_previous_or_next_page' );
Again, as I said, there is no pagination function that will be able to paginate your custom query or read pagenumbers from the URL, so you have to write your own.
Here is my idea of creating links to the next and previous pages in your custom query. If you look closely, you will see how I have used the previous declared function get_single_pagination_link() to get the links to the next and previous pages
function get_next_single_page_link ( $label = null, $max_page = 0 ) {
global $wp_query;
if ( !$max_page ) {
$max_page = $wp_query->max_num_pages;
}
$paged = ( get_query_var('page') ) ? get_query_var('page') : 1;
if( is_singular() ) {
$next_page = intval($paged) + 1;
if ( null === $label ) {
$label = __( 'Next Page »' );
}
if ( ( $next_page <= $max_page ) ) {
return '' . $label . '';
}
}
}
function get_previous_single_page_link( $label = null ) {
$paged = ( get_query_var('page') ) ? get_query_var('page') : 1;
if( is_singular() ) {
$prev_page = intval($paged) - 1;
if ( null === $label ) {
$label = __( '« Previous Page' );
}
if ( ( $prev_page > 0 ) ) {
return '' . $label . '';
}
}
}
You can now use this two functions in your code to paginate your custom query. Both functions work exactly the same as get_next_posts_link() and get_previous_posts_link() and uses the same exact parameters, so you'll need to keep in mind that you need to pass the $max_page parameter for your custom query
Here is your custom query with these functions.
function get_related_author_posts() {
global $authordata, $post;
$paged = ( get_query_var('page') ) ? get_query_var('page') : 1;
$args = array(
'posts_per_page' => 2,
'paged' => $paged
);
$authors_posts = new WP_Query( $args );
$output = '';
if( $authors_posts->have_posts() ) {
$output = '<ul>';
while( $authors_posts->have_posts() ) {
$authors_posts->the_post();
$output .= '<li>' . get_the_title() . '' . get_the_excerpt() . '</li>';
}
$output .= '</ul>';
$output .= get_previous_single_page_link();
$output .= get_next_single_page_link( null , $authors_posts->max_num_pages );
wp_reset_postdata();
}
return $output;
}
If you want your long post to be shown 2 or 3 pages . You just have to add <!--nextpage--> in your post . The content after it will be shown in the next page . Reference

pre_get_posts for products on category page - woocommerce

I would like to filter the products on a category products so that it shows only the products of a certain author or multiple authors.
I already have the following code. This works as it filters the products. The correct products are displayed. Except the Woocommerce filters on in the left sidebar are not affected by the filter. The filters on the left side are showing all the original products in the category (also from other users) so the count isn't correct and also attributes from products that are filtered are showing. This shouldn't be the case. Do I have to add another pre_get_posts for the filters?
<?php
function pre_get_posts_by_author( $q ) {
if ( ! $q->is_main_query() ) return;
if ( ! $q->is_post_type_archive() ) return;
$cat_obj = $q->get_queried_object();
if($cat_obj->name == 'Nieuw')
{
$q->set( 'author_ids', '2086,2084');
}
}
add_action( 'pre_get_posts', 'pre_get_posts_by_author' );
add_filter( 'posts_where', 'author_posts_where', 10, 2 );
function author_posts_where( $where, &$wp_query )
{
global $wpdb;
if ( $wp_query->get( 'author_ids' ) ) {
$where .= ' AND ' . $wpdb->posts . '.post_author IN (' . $wp_query->get( 'author_ids' ) .')';
}
return $where;
}
?>
Thanks for helping out!
Probabaly a better way to do this is by setting 'author__in' argument in your query which will take an array of authors the query will return products of.
function pre_get_posts_by_author( $q ) {
if ( ! $q->is_main_query() || !$q->is_post_type_archive() ) return;
$cat_obj = $q->get_queried_object();
if( $cat_obj->name == 'Nieuw' ){
$q->set( 'author__in', array(2086,2084));
}
}
add_action( 'pre_get_posts', 'pre_get_posts_by_author' );
My updated solution: still dirty as it modifies Woocommerce's core files (2.2.4), but it works:
in the pre_get_posts-hook I retrieve the ids of the products I want to exclude with the following code:
$_SESSION['total_excluded'] = get_objects_in_term( $term_ids, $taxonomies, $args )`
(reference: http://codex.wordpress.org/Function_Reference/get_objects_in_term)
Then in woocommerce/includes/widgets/class-wc-widget-layered-nav.php I changed line 258 to:
$count = sizeof( array_diff(array_intersect( $_products_in_term, WC()->query->filtered_product_ids) , $_SESSION['total_excluded'] ) );
In woocommerce/includes/widgets/class-wc-widget-price-filter.php the new lines 121, 136 are:
%1$s.ID IN (' . implode( ',', array_map( 'absint', array_diff(WC()->query->layered_nav_product_ids, $_SESSION['total_excluded'] ) ) ) . ')
In woocommerce/includes/widgets/class-wc-widget-price-filter.php the new lines 123, 138 are:
%1$s.post_parent IN (' . implode( ',', array_map( 'absint', array_diff(WC()->query->layered_nav_product_ids, $_SESSION['total_excluded'] ) ) ) . ')

Resources