1

I am using the 'standard' trick for category removal in wordpress. In my permalinks I set the permalinks structure to

/%category%/%postname%/

And my category base to

.

Which worked fine, until the client said he wants parent categories to appear in the header of the article, and the child categories to show in the footer of the articles on the home/index pages...

I've set it up, but the problem is that my child categories links are now:

http://www.example.com/./parent_category/child_category

And when I click the child category, I get sent to the latest article.

I've tried to add in my .htaccess

RewriteRule ^category/(.+)$ http://www.example.com/$1 [R=301,L]

But this did nothing.

Now all this works, when I remove the dot, but then my links look like:

http://www.example.com/category/parent_category/child_category

And client explicitly doesn't want category/ in the link...

Other than installing a plugin, what can I do? How to sort this? Can I have

http://www.example.com/parent_category/child_category

As my permalink, and if so how to achieve this?

1

The "." method was always an unlikely kludge.

The "Remove Category URL" plug-in works fine and, in my experience, as advertised. I'm not sure about the basis of the aversion to installing a plug-in for this purpose is, but, if you don't want to do so for some reason, you could always just examine it, and copy and pare down the technique employed by the plug-in developers, though the code, which relies on mastery of built-in rules, is not actually very complicated. The entirety of the main file is only 128 lines, and the core functionality resides in around 100 of them, covering four actions and four filters.

If you don't want to install it as a plugin, I suppose you could just strip out the plug-in convenience and installation functions/files, and add the core functions to your theme functions file. I suspect that whatever method anyone else drafts here is likely just to re-invent the same wheel.

/**
 * Plugin Name: Remove Category URL
 * Plugin URI: http://valeriosouza.com.br/portfolio/remove-category-url/
 * Description: This plugin removes '/category' from your category permalinks. (e.g. `/category/my-category/` to `/my-category/`)
 * Version: 1.1
 * Author: Valerio Souza, WordLab Academy
 * Author URI: http://valeriosouza.com.br/
 */

/* hooks */
register_activation_hook( __FILE__,   'remove_category_url_refresh_rules' );
register_deactivation_hook( __FILE__, 'remove_category_url_deactivate' );

/* actions */
add_action( 'created_category', 'remove_category_url_refresh_rules' );
add_action( 'delete_category',  'remove_category_url_refresh_rules' );
add_action( 'edited_category',  'remove_category_url_refresh_rules' );
add_action( 'init',             'remove_category_url_permastruct' );

/* filters */
add_filter( 'category_rewrite_rules', 'remove_category_url_rewrite_rules' );
add_filter( 'query_vars',             'remove_category_url_query_vars' );    // Adds 'category_redirect' query variable
add_filter( 'request',                'remove_category_url_request' );       // Redirects if 'category_redirect' is set
add_filter( 'plugin_row_meta',        'remove_category_url_plugin_row_meta', 10, 4 );

function remove_category_url_refresh_rules() {
    global $wp_rewrite;
    $wp_rewrite->flush_rules();
}

function remove_category_url_deactivate() {
    remove_filter( 'category_rewrite_rules', 'remove_category_url_rewrite_rules' ); // We don't want to insert our custom rules again
    remove_category_url_refresh_rules();
}

/**
 * Removes category base.
 *
 * @return void
 */
function remove_category_url_permastruct() {
    global $wp_rewrite, $wp_version;

    if ( 3.4 <= $wp_version ) {
        $wp_rewrite->extra_permastructs['category']['struct'] = '%category%';
    } else {
        $wp_rewrite->extra_permastructs['category'][0] = '%category%';
    }
}

/**
 * Adds our custom category rewrite rules.
 *
 * @param  array $category_rewrite Category rewrite rules.
 *
 * @return array
 */
function remove_category_url_rewrite_rules( $category_rewrite ) {
    global $wp_rewrite;

    $category_rewrite = array();

    /* WPML is present: temporary disable terms_clauses filter to get all categories for rewrite */
    if ( class_exists( 'Sitepress' ) ) {
        global $sitepress;

        remove_filter( 'terms_clauses', array( $sitepress, 'terms_clauses' ) );
        $categories = get_categories( array( 'hide_empty' => false, '_icl_show_all_langs' => true ) );
        add_filter( 'terms_clauses', array( $sitepress, 'terms_clauses' ) );
    } else {
        $categories = get_categories( array( 'hide_empty' => false ) );
    }

    foreach ( $categories as $category ) {
        $category_nicename = $category->slug;
        if (  $category->parent == $category->cat_ID ) {
            $category->parent = 0;
        } elseif ( 0 != $category->parent ) {
            $category_nicename = get_category_parents(  $category->parent, false, '/', true  ) . $category_nicename;
        }
        $category_rewrite[ '(' . $category_nicename . ')/(?:feed/)?(feed|rdf|rss|rss2|atom)/?$' ] = 'index.php?category_name=$matches[1]&feed=$matches[2]';
        $category_rewrite[ '(' . $category_nicename . ')/page/?([0-9]{1,})/?$' ] = 'index.php?category_name=$matches[1]&paged=$matches[2]';
        $category_rewrite[ '(' . $category_nicename . ')/?$' ] = 'index.php?category_name=$matches[1]';
    }

    // Redirect support from Old Category Base
    $old_category_base = get_option( 'category_base' ) ? get_option( 'category_base' ) : 'category';
    $old_category_base = trim( $old_category_base, '/' );
    $category_rewrite[ $old_category_base . '/(.*)$' ] = 'index.php?category_redirect=$matches[1]';

    return $category_rewrite;
}

function remove_category_url_query_vars( $public_query_vars ) {
    $public_query_vars[] = 'category_redirect';

    return $public_query_vars;
}

