I am trying to add a custom URL structure to a WordPress based website.
for example:
example.com/machines //list all machines in a table
example.com/machines?some=params //list filtered machines in a table
example.com/machines/1 //show single machine
The data will come from an external api i have already developed, via curl.
I cannot import the data into a custom post type as it is normalized over many tables, the business logic is complicated and the api is used by other devices anyway.
I have looked at the docs for add_rewrite_rule, but the second parameter has me stumped:
$redirect
(string) (required) The URL you would like to actually fetch
Well I don't have a url to fetch, I want to run a function, that will act as a simple router - take the url parts, call the external api and return a template with the correct data.
Calling the API will be simple, but how i actually route the url to the function, and how I then load a template (utilizing existing WordPress header.php and footer.php) has me stumped.
After much googling and reading a few good resources, I have found the solution.
Step 1: Use add_rewrite_endpoint to create a base url that will be mapped to a query variable:
add_action( 'init', function(){
add_rewrite_endpoint( 'machines', EP_ROOT );
} );
Step 2: Visit the permalinks settings page and click "Save Changes" to flush the rewrite rules.
Step 3: Hook into the action 'template_redirect' to actually do something when the url is hit:
add_action( 'template_redirect', function() {
if ( $machinesUrl = get_query_var( 'machines' ) ) {
// var_dump($machinesUrl, $_GET);
// $machinesURl contains the url part after example.com/machines
// e.g. if url is example.com/machines/some/thing/else
// then $machinesUrl == 'some/thing/else'
// and params can be retrieved via $_GET
// after parsing url and calling api, it's just a matter of loading a template:
locate_template( 'singe-machine.php', TRUE, TRUE );
// then stop processing
die();
}
});
Step 4: The only other thing to do is handle a hit to a url with no further parts to it e.g. example.com/machines.
It turns out that at some point within WordPress's guts, the empty string gets evaluated to false and thus skipped, so the final step is to hook into the filter 'request' and set a default value:
add_filter( 'request', function( $vars = [] ) {
if ( isset( $vars['machines'] ) && empty( $vars['machines'] ) ) {
$vars['machines'] = 'default';
}
return $vars;
});
This can easily be improved by wrapping it all in a class(es).
The url parsing and template loading logic can be passed to a basic router, even a rudimentary MVC setup, loading routes from a file etc, but the above is the starting point.
A simplier solution is to just create a new template redirect.
So assuming you loading example.com/custom-url
/**
* Process the requests that comes to custom url.
*/
function process_request() {
// Check if we're on the correct url
global $wp;
$current_slug = add_query_arg( array(), $wp->request );
if($current_slug !== 'custom-url') {
return false;
}
// Check if it's a valid request.
$nonce = filter_input(INPUT_GET, '_wpnonce', FILTER_SANITIZE_STRING);
if ( ! wp_verify_nonce( $nonce, 'NONCE_KEY')) {
die( __( 'Security check', 'textdomain' ) );
}
// Do your stuff here
//
die('Process completed' );
}
add_action( 'template_redirect', 'process_request', 0);
Related
I need some help. I am trying to prevent direct access to a page that my customers get redirected to after checkout. I want the page to be accessible only after checkout.
I have found this topic: https://wordpress.stackexchange.com/questions/290234/prevent-block-direct-access-to-a-thank-you-page
I placed the following code snippet to my functions.php:
add_action('template_redirect', function() {
// ID of the redirect page
if (!is_page(2072)) {
return;
}
// URL of checkout page
if (wp_get_referer() === 'https://www.exampledomain.com/checkout/') {
return;
}
// we are on thank you page
// visitor is not coming from form
// so redirect to home
wp_redirect(get_home_url());
exit;
} );
This works fine if the customer pays through Stripe. However, it does not work if the customer chooses to pay through PayPal because PayPal redirects the customer to their website to make the payment.
Can something be done here to fix this issue?
You could do it the other way around. Only accept an array of predefined urls. For example here we have defined an array with github and stackoverflow as referees. If the referring url isn't one of those two, then we kill the process. With wp_die() we can display a custom message and a backlink, and with header() we can redirect automatically after 3 seconds.
add_action( 'wp', function() {
if( is_page( '3975' ) && ! is_admin() ) { // restrict page ID '2072' if it's on the front end and if user doesn't have the permissions
$base = [ // allowed referees
'https://github.com/', // referee 1
'https://stackoverflow.com/', // referee 1 ... and so on
];
if( ! in_array( $_SERVER['HTTP_REFERER'], $base ) ) { // if not in referees
header( 'Refresh: 3; ' . esc_url( home_url() ) ); // redirect in 3 seconds
wp_die( 'Something went wrong.', NULL, $args = array( 'back_link' => true, ) ); // kills WordPress execution and displays an HTML page with an error message
exit; // terminate the current script
};
};
} );
I've had a look at a bunch of things. wp_get_referer() can't be used that way as it's specific to Wordpress
Retrieve referer from _wp_http_referer or HTTP referer. If it’s the same as the current request URL, will return false.
I'm using $_SERVER['HTTP_REFERER'] instead # https://stackoverflow.com/a/16374737/3645650. Which does the job I thaught wp_get_referer() would do.
I have some custom code and I am not sure how to catch that Post request.
<?php $nonce = wp_create_nonce('nrc_update_certifications_nonce');?>
<?php $link = admin_url('admin-ajax.php?action=nrc_update_certifications&nonce=' . $nonce); ?>
<file-upload
extensions="jpg,jpeg,png,pdf"
:accept="accept"
:multiple="true"
post-action="<?php echo $link;?>"
:data="{
types: accept,
certifications_ids: certifications_ids,
}"
v-model="certifications"
name="certifications[]"
#input-filter="inputFilter"
ref="upload">
<span class="button">Select files</span>
</file-upload>
In my child theme I have a file update-certifications.php. This file is imported in functions.php
function nrc_update_certifications() {
// I don't get here!
if ( !wp_verify_nonce( $_REQUEST['nonce'], "nrc_update_certifications_nonce")) {
exit("No naughty business please");
}
exit ("Works!");
}
add_action('wp_ajax_update_certifications', 'nrc_update_certifications');
The correct way to do that is the following:
function nrc_update_certifications() {
// I don't get here!
if ( !wp_verify_nonce( $_REQUEST['nonce'], "nrc_update_certifications_nonce")) {
exit("No naughty business please");
}
exit ("Works!");
}
add_action('wp_ajax_nrc_update_certifications', 'nrc_update_certifications');
What changes is that when you do add_action the action starts with "wp_ajax_" and it MUST be followed by what you specify in the action parameter of your call.
Since you build the link like this:
<?php $link = admin_url('admin-ajax.php?action=nrc_update_certifications&nonce=' . $nonce); ?>
Then the missing part after wp_ajax_ must be "nrc_update_certifications". What you are free to change is the function specified as the second parameter of add_action
Sidenote: if you want that ajax to be available for non-logged user aswell, then you are missing another call to add action which should be:
add_action( 'wp_ajax_nopriv_nrc_update_certifications', 'nrc_update_certifications' );
See the "nopriv" part of the action.
If you want to know more i suggest you to take a look at this: https://codex.wordpress.org/AJAX_in_Plugins
In Wordpress function.php, I have following code to get the short URL of the current post:
add_action('wpcf7_before_send_mail', 'save_cf7_data');
function save_cf7_data($cf)
{
$post_url = 'https://' . $_SERVER[ 'SERVER_NAME' ] . $_SERVER[ 'REQUEST_URI' ];
$post_id = url_to_postid( $post_url );
$post_shorturl = wp_get_shortlink($post_id);
}
It used to work perfectly without any problem for months. However, there must be some changes in contact form 7 plugin or Wordpress, $post_url starts to return url like this:
https://example.com/wp-json/contact-form-7/v1/contact-forms/108/feedback
How to retrieve the short URL of the current post correctly in this case?
Try this code:
function save_cf7_data($cf) {
$submission = WPCF7_Submission::get_instance();
$cf_url = $submission->get_meta( 'url' );
$cf_postid = url_to_postid( $cf_url );
$cf_shorturl = wp_get_shortlink($cf_postid);
}
add_action('wpcf7_before_send_mail', 'save_cf7_data');
See the file mail.php in source code of CF7.
Have a look at the Special Mail Tags, specifically [_post_url] as well.
Then try like this,
add_action('wpcf7_before_send_mail', 'save_cf7_data');
function save_cf7_data($cf)
{
global $wp;
$post_url = home_url(add_query_arg(array(),$wp->request));
$post_id = url_to_postid( $post_url );
$post_shorturl = wp_get_shortlink($post_id);
}
I would use get_permalink() for retrieving post url in a page template. functions.php may load before global $post is set, not too sure.
EDIT:
Oh if you look at the $post_url you got, it's actually a WordPress Rest API url. So I think the form plugin used to submit to the original page but now chooses to use WordPress Rest API.
But your solution is not the correct approach in the first place. Since this is a form submission, you should do a dump of the $_REQUEST. I bet there's form's post url info in there. If not submitted by default, you can probably add a post url (most likely hidden) field in the plugin form.
I have installed SMS Validator so everyone who register to my site must enter there phone number to sign-up
Now I have created a new function (link) to add a different type user role if users sign-up by visit this link:
http://example.com/wp-login.php?action=register&role=vip_member
I want to turn OFF SMS Validator ONLY for this URL link.
Is it possible to do it somehow?
Success so far using mathielo code:
// Activate SMSGlobal
function activate_plugin_conditional() {
if ( !is_plugin_active('sms-validator-SMSG/sms-validator.php') ) {
activate_plugins('sms-validator-SMSG/sms-validator.php');
}
}
// Deactivate SMSGlobal
function deactivate_plugin_conditional() {
if ( is_plugin_active('sms-validator-SMSG/sms-validator.php') ) {
deactivate_plugins('sms-validator-SMSG/sms-validator.php');
}
}
// Now you in fact deactivate the plugin if the current URL matches your desired URL
if(strpos($_SERVER["REQUEST_URI"], '/wp-login.php?action=register&role=vip_member') !== FALSE){
// Calls the disable function at WP's init
add_action( 'init', 'deactivate_plugin_conditional' );
}
if(strpos($_SERVER["REQUEST_URI"], '/wp-login.php?action=register&role=seller') !== FALSE){
// Calls the enable function at WP's init
add_action( 'init', 'activate_plugin_conditional' );
}
if(strpos($_SERVER["REQUEST_URI"], '/wp-login.php?action=register&role=provider') !== FALSE){
// Calls the enable function at WP's init
add_action( 'init', 'activate_plugin_conditional' );
}
So far this code helps me to activate and deactivate this plugin in this 3 selected URLs.
But I want this plugin to be activated if is deactivated in this links: /wp-login.php and /wp-login.php?action=register
But if I set it to activate on URL: /wp-login.php then it will not be deactivated on URL: /wp-login.php?action=register&role=vip_member where I need it to be deactivated.
You could match the current URL using $_SERVER["REQUEST_URI"] and then disable the plugin in functions.php:
// Just creating the function that will deactivate the plugin
function deactivate_plugin_conditional() {
if ( is_plugin_active('plugin-folder/plugin-name.php') ) {
deactivate_plugins('plugin-folder/plugin-name.php');
}
}
// Now you in fact deactivate the plugin if the current URL matches your desired URL
if(strpos($_SERVER["REQUEST_URI"], 'my-disable-url') !== FALSE){
// Calls the disable function at WP's init
add_action( 'init', 'deactivate_plugin_conditional' );
}
Note: Be aware that php's strpos() may return 0 if the needle matches the given string at position zero, so the condiional !== is needed.
Got my references from here and implemented the URL check. Check out the current value of $_SERVER["REQUEST_URI"] at your desired URL for a perfect match.
Answering the second part of your question:
You could improve your code removing the last 2 ifs and complementing the first one with an else, like so:
// Now you in fact deactivate the plugin if the current URL matches your desired URL
if(strpos($_SERVER["REQUEST_URI"], '/wp-login.php?action=register&role=vip_member') !== FALSE){
// Calls the disable function at WP's init
add_action( 'init', 'deactivate_plugin_conditional' );
}
// Otherwise, for any other URLs the plugin is activated if it's currently disabled.
else{
add_action( 'init', 'activate_plugin_conditional' );
}
Now for every URL that is not the one you wish to deactivate the plugin, your code will enable the plugin if it is currently inactive.
I've added a custom rewrite rule to my Wordpress site like so:
add_rewrite_rule( 'register/bronze', 'index.php?action=register&type=2', 'top' );
Then within the template_redirect action, I check against the action querystring variable and load my include like so:
$action = get_query_var( 'action' );
if( $action == "register" ) {
include( BG_FILE_PATH . '/templates/register-brand.php' );
exit;
}
This all works fine and my custom template is displayed, however, the page title appears as "Page not found | Site Name".
Is there a way I can set the page title from my custom template? I'm trying to avoid setting these pages up as a Wordpress Page since they're fundamental to the running of the site, I don't want one of the admins to change the page settings or delete the page entirely.
Any help is much appreciated.
WordPress is likely overwriting your title because its still throwing a 404 (you can verify this using firebug).
WordPress will typically throw 404s when including files in the template redirect.
You need to reset the header using code like this :
global $wp_query;
$wp_query->is_404 = false;
status_header( '200' ); //if status_header doesn't work, try header("HTTP/1.1 200 OK")
//now that you reset the 404, you can proceed with your include
$action = get_query_var( 'action' );
if( $action == "register" ) {
include( BG_FILE_PATH . '/templates/register-brand.php' );
exit;
}
This should reset the status header and allow the title of your included file to appear normally. No need for javascript :)
You can do this by inserting a small piece of JavaScript:
<script>
$(document).ready(function() {
document.title = 'your title here';
});
</script>
If you want the title to be dynamic then replace with:
document.title = <?php echo 'your dynamic title here'; ?>;