Custom rewrite rules in Wordpress - wordpress

I'm in trouble with the internal wordpress rewrite rules.
I've read this thread but I still can't get any results: wp_rewrite in a WordPress Plugin
I explain my situation:
1) I have a page_template called 'myplugin_template.php' associated to a wordpress page called "mypage".
<?php
get_header();
switch ($_GET['action']) {
case = "show" {
echo $_GET['say'];
}
}
get_footer();
?>
2) I need to create a rewrite rule for this link:
http://myblog/index.php?pagename=mypage&action=show&say=hello_world
If I use this url all the things works without problems but I'd like to achieve this result:
http://myblog/mypage/say/hello_world/
I really don't want to hack my .htaccess file but I don't know how I can do this with the internal wordpress rewriter.

You'll need to add your own rewrite rule and query vars - pop this in functions.php;
function my_rewrite_rules($rules)
{
global $wp_rewrite;
// the slug of the page to handle these rules
$my_page = 'mypage';
// the key is a regular expression
// the value maps matches into a query string
$my_rule = array(
'mypage/(.+)/(.+)/?' => 'index.php?pagename=' . $my_page . '&my_action=$matches[1]&my_show=$matches[2]'
);
return array_merge($my_rule, $rules);
}
add_filter('page_rewrite_rules', 'my_rewrite_rules');
function my_query_vars($vars)
{
// these values should match those in the rewrite rule query string above
// I recommend using something more unique than 'action' and 'show', as you
// could collide with other plugins or WordPress core
$my_vars = array(
'my_action',
'my_show'
);
return array_merge($my_vars, $vars);
}
add_filter('query_vars', 'my_query_vars');
Now in your page template, replace $_GET[$var] with get_query_var($var) like so;
<?php
get_header();
switch (get_query_var('my_action')) {
case = "show" {
echo esc_html(get_query_var('my_say')); // escape!
}
}
get_footer();
?>

Related

Override wordpress routing / url rewriting?

I would like to serve different content based on the URL.
I started with a custom page setup via a custom template but I am open to something else.
The main goal is to have one PHP page that can serve different contents programmatically based on the URL.
For example:
https://some-url.com/my-plugin/ -> run my page
https://some-url.com/my-plugin/foo/ -> run my page
https://some-url.com/my-plugin/foo2/abc/ -> run my page
etc...
I have been looking at add_rewrite_rule, add_rewrite_tag, flush_rewrite_rules and WP_Rewrite API but I am getting confused about which one I should use?
I found an example here but I could not make it work, I get 404s, any idea why?:
/*
Plugin Name: Products Plugin
Plugin URI: http://clivern.com/
Description: Register URL rules for our products
Version: 1.0
Author: Clivern
Author URI: http://clivern.com
License: MIT
*/
function products_plugin_activate() {
products_plugin_rules();
flush_rewrite_rules();
}
function products_plugin_deactivate() {
flush_rewrite_rules();
}
function products_plugin_rules() {
add_rewrite_rule('products/?([^/]*)', 'index.php?pagename=products&product_id=$matches[1]', 'top');
}
function products_plugin_query_vars($vars) {
$vars[] = 'product_id';
return $vars;
}
function products_plugin_display() {
$products_page = get_query_var('pagename');
$product_id = get_query_var('product_id');
if ('products' == $products_page && '' == $product_id):
//show all products
exit;
elseif ('products' == $products_page && '' != $product_id):
//show product page
exit;
endif;
}
//register activation function
register_activation_hook(__FILE__, 'products_plugin_activate');
//register deactivation function
register_deactivation_hook(__FILE__, 'products_plugin_deactivate');
//add rewrite rules in case another plugin flushes rules
add_action('init', 'products_plugin_rules');
//add plugin query vars (product_id) to wordpress
add_filter('query_vars', 'products_plugin_query_vars');
//register plugin custom pages display
add_filter('template_redirect', 'products_plugin_display');
First of all, make sure your pretty permalinks are enabled, in this case the option "Plain" in Settings - Permalinks should be unselected:
Select one of these options to enable pretty permalinks
You can check whether pretty permalinks are enabled in the code like so:
function is_enabled_pretty_permalinks() {
return !empty( get_option( 'permalink_structure' ) );
}
if ( is_enabled_pretty_permalinks() ) {
echo 'Pretty permalinks enabled';
}
Next add a new rewrite rule:
function add_my_rewrite_rule() {
$page_slug = 'products'; // slug of the page you want to be shown to
$param = 'do'; // param name you want to handle on the page
add_rewrite_rule('my-plugin/?([^/]*)', 'index.php?pagename=' . $page_slug . '&' . $param . '=$matches[1]', 'top');
}
add_action('init', 'add_my_rewrite_rule');
Add your parameter to query vars so you will be able to handle it on the page:
function add_my_query_vars($vars) {
$vars[] = 'do'; // param name you want to handle on the page
return $vars;
}
add_filter('query_vars', 'add_my_query_vars');
Then you can access your query var do in the page template or in a shortcode, for example:
function my_plugin_shortcode_handler( $atts ){
$do = get_query_var( 'do' );
if ( $do === 'this' ) {
return 'do this';
} else {
return 'do that';
}
}
add_shortcode( 'myshortcode', 'my_plugin_shortcode_handler' );
Then place the shortcode to the content via Gutenberg.
Check out the links:
https://some-url.com/my-plugin/this/ - outputs "do this"
https://some-url.com/my-plugin/that/ - outputs "do that".
This can be solved by using query params. Like you mentioned you have set up custom page via a custom template. You can read the URL and check for the parameters and based on that you can send data from the PHP template page.
e.g,
function cleanTheInput($data) {
$data = trim($data);
$data = stripslashes($data);
$data = htmlspecialchars($data);
$data = htmlentities($data);
return $data;
}
if (isset($_GET['page_url'])) {
$theUrl = cleanTheInput($_GET['page_url']);
}
if($theUrl == 266)){
// data for https://some-url.com/?page_url=266
}
if($theUrl == 366)){
// data for https://some-url.com/?page_url=366
}

