I am working on a WordPress plugin which adds an auth code to the login form.
This is the process of checking if the auth code is valid:
add_action( 'wp_authenticate', 'authcode_check', 5 );
function authcode_check($username) {
$options = get_option( 'authcode_settings' );
if(!empty($options['code'])) {
global $wpdb;
if ( !username_exists( $username ) ) {
return;
}
$set_code = $options['code'];
$submit_code = $_POST['auth_key'];
if(empty($submit_code)) {
add_filter( 'login_errors', function( $error ) {$error = '<strong>ERROR</strong>: Authentication code cannot be empty.';return $error;} );
return;
} elseif ( ! ( $set_code == $submit_code ) ) {
add_filter( 'login_errors', function( $error ) {$error = '<strong>ERROR</strong>: Authentication code is invalid.';return $error;} );
return;
}
}
}
The problem is; when the user enters their WordPress name and password correctly, but not the auth code, the form still submits and log the user in.
I tried return false but that didn't work.
Is there any way to prevent the form from logging the user in when they have entered a wrong auth code?
Working Example
Updated after chat with #J.Doe
We can hook into the login_form hook, to display the input for the authentication code:
/**
* 'Authentication Code' Input
*/
add_action( 'login_form', function()
{
// Fetch the stored code
$options = get_option( 'authcode_settings' );
// Display code input
if( isset( $options['code'] ) )
printf(
'<p class="login-authenticate">
<label for="auth_key">%s</label>
<input type="text" name="so38551606_auth_key" id="so38551606_auth_key"
class="input" value="" size="20" autocomplete="off" />
</p>',
esc_html__( 'Authentication Code', 'mydomain' )
);
} );
You can hook into the authenticate filter, within the wp_authenticate() function, for the validating part:
/**
* Validate 'Authentication Code' Input
*/
add_filter( 'authenticate', function( $user )
{
// Fetch stored code value
$options = get_option( 'authcode_settings' );
// Nothing to do if there's no stored code value
if( ! isset( $options['code'] ) )
return $user;
// Fetch the user's code input
$submit_code = isset( $_POST['so38551606_auth_key'] )
? $_POST['so38551606_auth_key']
: null;
// Validation's logic
$is_valid_auth_code = ! is_null( $submit_code )
&& ( $options['code'] === $submit_code );
// Valid auth code
if( $is_valid_auth_code )
return $user;
// Add an unvalid auth code error
if( is_wp_error( $user ) )
$user->add(
'invalid_auth_code',
sprintf(
'<strong>%s</strong>: %s',
esc_html__( 'ERROR', 'mydomain' ),
esc_html__( 'Authentication code is invalid.', 'mydomain' )
)
);
// Create a new auth code error
else
$user = new WP_Error(
'invalid_auth_code',
sprintf(
'<strong>%s</strong>: %s',
esc_html__( 'ERROR', 'mydomain' ),
esc_html__( 'Authentication code is invalid.', 'mydomain' )
)
);
return $user;
}, 100 );
Here we use a priority of 100 because we want to run it after the default callbacks of:
add_filter( 'authenticate', 'wp_authenticate_username_password', 20, 3 );
add_filter( 'authenticate', 'wp_authenticate_email_password', 20, 3 );
add_filter( 'authenticate', 'wp_authenticate_spam_check', 99 );
We prefixed the POST variable with so38551606_ to avoid possible name collision.
Output example:
Instead of return false use return as apparently WordPress prefers it that way, look at the existing username code it just returns, not false, not true, just return
Also place the add filter functions before the return statements as a return signifies that the code stops running at that point, nothing below it is executed anymore, so your filters won't appear unless you move them
Can you prove the user isn't being logged in? Or are you just assuming they are being logged in because the error messages aren't being shown?
Anything after the return keyword won't be run. So in your if statements, you have two return false;s if the auth key doesn't match and then you are calling a function afterwards. This function won't be run so the error code won't be displayed.
What you can do however, is move the function above the return keyword. It will then display the error and return false.
Related
i'm using WP User Frontend Pro plugin
i want to echo the pack title using a shortcode to put it in bakery visual.
all what i know is : this is the title $pack->post_title;
$pack is coming from here :
public function current_pack() {
global $pack;
$pack = $this->pack;
if ( ! isset( $this->pack['pack_id'] ) ) {
$pack_page = get_permalink( wpuf_get_option( 'subscription_page', 'wpuf_payment' ) );
return new WP_Error( 'no-pack', sprintf( __( 'You must purchase a subscription package before posting', 'wp-user-frontend'), $pack_page ) );
}
// seems like the user has a pack, now check expiration
if ( $this->expired() ) {
return new WP_Error( 'expired', __( 'The subscription pack has expired. Please buy a pack.', 'wp-user-frontend' ) );
}
return $pack;
}
i try to do something like this :
function wpc_shortcode_pack_title() {
global $pack;
return $pack->post_title;
}
add_shortcode( 'sub_name', 'wpc_shortcode_pack_title' );
to explain more
the slected code in line 5 is working correctly in the plugin pages
but i want it as a shortcode
but it didn't work
any help please ?
The callback function of add_shortcode() should return the content, not print it.
Meaning, you have to return $pack->post_title instead of echo $pack->post_title.
Like so:
function wpc_shortcode_pack_title() {
global $pack;
return $pack->post_title;
}
add_shortcode( 'sub_name', 'wpc_shortcode_pack_title' );
Edit: After taking a look at the source of “WP User Frontend Pro”:
$pack seems to be getting its value from WPUF_Subscription::get_subscription() passing the subscription id, which basically gets the post with that id.
The subscription id seems to be getting its value from WPUF_Subscription::get_user_pack() passing the user id.
So, I guess you could call get_current_user_id() and try something like this:
function wpc_shortcode_pack_title() {
$user_id = get_current_user_id();
if ( ! class_exists( 'WPUF_Subscription' ) ) {
return 'WP User Frontend Pro is not installed/activated';
}
$user_sub = WPUF_Subscription::get_user_pack( $user_id );
$pack = WPUF_Subscription::get_subscription( $user_sub['pack_id'] );
return $pack->post_title;
}
add_shortcode( 'sub_name', 'wpc_shortcode_pack_title' );
Edit #2: To get the expire date as well, you would do something similar:
function wpc_shortcode_pack_title() {
$user_id = get_current_user_id();
if ( ! class_exists( 'WPUF_Subscription' ) ) {
return 'WP User Frontend Pro is not installed/activated';
}
// Get WPUF subscription/pack
$user_sub = WPUF_Subscription::get_user_pack( $user_id );
$pack = WPUF_Subscription::get_subscription( $user_sub['pack_id'] );
// Get expiration date
$expire = ( $user_sub['expire'] == 'unlimited' ) ? ucfirst( 'unlimited' ) : wpuf_date2mysql( $user_sub['expire'] );
return sprintf(
'Subscription name: %1$s | Expire date: %2$s',
$pack->post_title,
wpuf_get_date( $expire )
);
}
add_shortcode( 'sub_name', 'wpc_shortcode_pack_title' );
I have a form setup for users to fill in after they register on my WordPress site. Ive setup the form using Contact Form 7, and have a radio button called radio-766 that has two options: subscriber and customer.
I want the user to pick one of these two options, then when they submit the form, it will change their user role.
Below is what I have so far... I've grabbed snippets from online and tried to create my own, but it isn't working. Any ideas on how I can get this to work?
function tm_cf7_roles_posted_data( $posted_data ) {
// Stop if user is not logged in.
if ( ! is_user_logged_in() )
return;
ob_start();
$role = sanitize_key( $posted_data['radio-766'] );
if ( ! in_array( $role, array( 'subscriber', 'customer' ) ) )
return;
$user = new WP_User( get_current_user_id() );
$index = key( $user->roles );
$user_role = $user->roles[ $index ];
$output = ob_get_contents();
ob_end_clean();
return $output;
}
add_action( 'wpcf7_posted_data', 'tm_cf7_roles_posted_data' );
Should I be including the form name or ID anywhere? Can't find info on this
Any help is so appreciated!
EDIT
I feel like there is nothing connecting this function to the CF7 form named "After LinkedIn", so I found this snippet of code, just not sure how to integrate and get working
if (!isset($cfdata->posted_data) && class_exists('WPCF7_Submission')) {
// Contact Form 7 version 3.9 removed $cfdata->posted_data and now
// we have to retrieve it from an API
$submission = WPCF7_Submission::get_instance();
if ($submission) {
$formdata = $submission->get_posted_data();
}
} elseif (isset($cfdata->posted_data)) {
// For pre-3.9 versions of Contact Form 7
$formdata = $cfdata->posted_data;
} else {
// We can't retrieve the form data
return $cfdata;
}
// Check this is the user registration form
if ( $cfdata->title() == 'After LinkedIn') {
As per the Contact form 7 plugin author is_user_logged_in() will work on below to cases in form submission:
subscribers_only: true set in the additional setting in form. OR
Set WPCF7_VERIFY_NONCE to true using add_filter( 'wpcf7_verify_nonce', '__return_true' );
For more information click here.
Also, to change the user role you can do the following:
add_action( 'wpcf7_posted_data', 'tm_cf7_roles_posted_data' );
function tm_cf7_roles_posted_data( $posted_data ) {
$submission = WPCF7_Submission::get_instance();
$wpcf7 = WPCF7_ContactForm::get_current();
$formID = $wpcf7->id();
if ( $submission ) {
if( $formID == "YOUR-FORM-ID" ) {
if ( is_user_logged_in() ) {
$role = sanitize_key( $posted_data['radio-766'][0] );
if ( ! in_array( $role, array( 'subscriber', 'customer' ) ) )
return;
// Getting the WP_User object
$user = wp_get_current_user();
// The user exists
if( $user && $user->exists() ) {
// Check if user already has that role or not
if ( !in_array( $role, (array) $user->roles ) ) {
// Remove all the previous roles from the user and add this one
// This will also reset all the caps and set them for the new role
$user->set_role( $role );
}
}
}
}
}
}
If you only want to add a new role to the user and retain the existing role then instead of set_role use below:
// Add a new role to the user while retaining the previous ones
$user->add_role( $role );
As for programatically setting a user role you can just take the user object and use the set_role() function to change their role to whatever you want as long as that role has been defined.Try this way
function tm_cf7_roles_posted_data( $posted_data ) {
// Stop if user is not logged in.
if ( ! is_user_logged_in() )
return;
ob_start();
$role = sanitize_key( $_POST['radio-766'] );
if ( ! in_array( $role, array( 'subscriber', 'customer' ) ) )
return;
$user = new WP_User( get_current_user_id() );
$index = key( $user->roles );
$user_role = $user->set_role($role);
$output = ob_get_contents();
ob_end_clean();
return $output;
}
add_action( 'wpcf7_posted_data', 'tm_cf7_roles_posted_data' );
Need a bulk status update to Orders to change from On Hold to Completed but without sending confirmation emails. However, still need to retain the email functionality. This would be a new custom bulk action in addition to the standard WooCommerce bulk action to update to Completed (which still would send the confirmation emails). I have the extra option added with no problem but can't find a method that will preclude the email notification or a way to temporarily disable email notifications (which doesn't sound like a good approach anyway).
So far code is as below. Everything is fine except the $order->update_status('completed') triggers the confirmation email.
Have tried using set_status() but that produces the same result (update_status calls set_status).
/*
* Custom bulk action in dropdown - Change status to completed without sending Confirmation Email
*/
add_filter( 'bulk_actions-edit-shop_order', 'register_bulk_action' ); // edit-shop_order is the screen ID of the orders page
function register_bulk_action( $bulk_actions ) {
$bulk_actions['complete_with_no_email'] = 'Change status to completed (no confirmation emails)';
return $bulk_actions;
}
/*
* Bulk action handler
*/
add_action( 'admin_action_complete_with_no_email', 'bulk_process_custom_status' ); // admin_action_{action name}
function bulk_process_custom_status() {
// if an array with order IDs is not presented, exit the function
if( !isset( $_REQUEST['post'] ) && !is_array( $_REQUEST['post'] ) )
return;
// New order emails
foreach( $_REQUEST['post'] as $order_id ) {
$order = new WC_Order( $order_id );
$order_note = 'Changed Status to Completed via bulk edit (no confirmation email)';
$order->update_status('completed', $order_note); //STILL SENDS EMAIL
}
// of course using add_query_arg() is not required, you can build your URL inline
$location = add_query_arg( array(
'post_type' => 'shop_order',
'changed' => count( $_REQUEST['post'] ), // number of changed orders
'ids' => join( $_REQUEST['post'], ',' ),
'marked_fulfilled_no_emails' => 1,
'post_status' => 'all'
), 'edit.php' );
wp_redirect( admin_url( $location ) );
exit;
}
/*
* Notices for Bulk Action
*/
add_action('admin_notices', 'custom_order_status_notices');
function custom_order_status_notices() {
global $pagenow, $typenow;
if( $typenow == 'shop_order'
&& $pagenow == 'edit.php'
&& isset( $_REQUEST['marked_fulfilled_no_emails'] )
&& $_REQUEST['marked_fulfilled_no_emails'] == 1
&& isset( $_REQUEST['changed'] ) ) {
$message = sprintf( _n( 'Order status changed.', '%s order statuses changed.', $_REQUEST['changed'] ), number_format_i18n( $_REQUEST['changed'] ) );
echo "<div class=\"updated\"><p>{$message}</p></div>";
}
}
Wanting to avoid triggering confirmation emails when using a custom bulk edit option from orders.
The best way I found to solve this was to add a flag using update_post_meta when looping through the selected orders flagging them to bypass the email confirmation.This, together with a function that hooks into woocommerce_email_recipient_customer_completed_order to return nothing for those that have been flagged and at the same time removing the flag so that all other functionality still triggers the email confirmation as normal afterwards. The relevant functions here:
Add the hook to add a new option for bulk editing:
/*
* Custom bulk action in dropdown - Change status to completed without sending Confirmation Email
*/
add_filter( 'bulk_actions-edit-shop_order', 'register_bulk_action' ); // edit-shop_order is the screen ID of the orders page
function register_bulk_action( $bulk_actions ) {
$bulk_actions['complete_with_no_email'] = 'Change status to completed (no confirmation emails)';
return $bulk_actions;
}
Handle this new option here, making sure to flag each of the selected posts with update_user_meta so we can avoid emailing them
/*
* Bulk action handler
* admin_action_{action name}
*/
add_action( 'admin_action_complete_with_no_email', 'bulk_process_complete_no_email' );
function bulk_process_complete_no_email() {
// if an array with order IDs is not presented, exit the function
if( !isset( $_REQUEST['post'] ) && !is_array( $_REQUEST['post'] ) )
return;
// Loop through selected posts
foreach( $_REQUEST['post'] as $order_id ) {
// Use this flag later to avoid sending emails via hook woocommerce_email_recipient_customer_completed_order
update_post_meta($order_id, 'bypass_email_confirmation', true);
$order = new WC_Order( $order_id );
$order_note = 'Changed Status to Completed via bulk edit (no confirmation email)';
$order->update_status('completed', $order_note);
}
// of course using add_query_arg() is not required, you can build your URL inline
$location = add_query_arg( array(
'post_type' => 'shop_order',
'changed' => count( $_REQUEST['post'] ), // number of changed orders
'ids' => join( $_REQUEST['post'], ',' ),
'marked_fulfilled_no_emails' => 1,
'post_status' => 'all'
), 'edit.php' );
wp_redirect( admin_url( $location ) );
exit;
}
This just adds a notice after the bulk action is completed
/*
* Notices for the Bulk Action
* This just adds the notice after action has completed
*/
add_action('admin_notices', 'custom_order_status_notices');
function custom_order_status_notices() {
global $pagenow, $typenow;
if( $typenow == 'shop_order'
&& $pagenow == 'edit.php'
&& isset( $_REQUEST['marked_fulfilled_no_emails'] )
&& $_REQUEST['marked_fulfilled_no_emails'] == 1
&& isset( $_REQUEST['changed'] ) ) {
$message = sprintf( _n( 'Order status changed.', '%s order statuses changed.', $_REQUEST['changed'] ), number_format_i18n( $_REQUEST['changed'] ) );
echo "<div class=\"updated\"><p>{$message}</p></div>";
}
}
Finally, hook into every time the site is going to send a confirmation email and prevent it from doing so when the flag is present. Importantly, remove the flag here as well so only our bulk action above will prevent the confirmation email.
/*
* Hook into every time the site is going to email customer and stop it if the flag is present
*/
add_filter('woocommerce_email_recipient_customer_completed_order','handle_email_recipient_completed_order', 10, 2);
function handle_email_recipient_completed_order($recipient, $order) {
//if notifications are disabled (e.g. by bulk_process_complete_no_email)
$notifications_disabled = get_post_meta($order->get_id(), 'bypass_email_confirmation', true);
if ($notifications_disabled) {
update_post_meta($order->get_id(), 'bypass_email_confirmation', false);
return '';
} else {
return $recipient;
}
}
Hopefully this helps someone, I spent quite a while trying to find an answer to this particular question and couldn't find it elsewhere.
When registering user, I am hooking into user_register hook to send some call to the external API.
add_action( 'user_register', 'create_user_on_api' );
inside the create_user_on_api function I am making the call (nonces are there, and tons of security checks, but I'm omitting those for brevity) using wp_remote_post()
function create_user_on_api( $user_id ) {
$user_create_response = wp_remote_post( "someurlgoeshere.api", $curl_args );
if ( is_wp_error( $user_create_response ) ) {
throw new Exception( 'wp error' );
} else {
$code = wp_remote_retrieve_response_code( $user_create_response );
$msg = wp_remote_retrieve_response_message( $user_create_response );
$body = wp_remote_retrieve_body( $user_create_response );
if ( $code !== 200 ) {
throw new Exceprion( 'error' );
}
}
}
This is the gist of it.
The problem is following: using any error handles causes white screen of death, error is logged in my debug.log, and I would like to throw a notice on my admin dashboard that, even though api call failed, the user is created in WP, and you'll have to create it manually on the API or try again.
I tried echoing stuff, custom exception handling mentioned here, but so far no luck.
Any idea how to do that? Can I hook into admin notices in any way?
Ok, found a solution. A hacky one but it works.
On api error I am storing a error message in a transient, which has an expiry of 60 seconds and I created a user_notices function that I hook on admin_notices hook. In it I check two things: if I'm on users screen
$current_screen = get_current_screen();
if ( $current_screen->id === 'users' ) {
}
And then inside this I check if the transients are empty, and then assign them to the $error_message array, which I output in a foreach (in case I have multiple error messages)
if ( ! empty( $error_msg_array ) ) {
foreach ( $error_msg_array as $error_msg_key => $error_msg ) {
?>
<div class="error notice">
<p><?php printf( '<strong>Apigee error:</strong> %s', esc_html( $error_msg ) ); ?></p>
</div>
<?php
}
}
And this seems to be working.
We have a complicated weird system where we need to check if the wordpress administrator has logged in and by logged in i dont just mean the session but initial second when the query is sent to validate the credentials entered. I was thinking doing traditional php where I do something like the following:
if(isset($_POST['submit']) { // do something }
But that would be the incorrect way. Where exactly would i look to see this peice , i believe its wp-login.php but wasnt able to find what i was looking for.
thanks
try
global $current_user;
$user = get_userdata($current_user->ID);
$userRole = (!empty($user->roles) ? $user->roles : '');
if(in_array('administrator', $userRole)) {
// do your stuff
}
or you can try :- http://codex.wordpress.org/Function_Reference/current_user_can
You can use very neat Wordpress function to determine the user capabilities, it is called current_user_can():
<?php
if ( current_user_can('update_core') ) {
echo 'The current user is Administrator or Super administrator (in Multisite)';
}
Only Administrators of single site installations have the below list of capabilities. In Multi-site, only the Super Admin has these abilities:
update_core
update_plugins
update_themes
install_plugins
install_themes
delete_themes
edit_plugins
edit_themes
edit_users
create_users
delete_users
unfiltered_html
See here for more information.
We can add some custom action inside the filter hook login_redirect. If the form was not submitted, the $user param is a WP_Error Object.
add_filter( 'login_redirect', function( $redirect_to, $requested_redirect_to, $user )
{
if( !is_wp_error( $user ) && in_array( 'administrator', $user->roles ) )
{
# Do your thing; here just inspecting the WP_User Object
wp_die(
sprintf( '<pre>%s</pre>', print_r( $user, true ) ),
'Var dump',
array(
'response' => 500,
'back_link' => true
)
);
}
return $redirect_to;
}, 10, 3 );