4

I tried to avoid ask this question, because I think that must be simple, but after long hours trying to get an answer I cant achieve it.

I am trying to get a permalink structure like this:

http://domain.com/u/USERNAME
http://domain.com/p/POSTID

To get that structure I set a "Custom structure" in the permalink settings of my Wordpress. This is the value of the custom structure:

/p/%post_id%

That is great for posts, but not for authors. Using that settings this is the result:

http://domain.com/p/u/USERNAME
http://domain.com/p/POSTID

And that can be changed via htaccess or functions.php.

Any idea will help me a lot. Thanks!

2 Answers 2

3

I would just change the author_base on the global $wp_rewrite object. Also add a field to the Permalink options page, so you can change it at will.

To start: a class to wrap everything up.

<?php
class Custom_Author_Base
{
    const SETTING = 'author_base';

    private static $ins = null;

    public static function instance()
    {
        is_null(self::$ins) && self::$ins = new self;
        return self::$ins;
    }

    public static function init()
    {
        add_action('plugins_loaded', array(self::instance(), '_setup'));
    }

    public function _setup()
    {
        // we'll add actions here
    }
}

Now, we can hook into admin init and use the settings API to add a field to the permalink page.

<?php
class Custom_Author_Base
{
    // snip snip

    public function _setup()
    {
        add_action('admin_init', array($this, 'fields'));
    }

    public function fields()
    {
        add_settings_field(
            self::SETTING,
            __('Author Base', 'custom-author-base'),
            array($this, 'field_cb'),
            'permalink',
            'optional',
            array('label_for' => self::SETTING)
        );
    }

    public function field_cb()
    {
        printf(
            '<input type="text" class="regular-text" name="%1$s" id="%1$s" value="%2$s" />',
            esc_attr(self::SETTING),
            esc_attr(get_option(self::SETTING))
        );
    }
}

Unfortunately, the settings API doesn't actually save anything on the permalink page, so you have to hook into load-options-permalink.php and do your own saving.

<?php
class Custom_Author_Base
{
    // snip snip

    public function _setup()
    {
        add_action('admin_init', array($this, 'fields'));
        add_action('load-options-permalink.php', array($this, 'maybe_save'));
    }

    public function maybe_save()
    {
        if ('POST' !== $_SERVER['REQUEST_METHOD']) {
            return;
        }

        if (!empty($_POST[self::SETTING])) {
            $res = sanitize_title_with_dashes($_POST[self::SETTING]);
            update_option(self::SETTING, $res);
            $this->set_base($res);
        } else {
            delete_option(self::SETTING);
        }
    }
}

Finally, you need to hook into init and set the author base (the set_base method used above).

<?php
class Custom_Author_Base
{
    // snip snip

    public function _setup()
    {
        add_action('init', array($this, 'set_base'));
        add_action('admin_init', array($this, 'fields'));
        add_action('load-options-permalink.php', array($this, 'maybe_save'));
    }

    public function set_base($base=null)
    {
        global $wp_rewrite;

        is_null($base) && $base = get_option(self::SETTING);

        if ($base) {
            $wp_rewrite->author_base = $base;
        }
    }
}

As a plugin.

1
  • 2
    Im really impressed, thank you for your time making a full plugin to help me, is really awesome!. I made a pull request in your GitHub to finally solve the problem with a small modification. Thanks.
    – Masta
    Commented Dec 25, 2012 at 20:11
3

Without knowing what you already have for rewrites you can try this:

You can add this to yourfunctions.php (or better: Into a (mu-)plugin):

<?php 
/* Plugin Name: (WPSE) 77228 Add /u/ rewrite rules */