Adding custom rewrite rules to WordPress

My WordPress site has a portfolio that is at www.mysite.com/portfolio/. The portfolio sections and items are administered through a custom plugin I created. I want to access the individual items like www.mysite.com/portfolio/my-cool-photo and have that put "my-cool-photo" into a query string like ?portfolio_item=my-cool-photo so I can read it from my code.
In the plugins activation PHP file I have this code:
function add_rewrite_rules($wp_rewrite) {
$new_rules = array(
'portfolio/(.+)/?$' => 'index.php?&portfolio_item=$1'
);
$wp_rewrite->rules = $new_rules + $wp_rewrite->rules;
}
add_action('generate_rewrite_rules', 'add_rewrite_rules');
function query_vars($public_query_vars) {
$public_query_vars[] = "portfolio_item";
return $public_query_vars;
}
add_filter('query_vars', 'query_vars');
This adds the rewrite rule to the array OK. The problem is it's not doing anything. When I go to www.mysite.com/portfolio/testing/ I get the "This is somewhat embarrassing, isn’t it?" WordPress 404 error page. Obviously the redirect isn't working, so the query string won't be filled, but just to make sure I did this:
global $wp_query, $wp_rewrite;
if ($wp_rewrite->using_permalinks()) {
$searchKey = $wp_query->query_vars['portfolio_item'];
} else {
$searchKey = $_GET['portfolio_item'];
}
...and sure enough the query string isn't getting passed.
Is there something I'm missing?
After you update the WordPress rewrite rules, you need to flush them:
http://codex.wordpress.org/Function_Reference/flush_rewrite_rules
You can choose to flush with the $hard parameter true, and then you should be able to see your rewrite rules in the .htaccess file.
Here is the example rewriting url code. Hopefully this will help users.
In this url like http://www.domainname.com/test-page?urltags=ABCD is run as http://www.domainname.com/test-page/ABCD
<?php
function add_my_rule() {
global $wp;
$wp->add_query_var('tags');
add_rewrite_rule('test-page/([^/]*)','index.php?pagename=test-page&urltags=$matches[1]','top');
}
function add_my_tags() {
add_rewrite_tag('%urltags%', '([^&]+)');
}
add_action('init', 'add_my_rule');
add_action('init', 'add_my_tags', 10, 0);
?>

How to make Wordpress permalinks ignore custom url rewrite