/**
 * Handles category redirects.
 *
 * @param $query_vars Current query vars.
 *
 * @return array $query_vars, or void if category_redirect is present.
 */
function remove_category_url_request( $query_vars ) {
    if ( isset( $query_vars['category_redirect'] ) ) {
        $catlink = trailingslashit( get_option( 'home' ) ) . user_trailingslashit( $query_vars['category_redirect'], 'category' );
        status_header( 301 );
        header( "Location: $catlink" );
        exit;
    }

    return $query_vars;
}

function remove_category_url_plugin_row_meta( $links, $file ) {
        if( plugin_basename( __FILE__ ) === $file ) {
            $links[] = sprintf(
                '<a target="_blank" href="%s">%s</a>',
                esc_url('http://wordlab.com.br/donate/'),
                __( 'Donate', 'remove_category_url' )
            );
        }
        return $links;
    }
  • I ended up installing the plugin, because that was the only way that worked. When I'm doing the site from scratch, I like to have total control over it, so if something is messed up I know I messed it up and where :D Thanks for the help tho :) – dingo_d Mar 15 '16 at 21:15
0

Place this code within your functions.php and you're good to go (even with WordPress 4.7.2)

/* hooks */
register_activation_hook(__FILE__,    'yw_no_category_slug_refresh_rules');
register_deactivation_hook(__FILE__,  'yw_no_category_slug_deactivate');

/* actions */
add_action('created_category',  'yw_no_category_slug_refresh_rules');
add_action('delete_category',   'yw_no_category_slug_refresh_rules');
add_action('edited_category',   'yw_no_category_slug_refresh_rules');
add_action('init',              'yw_no_category_slug_permastruct');

/* filters */
add_filter('category_rewrite_rules', 'yw_no_category_slug_rewrite_rules');
add_filter('query_vars',             'yw_no_category_slug_query_vars');    // Adds 'category_redirect' query variable
add_filter('request',                'yw_no_category_slug_request');       // Redirects if 'category_redirect' is set

function yw_no_category_slug_refresh_rules() {
    global $wp_rewrite;
    $wp_rewrite->flush_rules();
}

function yw_no_category_slug_deactivate() {
    remove_filter( 'category_rewrite_rules', 'yw_no_category_slug_rewrite_rules' ); // We don't want to insert our custom rules again
    yw_no_category_slug_refresh_rules();
}

/**
 * Removes category base.
 *
 * @return void
 */
function yw_no_category_slug_permastruct()
{
    global $wp_rewrite;
    global $wp_version;

    if ( $wp_version >= 3.4 ) {
        $wp_rewrite->extra_permastructs['category']['struct'] = '%category%';
    } else {
        $wp_rewrite->extra_permastructs['category'][0] = '%category%';
    }
}

/**
 * Adds our custom category rewrite rules.
 *
 * @param  array $category_rewrite Category rewrite rules.
 *
 * @return array
 */
function yw_no_category_slug_rewrite_rules($category_rewrite) {
    global $wp_rewrite;
    $category_rewrite=array();

    /* WPML is present: temporary disable terms_clauses filter to get all categories for rewrite */
    if ( class_exists( 'Sitepress' ) ) {
        global $sitepress;

        remove_filter( 'terms_clauses', array( $sitepress, 'terms_clauses' ) );
        $categories = get_categories( array( 'hide_empty' => false ) );
        //Fix provided by Albin here https://wordpress.org/support/topic/bug-with-wpml-2/#post-8362218
        //add_filter( 'terms_clauses', array( $sitepress, 'terms_clauses' ) );
        add_filter( 'terms_clauses', array( $sitepress, 'terms_clauses' ), 10, 4 );
    } else {
        $categories = get_categories( array( 'hide_empty' => false ) );
    }

    foreach( $categories as $category ) {
        $category_nicename = $category->slug;

        if ( $category->parent == $category->cat_ID ) {
            $category->parent = 0;
        } elseif ( $category->parent != 0 ) {
            $category_nicename = get_category_parents( $category->parent, false, '/', true ) . $category_nicename;
        }

        $category_rewrite['('.$category_nicename.')/(?:feed/)?(feed|rdf|rss|rss2|atom)/?$'] = 'index.php?category_name=$matches[1]&feed=$matches[2]';
        $category_rewrite["({$category_nicename})/{$wp_rewrite->pagination_base}/?([0-9]{1,})/?$"] = 'index.php?category_name=$matches[1]&paged=$matches[2]';
        $category_rewrite['('.$category_nicename.')/?$'] = 'index.php?category_name=$matches[1]';
    }

    // Redirect support from Old Category Base
    $old_category_base = get_option( 'category_base' ) ? get_option( 'category_base' ) : 'category';
    $old_category_base = trim( $old_category_base, '/' );
    $category_rewrite[$old_category_base.'/(.*)$'] = 'index.php?category_redirect=$matches[1]';

    return $category_rewrite;
}

function yw_no_category_slug_query_vars($public_query_vars) {
    $public_query_vars[] = 'category_redirect';
    return $public_query_vars;
}

/**
 * Handles category redirects.
 *
 * @param $query_vars Current query vars.
 *
 * @return array $query_vars, or void if category_redirect is present.
 */
function yw_no_category_slug_request($query_vars) {
    if( isset( $query_vars['category_redirect'] ) ) {
        $catlink = trailingslashit( get_option( 'home' ) ) . user_trailingslashit( $query_vars['category_redirect'], 'category' );
        status_header( 301 );
        header( "Location: $catlink" );
        exit();
    }

    return $query_vars;
}
  • Please edit your answer, and add an explanation: why could that solve the problem? – fuxia Mar 5 '17 at 20:44

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.