add_action( 'init', 'add_author_rules' );  
function add_author_rules()
{   
    add_rewrite_rule(  
        'u/([^/]+)/?',
        'index.php?author=$matches[1]',
        'top'
    );  

    add_rewrite_rule(  
        'u/([^/]+)/page/?([0-9]{1,})/?",  
        'index.php?author=$matches[1]&paged=$matches[2]',
        'top'
    );  

    add_rewrite_rule(  
        'u/([^/]+)/(feed|rdf|rss|rss2|atom)/?',
        'index.php?author=$matches[1]&feed=$matches[2]',
        'top'
    );  

    add_rewrite_rule(  
        'u/([^/]+)/feed/(feed|rdf|rss|rss2|atom)/?',
        'index.php?author=$matches[1]&feed=$matches[2]',  
        'top'
    );  
}

If that doesnt work you must replace index.php?author= with index.php?author_name= for each entry.

Tell me if it works.

You can test your rewrite rules with the following code in your functions.php. I always use this code and helped me out a couple of times. Once you make a rewrite you can see if it shows up correctly.

define( 'REWRITE_RULES_INSPECTOR_VERSION', '1.1' );
define( 'REWRITE_RULES_INSPECTOR_ROOT', dirname( __FILE__ ) );
define( 'REWRITE_RULES_INSPECTOR_FILE_PATH' , REWRITE_RULES_INSPECTOR_ROOT . '/' . basename( __FILE__ ) );