i'm currently working on a wordpress site that needs to have the option to be offered in french. i've found a way to make the theme work with the fr_FR po and mo files when i add a querystring variable l. i.e.
site.tld will yield the vanilla english site, while site.tld/?l=fr will activate the following code in my functions.php to serve the french translation:
<?php
// http://codex.wordpress.org/Plugin_API/Filter_Reference/locale
add_filter( 'locale', 'set_my_locale' );
function set_my_locale( $lang ) {
if ("fr" == substr(strtolower(trim(strip_tags(stripslashes($_GET['l'])))), 0, 2)) {
// set language to french
return "fr_FR";
} else {
// return original locale
return $lang;
}
}
?>
this setup is already working. my question is: how do i rewrite the url so instead of site.tld/?l=fr i can just prepend the folder structure with fr i.e. site.tld/fr/?
so like if there's a page site.tld/portoflio/autumn/ in english, site.tld/fr/portfolio/autumn/ will spit out the french one. i got this idea from the apple website, where the language is always prepended to the folder structure.
i can already make this happen with an external redirect in htaccess:
RewriteBase /
RewriteCond ^[a-z]{2}/
RewriteRule ^([a-z]{2})/(.*)$ /$2?l=$1 [R,L]
this works, but once i remove the R flag it serves the french-translated 404 not found isntead. i'm guessing what i did is messing-up with wordpress' rewrite rules because i need to use pretty permalinks. right now i'm set to use Month and name (/%year%/%monthnum%/%postname%/) in the admin general options.
my question is, how do i make wordpress ignore the /fr/ part and still serve the correct page/post/etc?
is this even possible? am i on the right track here? i need your help especially doing what's WISE and not just what's NICE. i tried this http://pmg.co/a-mostly-complete-guide-to-the-wordpress-rewrite-api but for the life of me i can't make it work. :/
UPDATE: ok, so here's some progress taking cue from #relu below:
i made sure my rules were canned from .htaccess
i then added the rules from #relu but in the following way, because Relu's always sent me to the same address without the /fr:
<?
// http://pmg.co/custom-wordpress-shortlinks
add_action( 'init', 'pmgtut_add_rewrites' );
function pmgtut_add_rewrites() {
add_rewrite_rule( '^([a-z]{2})/?$', 'index.php?l=$matches[1]', 'top' );
add_rewrite_rule( '^([a-z]{2})/(.*)$', 'index.php?l=$matches[1]&q=$matches[2]', 'top' );
}
add_filter( 'query_vars', 'pmgtut_query_vars', 10, 1 );
function pmgtut_query_vars( $vars ) {
$vars[] = 'l';
return $vars;
}
?>
now the /fr stays in the address bar, so that's a good thing, but two problems persist:
wordpress is serving me the main index page. seems like it's not using the &q=$matches[2] part of the rule; and
the locale still doesn't get set properly. i checked if the variable l is getting fed, so i added echo 'l: $l'; after $l = get_query_var('l'); and interestingly i get two echoes: one l: and another l: fr right after that. is the locale filter being run twice? seems like the first time it doesn't see the value of the queryvar, and then there seems to be a second time that it outputs l: with fr already in there.. at the end of the day the locale still doesn't get changed.
aaaah help.
FINAL UPDATE: in my last breath of frustration i searched again, and found this plugin qtranslate. does what i need. thanks y'all, esp to #relu's epic effort and stayin pow'r.
Add this to your functions.php
add_filter( 'rewrite_rules_array','my_insert_rewrite_rules' );
add_filter( 'query_vars','my_insert_query_vars' );
add_action( 'wp_loaded','my_flush_rules' );
// flush_rules() if our rules are not yet included
function my_flush_rules(){
$rules = get_option( 'rewrite_rules' );
if ( ! isset( $rules['^([a-z]{2})/(.*)$'] ) ) {
global $wp_rewrite;
$wp_rewrite->flush_rules();
}
}
// Adding a new rule
function my_insert_rewrite_rules( $rules )
{
$newrules = array();
$newrules['^([a-z]{2})/(.*)$'] = 'index.php?l=$matches[1]&q=$matches[2]';
return $newrules + $rules;
}
// Adding the l var so that WP recognizes it
function my_insert_query_vars( $vars )
{
array_push($vars, 'l');
return $vars;
}
Haven't tested this, it may need some tweaking, but this is the way to do it.
I've basically adapted the version found here.
It also registers the "l" query var so you can get it's value by calling: get_query_var
add_filter( 'locale', 'set_my_locale' );
function set_my_locale( $lang ) {
$l = get_query_var('l');
if ("fr" == $l) {
// set language to french
return "fr_FR";
} else {
// return original locale
return $lang;
}
}
UDATE: Ignore the above!
Remove the my_insert_query_vars() filter and replace it with:
function register_rewrite_tag() {
add_rewrite_tag('%l%', '([a-z]{2})');
}
add_action('init', 'register_rewrite_tag');
Here are also the updated rewrites for pages and posts, I've looked at WordPress' $wp_rewrite->rules and adapted them to fit this special case:
function pmgtut_add_rewrites() {
add_rewrite_rule( '^([a-z]{2})/?$', 'index.php?l=$matches[1]', 'top' );
add_rewrite_rule( '^([a-z]{2})/(.?.+?)(/[0-9]+)?/?$', 'index.php?l=$matches[1]&pagename=$matches[2]&page=$matches[3]', 'top' ); // Pages
add_rewrite_rule( '^([a-z]{2})/([^/]+)(/[0-9]+)?/?$', 'index.php?l=$matches[1]&name=$matches[2]&page=$matches[3]', 'top' ); // Posts
}
maybe out of topic but here's another rewrite rule for languages of the form; en_us/ or jp/
add_rewrite_rule( '^([a-z]{2}_[A-Z]{2}|^[a-z]{2})/?$', 'index.php?l=$matches[1]', 'top' );

