WP Custom url structure for categories - wordpress

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');

Related

how to change edit url with "Id" to edit url with "Slug" in Wordpress

I have this is WordPress Post editing Url:
https://example.com/wp-admin/post.php?post=ID&action=edit
and I want to change it to Slug Not the ID Like This:
https://example.com/wp-admin/post.php?post=Slug&action=edit
I was trying to edit the post with this Url but is not working:
https://example.com/wp-admin/post.php?post=MyPostSlug&action=edit
In order to change the edit post link structure, you can use the get_edit_post_link filter like so:
add_filter( 'get_edit_post_link', 'so_73914075_get_edit_post_link', 10, 3);
function so_73914075_get_edit_post_link($link, $post_id, $context) {
$post = get_post( $post_id );
if ( ! in_array( $post->post_type, array( 'post', 'page' ) ) ) {
return $link;
}
$post_type_object = get_post_type_object( $post->post_type );
if ( 'revision' === $post->post_type ) {
$action = '';
} elseif ( 'display' === $context ) {
$action = '&action=edit';
} else {
$action = '&action=edit';
}
if ( 'display' === $context ) {
$post_type = '&post-type=' . $post->post_type;
} else {
$post_type = '&post-type=' . $post->post_type;
}
$custom_edit_link = str_replace( '?post=%d', '?post-name=%s', $post_type_object->_edit_link );
return admin_url( sprintf( $custom_edit_link . $action . $post_type, $post->post_name ) );
}
This will change the edit links for regular posts and pages to something like this:
https://example.com/wp-admin/post.php?post-name=the-post-slug&action=edit&post-type=post
WARNING: make sure you limit this to only the post types you need to change the URL for. Making this change global will almost surely
have unintended effects over other plugins
However, making the admin panel actually load the edit screen is not that easy.
Looking at the source code of the wp-admin/post.php file, you will see that there is no way to hook into the flow and populate the $post_id variable with the post id matching the slug you are sending through.
That means you have 2 options:
RECOMMENDED Update the edit link to whatever format you want and then create an admin redirect function that pulls the slug from the initial URL and redirects to the actual edit page with the ?post=%d in it.
NOT RECOMMENDED Create a new admin edit page that will understand your URL structure and include the wp-admin/post.php after the code that pulls the $post_id based on the slug.
The 2nd method might come with lots of extra code you need to write to cover all the cases and all the instances a post reaches the post.php in the admin panel

Wordpress remove parent category in URL (Domain mapping)

The following code allow me to remove parent category for my primary domain post.
My problem is that I am using a third party plugin in order to map a second domain in my author name.
domain1.com PRIMARY DOMAIN
domain2.com MAPPED DOMAIN
Permalink structure :
domain1.com/me/cat/subcat/postname => domain2.com/cat/subcat/postname
So basically, domain1.com/me MAP TO domain2.com and it's Working good this way
BUT if I remove cat slug and let only subcat like this (with the script) :
domain1.com/me/subcat/postname WORKING
domain2.com/subcat/postname NOT WORKING (ERR_TOO_MANY_REDIRECTS)
Script to remove parent slug in URL
add_filter( 'post_link', 'remove_parent_category', 10, 3 );
function remove_parent_category( $permalink, $post, $leavename )
{
$cats = get_the_category( $post->ID );
if ( $cats ) {
usort( $cats, '_usort_terms_by_ID' );
$category = $cats[0]->slug;
if ( $parent = $cats[0]->parent ) {
// Find parent categories and replace them in the link
$parentcats = get_category_parents( $parent, false, '/', true );
$permalink = str_replace( $parentcats, '', $permalink );
}
}
return $permalink;
}
Take a look at plugin like WP remove category base You might hook up to query_vars, category_link, request, category_rewrite_rules to accomplish that.

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

Categories not appearing in correct hierarchy

When I create a post and try to assign the post to categories, the widget on the right-hand side shows the categories mixed up. For example, when I created the categories, I had the "accessories" category under the parent category "Men", but in the widget it doesn't appear under it; only when I go to category on the left-hand side menu does it appear in the correct order. What is wrong?
In categories I have this hierarchy
but when I assign a post to but when I assign a post to categories for example Men-Accesories-Ties, Men appears second in categories and not first thus messing up the breadcrumbs too
I'll leave the first version of this Answer at the bottom, as it may be useful.
There is no way of organizing that column in a hierarchical way. The solution is to make a custom column and use this Codex snippet:
// Change "post_" for the desired post type
add_filter( 'manage_edit-post_columns', 'custom_categories_register_so_15813936', 20, 1 );
add_action( 'manage_post_posts_custom_column', 'custom_categories_display_so_15813936', 20, 2 );
function custom_categories_register_so_15813936( $columns )
{
$columns[ 'custom-cat' ] = 'Categories';
return $columns;
}
function custom_categories_display_so_15813936( $column_name, $post_id )
{
if ( 'custom-cat' != $column_name )
return;
// get the category IDs assigned to post
$categories = wp_get_post_categories( $post_id, array( 'fields' => 'ids' ) );
// separator between links
$separator = ', ';
if ( $categories ) {
$cat_ids = implode( ',' , $categories );
$cats = wp_list_categories( 'title_li=&style=none&echo=0&include=' . $cat_ids );
$cats = rtrim( trim( str_replace( '<br />', $separator, $cats ) ), $separator );
$cats = str_replace( site_url('category/'), admin_url('edit.php?category_name='), $cats );
echo str_replace( '/" title', '" title', $cats );
}
}
At the left, the default column, and at the right, the custom one.
[first version]
I suppose you are talking about the back-end (/wp-admin). And the behavior you see is WordPress default. To get rid of that "feature", you need the plugin Category Checklist Tree:
On the post editing screen, after saving a post, you will notice that
the checked categories are displayed on top, breaking the category
hierarchy. This plugin removes that "feature".
Additionally, it automatically scrolls to the first checked category.
Works with custom taxonomies too.

How can I exclude certain posts from Wordpress RSS feeds?

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 );

Resources