class Rewrite_Rules_Inspector
{

var $parent_slug = 'tools.php';
var $page_slug = 'rewrite-rules-inspector';
var $view_cap = 'manage_options';
var $flushing_enabled = true;
var $sources = array();

/**
 * Construct the plugin
 */
function __construct() {

    // This plugin only runs in the admin, but we need it initialized on init
    add_action( 'init', array( $this, 'action_init' ) );

}

/**
 * Initialize the plugin
 */
function action_init() {

    if ( !is_admin() )
        return;

    add_action( 'admin_menu', array( $this, 'action_admin_menu' ) );

    // Allow the view to be placed elsewhere than tools.php
    $this->parent_slug = apply_filters( 'rri_parent_slug', $this->parent_slug );

    // Whether or not users can flush the rewrite rules from this tool
    $this->flushing_enabled = apply_filters( 'rri_flushing_enabled', $this->flushing_enabled );

    // User actions available for the rewrite rules page
    if ( isset( $_GET['page'], $_GET['action'] ) && $_GET['page'] == $this->page_slug && $_GET['action'] == 'download-rules' )
        add_action( 'admin_init', array( $this, 'download_rules' ) );
    elseif ( isset( $_GET['page'], $_GET['action'] ) && $_GET['page'] == $this->page_slug && $_GET['action'] == 'flush-rules' )
        add_action( 'admin_init', array( $this, 'flush_rules' ) );
    elseif ( isset( $_GET['page'], $_GET['message'] ) && $_GET['page'] == $this->page_slug && $_GET['message'] == 'flush-success' )
        add_action( 'admin_notices', array( $this, 'action_admin_notices' ) );

}

/**
 * Add our sub-menu page to the VIP dashboard navigation
 */
function action_admin_menu() {

    add_submenu_page( $this->parent_slug, __( 'Rewrite Rules Inspector', 'rewrite-rules-inspector' ), __( 'Rewrite Rules', 'rewrite-rules-inspector' ), $this->view_cap, $this->page_slug, array( $this, 'view_rules' ) );

}

/**
 * Show a message when you've successfully flushed your rewrite rules
 */
function action_admin_notices() {
    echo '<div class="message updated"><p>' . __( 'Rewrite rules flushed.', 'rewrite-rules-inspector' ) . '</p></div>';
}

/**
 * Get the rewrite rules for the current view
 */
function get_rules() {
    global $wp_rewrite;

    $rewrite_rules_array = array();
    $rewrite_rules = get_option( 'rewrite_rules' );
    if ( !$rewrite_rules )
        $rewrite_rules = array();
    // Track down which rewrite rules are associated with which methods by breaking it down
    $rewrite_rules_by_source = array();
    $rewrite_rules_by_source['post'] = $wp_rewrite->generate_rewrite_rules( $wp_rewrite->permalink_structure, EP_PERMALINK );
    $rewrite_rules_by_source['date'] = $wp_rewrite->generate_rewrite_rules( $wp_rewrite->get_date_permastruct(), EP_DATE );
    $rewrite_rules_by_source['root'] = $wp_rewrite->generate_rewrite_rules( $wp_rewrite->root . '/', EP_ROOT );
    $rewrite_rules_by_source['comments'] = $wp_rewrite->generate_rewrite_rules( $wp_rewrite->root . $wp_rewrite->comments_base, EP_COMMENTS, true, true, true, false );
    $rewrite_rules_by_source['search'] = $wp_rewrite->generate_rewrite_rules( $wp_rewrite->get_search_permastruct(), EP_SEARCH );
    $rewrite_rules_by_source['author'] = $wp_rewrite->generate_rewrite_rules($wp_rewrite->get_author_permastruct(), EP_AUTHORS );
    $rewrite_rules_by_source['page'] = $wp_rewrite->page_rewrite_rules();

    // Extra permastructs including tags, categories, etc.
    foreach ( $wp_rewrite->extra_permastructs as $permastructname => $permastruct ) {
        if ( is_array( $permastruct ) ) {
            // Pre 3.4 compat
            if ( count( $permastruct ) == 2 )
                $rewrite_rules_by_source[$permastructname] = $wp_rewrite->generate_rewrite_rules( $permastruct[0], $permastruct[1] );
            else
                $rewrite_rules_by_source[$permastructname] = $wp_rewrite->generate_rewrite_rules( $permastruct['struct'], $permastruct['ep_mask'], $permastruct['paged'], $permastruct['feed'], $permastruct['forcomments'], $permastruct['walk_dirs'], $permastruct['endpoints'] );
        } else {
            $rewrite_rules_by_source[$permastructname] = $wp_rewrite->generate_rewrite_rules( $permastruct, EP_NONE );
        }
    }

    // Apply the filters used in core just in case
    foreach( $rewrite_rules_by_source as $source => $rules ) {
        $rewrite_rules_by_source[$source] = apply_filters( $source . '_rewrite_rules', $rules );
        if ( 'post_tag' == $source )
            $rewrite_rules_by_source[$source] = apply_filters( 'tag_rewrite_rules', $rules );
    }

    foreach( $rewrite_rules as $rule => $rewrite ) {
        $rewrite_rules_array[$rule]['rewrite'] = $rewrite;
        foreach( $rewrite_rules_by_source as $source => $rules ) {
            if ( array_key_exists( $rule, $rules ) ) {
                $rewrite_rules_array[$rule]['source'] = $source;
            }
        }
        if ( !isset( $rewrite_rules_array[$rule]['source'] ) )
            $rewrite_rules_array[$rule]['source'] = 'other';
    }

    // Find any rewrite rules that should've been generated but weren't
    $maybe_missing = $wp_rewrite->rewrite_rules();
    $missing_rules = array();
    $rewrite_rules_array = array_reverse( $rewrite_rules_array, true );
    foreach( $maybe_missing as $rule => $rewrite ) {
        if ( !array_key_exists( $rule, $rewrite_rules_array ) ) {
            $rewrite_rules_array[$rule] = array( 
                'rewrite' => $rewrite,
                'source' => 'missing',
            );
        }
    }
    // Prepend rules so it's obvious
    $rewrite_rules_array = array_reverse( $rewrite_rules_array, true );

    // Allow static sources of rewrite rules to override, etc.
    $rewrite_rules_array = apply_filters( 'rri_rewrite_rules', $rewrite_rules_array );
    // Set the sources used in our filtering
    $sources = array( 'all' );
    foreach( $rewrite_rules_array as $rule => $data ) {
        $sources[] = $data['source'];
    }
    $this->sources = array_unique( $sources );

    if ( ! empty( $_GET['s'] ) ) {
        $match_path = parse_url( esc_url( $_GET['s'] ), PHP_URL_PATH );
        $wordpress_subdir_for_site = parse_url( home_url(), PHP_URL_PATH );
        if ( ! empty( $wordpress_subdir_for_site ) ) {
            $match_path = str_replace( $wordpress_subdir_for_site, '', $match_path );
        }
        $match_path = ltrim( $match_path, '/' );
    }

    $should_filter_by_source = ! empty( $_GET['source'] ) && 'all' !== $_GET[ 'source' ] && in_array( $_GET['source'], $this->sources );

    // Filter based on match or source if necessary
    foreach( $rewrite_rules_array as $rule => $data ) {
        // If we're searching rules based on URL and there's no match, don't return it
        if ( ! empty( $match_path ) && ! preg_match( "!^$rule!", $match_path ) ) {
            unset( $rewrite_rules_array[$rule] );
        } elseif ( $should_filter_by_source && $data['source'] != $_GET['source'] ) {
            unset( $rewrite_rules_array[$rule] );
        }
    }

    // Return our array of rewrite rules to be used
    return $rewrite_rules_array;

}

/**
 * View the rewrite rules for the site
 */
function view_rules() {

    // Bump view stats or do something else on page load
    do_action( 'rri_view_rewrite_rules' );

    $wp_list_table = new Rewrite_Rules_Inspector_List_Table();
    $wp_list_table->prepare_items();

    ?>
    <style>
        #the-list tr.type-sunrise,
        #the-list tr.type-custom {
            background-color: #EEC7F0;
        }
        #the-list tr.type-sunrise td,
        #the-list tr.type-custom td {
            border-top-color: #F4E6F5;
            border-bottom-color: #EFBBF2;
        }
        #the-list tr.source-missing {
            background-color: #f7a8a9;
        }
        #the-list tr.type-missing td {
            border-top-color: #FECFD0;
            border-bottom-color: #f99b9d;
        }
    </style>
    <div class="wrap">
    <?php screen_icon( 'tools' ); ?>
    <h2><?php _e( 'Rewrite Rules Inspector', 'rewrite-rules-inspector' ); ?></h2>

    <?php
    $rules = $this->get_rules();
    if ( empty( $rules ) ) {
        $error_message = apply_filters( 'rri_message_no_rules', __( 'No rewrite rules yet, try flushing.', 'rewrite-rules-inspector' ) );
        echo '<div class="message error"><p>' . $error_message . '</p></div>';
    } else if ( in_array( 'missing', $this->sources ) ) {
        $error_message = apply_filters( 'rri_message_missing_rules', __( 'Some rewrite rules may be missing, try flushing.', 'rewrite-rules-inspector' ) );
        echo '<div class="message error"><p>' . $error_message . '</p></div>';
    }
    ?>

    <?php if ( ! empty( $_GET['s'] ) ): ?>
    <p><?php printf( __( 'A listing of all %1$s rewrite rules for this site that match "<a target="_blank" href="%2$s">%3$s</a>"' ), count( $wp_list_table->items ), esc_url( $_GET['s'] ), esc_url( $_GET['s'] ) ); ?></p>
    <?php else: ?>
    <p><?php printf( __( 'A listing of all %1$s rewrite rules for this site.' ), count( $wp_list_table->items ) ); ?></p>
    <?php endif; ?>

    <?php $wp_list_table->display(); ?>

    </div>