Single Taxonomy Attached to Multiple Post Types Template Question

So I have a site with about 6 custom post types.Three of these post types are attached to a single custom taxonomy.
I would like to be able to list_cats in the sidebar and spit out a url like ../%post_type%/%taxonomy%/ and have this take you to the taxonomy-%taxonomy%.php template and return only results of the %post_type%.
I can create a conditional that will read the current %post_type% and style correctly once in the taxonomy-%taxonomy%.php file. I need some direction on the most effective method to modify the URL to pass the %post_type% to the query.
Thanks in advance. I have searched for a couple days with no clear answer.
So here are my feeble attempts at a solution yet they are one big FAIL
This is based off a discussion found at rlmseo.com ant the answer here.
Trying to add the post type to a variable to use in the Query:
practice-areas is a taxonomy. Trying to add a variable that I can use to filter the taxonomy by post type in taxonomy-practice-areas.php
function add_query_vars($aVars) {
$aVars[] = "cust_pt_var"; // represents the name of the custom post type as shown in the URL
return $aVars;
}
// hook add_query_vars function into query_vars
add_filter('query_vars', 'add_query_vars');
function add_rewrite_rules($aRules) {
$aNewRules = array('practice-areas/([^/]+)/pt/([^/]+)/?$' => 'index.php?practice-areas=$matches[1]&cust_pt_var=$matches[2]');
$aRules = $aNewRules + $aRules;
return $aRules;
}
// hook add_rewrite_rules function into rewrite_rules_array
add_filter('rewrite_rules_array', 'add_rewrite_rules')
Trying to pass query variables in the rewrite
Trying to add the post types (portfolio & clients) as well as the practice-areas taxonomy term to the query at rewrite.
function add_rewrite_rules($aRules) {
$aNewRules = array('portfolio/practice-areas/([^/]+)/?$' => 'index.php?post_type=portfolio&practice-areas=$matches[1]');
$aNewRules = array('clients/practice-areas/([^/]+)/?$' => 'index.php?post_type=clients&practice-areas=$matches[1]');
$aRules = $aNewRules + $aRules;
return $aRules;
}
// hook add_rewrite_rules function into rewrite_rules_array
add_filter('rewrite_rules_array', 'add_rewrite_rules')
You should be able to accomplish that by modifying WP's rewrite rules:
add_action( 'init', 'ss_permalinks' );
function ss_permalinks() {
add_rewrite_rule(
'page/remove/([^/]+)/?',
'index.php?pagename=page&service=$matches[1]',
'top'
);
}
add_filter( 'query_vars', 'ss_query_vars' );
function ss_query_vars( $query_vars ) {
$query_vars[] = 'removeid';
return $query_vars;
}
This is incomplete and you'll need to change the target URL and rule itself, but this is the basis. Re-save your permalink settings once after implementing. page is the slug of the page to point to when the user access this URL (in this case domain.com/page/remove/432), and $matches[1] should be the number after remove/ in the URL. This number is accessible by the variable specified later, $query_vars[] = 'removeid'; / '$removeid' on the target page's template will be the number in the URL, if specified.
Ok so I chucked the rewrite all together and went with a $_POST solution. It works really well and no slop in my URL.
Put in Destination Page:
<?php
$myvar = $_POST['formVar'];
query_posts($query_string . '&post_type='.$myvar );
?>
Put in Originating Page:
<!-- The Form: Put this form somewhere on the page: -->
<form method=post name="post_form" action="">
<input type="hidden" name="formVar" value="">
</form>
<!-- The URL: Here I am assigning the Destination URL to a variable. This can be whatever you want the destination page to be. -->
<?php
$dest_url = get_bloginfo('url').'/practice-areas/'. $category->slug;
?>
<!-- The Link: Here we are assigning 'portfolio' to 'formVar' and setting the form 'action' to the $dest_url value. The title stuff is irrelevant here-->
<a href="" onclick="document.post_form.formVar.value='portfolio'; document.post_form.action ='<?php $dest_url; ?>'; document.post_form.submit(); return false" title ="<?php sprintf( __( "View all posts in %s" ), $category->name ); ?>" >

Date-based archives for custom post_types (wordpress 3.0)

If I have a custom post type named 'my_type', how can I get Wordpress to make date-based archives, for example:
mysite.com/my_type/2010/
mysite.com/my_type/2010/07/
mysite.com/my_type/2010/07/28/
I'm looking for tips both on creating the rewrite rules and on linking the urls to templates.
Thanks!
Update:
I've tried the following in my functions.php (EVENT_TYPE_SLUG is a constant defined elsewhere):
function event_rewrite_rules() {
global $wp_rewrite;
$new_rules = array(
EVENT_TYPE_SLUG."/([0-9]{4})/([0-9]{1,2})/$" => 'index.php?post_type=event&year='.$wp_rewrite->preg_index(1).'&monthnum='.$wp_rewrite->preg_index(2),
EVENT_TYPE_SLUG."/([0-9]{4})/$" => 'index.php?post_type=event&year='.$wp_rewrite->preg_index(1),
);
$wp_rewrite->rules = $new_rules + $wp_rewrite->rules;
}
add_action('generate_rewrite_rules', 'event_rewrite_rules');
the rewrite rules show up in $wp_rewrite-> rules, but when I navigate to those pages I get a 404 error. If I manually navigate to mysite.com/index.php?post_type=event&year=2010, I get redirected to mysite.com/2010?post_type=event
Benj I think WordPress automatically creates archives for custom post type
OK ... took some time but I figured this out (there may be more elegant solutions out there).
1) Create a standard wordpress page to serve as the archive page (and to give me access to the template). I defined the page's slug as a constant (EVENT_ARCHIVE_BASE) so that it's just hard-coded in one place and referenced everywhere else.
2) Make custom rewrite rules that catch that page's urls and redirect them to the page:
function event_date_queryvars($qvars) {
array_push($qvars, 'eyear', 'emonth');
return $qvars;
}
add_filter('query_vars', 'event_date_queryvars');
function event_date_rewrite_rules() {
// Adds rewrite rules for date-based event archives
global $wp_rewrite;
$new_rules = array(
EVENT_ARCHIVE_BASE."/([0-9]{4})/([0-9]{1,2})/?$" =>
"index.php?pagename=".EVENT_ARCHIVE_BASE."&eyear=".$wp_rewrite->preg_index(1)."&emonth=".$wp_rewrite->preg_index(2),
EVENT_ARCHIVE_BASE."/([0-9]{4})/?$" => "index.php?pagename=".EVENT_ARCHIVE_BASE."&eyear=".$wp_rewrite->preg_index(1),
);
$wp_rewrite->rules = $new_rules + $wp_rewrite->rules;
}
add_action('generate_rewrite_rules', 'event_date_rewrite_rules');
3) At the top of my page template, I check for the custom query vars and build the page accordingly. (I should have been able to use the built-in year and monthnum, but couldn't get them to work properly. Future troubleshooting):
// Was the page called with a specific year, month, or just plain?
$year = get_query_var('eyear');
$monthnum = sprintf('%02d', get_query_var('emonth'));
if ($monthnum) {
$list_type = 'Month';
$monthname = $month[$monthnum];
$heading = "Events from $monthname, $year";
} elseif ($year) {
$list_type = 'Year';
$heading = "Events from $year";
} else {
$list_type = 'AllPast';
$heading = get_the_title();
}
Thanks for the help, and hope this is useful for someone else! Let me know if you have a simpler/built-in way to do this.
I managed to find a more elegant/built-in solution.
The trick is in the "post_type=article" parameter.
If you create a rewrite like so:
'(articles)/([0-9]{4})' => 'index.php?post_type=article&eyear=' . $wp_rewrite->preg_index(2)
This will then keep your URL structure, but go through the default 'archive' template.
Then you steal the template away from the default to your own archive template. (for instance 'archive-articles.php')
This function accomplishes this (works for me):
add_action("template_redirect", 'my_template_redirect');
// Template selection
function my_template_redirect()
{
global $wp;
global $wp_query;
if ($wp->query_vars["post_type"] == "article")
{
if (have_posts())
{
include(TEMPLATEPATH . '/archive-articles.php');
die();
}
else
{
$wp_query->is_404 = true;
}
}
}
You'll still need to create your own methods to handle the get_query_var()'s for the year/month/day, but at least you don't need to create a custom page template.

Resources