<?php

}

/**
 * Process a user's request to download a set of the rewrite rules
 * Prompts a download of the current set of rules as a text file by
 * setting the header. Respects current filter rules.
 */
function download_rules() {

    // Check nonce and permissions
    check_admin_referer( 'download-rules' );
    if ( !current_user_can( $this->view_cap ) )
        wp_die( __( 'You do not have permissions to perform this action.' ) );

    // Get the rewrite rules and prompt the user to download them
    // File is saved as YYYYMMDD.themename.rewriterules.txt 
    $theme_name = sanitize_key( get_option( 'stylesheet' ) );
    $filename = date( 'Ymd' ) . '.' . $theme_name . '.rewriterules.txt';
    header( 'Content-Type: text/plain' );
    header( 'Content-Disposition: attachment; filename="' . $filename . '"' );

    $rewrite_rules = $this->get_rules();
    $rules_to_export = array();
    foreach( $rewrite_rules as $rule => $data ) {
        $rules_to_export[$rule] = $data['rewrite'];
    }
    echo var_export( $rules_to_export, true );
    exit;
}

/**
 * Allow a user to flush rewrite rules for their site
 */
function flush_rules() {
    global $plugin_page;

    // Check nonce and permissions
    check_admin_referer( 'flush-rules' );
    if ( !current_user_can( $this->view_cap ) || !$this->flushing_enabled )
        wp_die( __( 'You do not have permissions to perform this action.' ) );

    flush_rewrite_rules( false );
    do_action( 'rri_flush_rules' );

    // Woo hoo!
    $args = array(
            'message' => 'flush-success',
        );
    wp_safe_redirect( add_query_arg( $args, menu_page_url( $plugin_page, false ) ) );
    exit;
}

}

global $rewrite_rules_inspector;
$rewrite_rules_inspector = new Rewrite_Rules_Inspector();

// Load the WP_List_Table class if it doesn't yet exist
if ( !class_exists( 'WP_List_Table' ) )
require_once ABSPATH . '/wp-admin/includes/class-wp-list-table.php';

/**
 * Display the rewrite rules in an easy to digest list table
 */
class Rewrite_Rules_Inspector_List_Table extends WP_List_Table {

/**
 * Construct the list table
 */
function __construct() {

    $screen = get_current_screen();

    parent::__construct( array(
        'plural' => 'Rewrite Rules',
    ) );
}

/**
 * Load all of the matching rewrite rules into our list table
 */
function prepare_items() {
    global $rewrite_rules_inspector;

    $columns = $this->get_columns();
    $hidden = array();
    $sortable = array();
    $this->_column_headers = array( $columns, $hidden, $sortable );

    $this->items = $rewrite_rules_inspector->get_rules();
}

/**
 * What to print when no items were found
 */
function no_items() {
    _e( 'No rewrite rules were found.', 'rewrite-rules-inspector' );
}

/**
 * Display the navigation for the list table
 */
function display_tablenav( $which ) {
    global $plugin_page, $rewrite_rules_inspector;

    $search = ! empty( $_GET['s'] ) ? esc_url( $_GET['s'] ) : '';

    if ( $which == 'bottom' )
        return false;
    ?>
    <div class="custom-tablenav-top" style="padding-top:5px;padding-bottom:10px;">
    <div style="float:right;">
        <?php
        // Only show the flush button if enabled
        if ( $rewrite_rules_inspector->flushing_enabled ):
        ?>
        <?php
            // Flush the current set of rewrite rules
            $args = array(
                    'action' => 'flush-rules',
                    '_wpnonce' => wp_create_nonce( 'flush-rules' ),
                );
            $flush_url = add_query_arg( $args, menu_page_url( $plugin_page, false ) );
        ?>
        <a title="<?php _e( 'Flush your rewrite rules to regenerate them', 'rewrite-rules-inspector' ); ?>" class="button-secondary" href="<?php echo esc_url( $flush_url ); ?>"><?php _e( 'Flush Rules', 'rewrite-rules-inspector' ); ?></a>
        <?php endif; ?>
        <?php
            // Prepare the link to download a set of rules
            // Link is contingent on the current filter state   
            $args = array(
                'action' => 'download-rules',
                '_wpnonce' => wp_create_nonce( 'download-rules' ),
            );
            if ( isset( $_GET['source'] ) && in_array( $_GET['source'], $rewrite_rules_inspector->sources ) )
                $args['source'] = sanitize_key( $_GET['source'] );
            else
                $args['source'] = 'all';
            $args['s'] = !empty( $_GET['s'] ) ? $_GET['s'] : '';

            $download_url = add_query_arg( $args, menu_page_url( $plugin_page, false ) );
        ?>
        <a title="<?php _e( 'Download current list of rules as a .txt file', 'rewrite-rules-inspector' ); ?>" class="button-secondary" href="<?php echo esc_url( $download_url ); ?>"><?php _e( 'Download', 'rewrite-rules-inspector' ); ?></a>
    </div>
    <form method="GET">
        <label for="s"><?php _e( 'Match URL:', 'rewrite-rules-inspector' ); ?></label>
        <input type="text" id="s" name="s" value="<?php echo esc_attr( $search ); ?>" size="50"/>
        <input type="hidden" id="page" name="page" value="<?php echo esc_attr( $plugin_page ); ?>" />
        <label for="source"><?php _e( 'Rule Source:', 'rewrite-rules-inspector' ); ?></label>
        <select id="source" name="source">
        <?php
            if (isset( $_GET['source'] ) && in_array( $_GET['source'], $rewrite_rules_inspector->sources ) )
                $filter_source = sanitize_key( $_GET['source'] );
            else
                $filter_source = 'all';
            foreach( $rewrite_rules_inspector->sources as $value ) {
                echo '<option value="' . esc_attr( $value ) . '" ';
                selected( $filter_source, $value );
                echo '>' . esc_attr( $value ) . '</option>';
            }
        ?>
        </select>
        <?php submit_button( __( 'Filter', 'rewrite-rules-inspector' ), 'primary', null, false ); ?>
        <?php if ( $search || !empty( $_GET['source'] ) ): ?>
            <a href="<?php menu_page_url( $plugin_page ); ?>" class="button-secondary"><?php _e( 'Reset', 'rewrite-rules-inspector' ); ?></a>
        <?php endif; ?>
    </form>
    </div>
    <?php
}

/**
 * Define the columns for our list table
 */
function get_columns() {

    $columns = array(
            'rule'          => __( 'Rule', 'rewrite-rules-inspector' ),
            'rewrite'       => __( 'Rewrite', 'rewrite-rules-inspector' ),
            'source'        => __( 'Source', 'rewrite-rules-inspector' ),
        );
    return $columns;

}

/**
 * Display each row of rewrite rule data
 */
function display_rows() {
    foreach ( $this->items as $rewrite_rule => $rewrite_data ) {
        $this->single_row( $rewrite_rule, $rewrite_data );
    }
}

/**
 * Display a single row of rewrite rule data
 */
function single_row( $rule, $data ) {

    $source = $data['source'];
    $rewrite = $data['rewrite'];

    $class = 'source-' . $source;

    echo "<tr class='$class'>";

    list( $columns, $hidden ) = $this->get_column_info();

    foreach ( $columns as $column_name => $column_display_name ) {

        switch ( $column_name ) {
            case 'rule':
                echo "<td class='column-rule'><strong>" . esc_html( $rule ) . "</strong></td>";
                break;
            case 'rewrite':
                echo "<td class='column-rewrite'>" . esc_html( $rewrite ) . "</td>";
                break;
            case 'source':
                echo "<td class='column-source'>" . esc_html( $source ) . "</td>";
                break;
        }
    }

    echo "</tr>";
}
}

When you're going to rewrite urls, set your permalinks to the option at the top so you have links like ?p=87. This way you know the structure.

3
  • After you add these rules visit the permalinks section to flush the rewrites.
    – jochem
    Commented Dec 25, 2012 at 16:36
  • Im trying to make it works, but still I can achieve it. Thanks for your help.
    – Masta
    Commented Dec 25, 2012 at 20:12
  • I updated my original answer with some tests you can run..
    – jochem
    Commented Dec 25, 2012 at 23:26

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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