Passing parameter to Wordpress do_action function through $_POST variable value - ajax

I've got an action handler in my plugin which catches a form being posted to admin-ajax.php. The action namespace is 'handle_custom_form_submission'.
I can easily pass arguments to my action handlers in php via the do_action( 'namespace', 'argument' ) function; However, if I'm catching an action via AJAX, all I can do, to my knowledge, is have a $_POST var whose name="action" and value="namespace". Is there any way to pass a parameter into my action call this way? Like
<input name="action" value="handle_custom_form_submission( 'a' )"/>
Here's the situation in more detail:
I'm trying to write a custom form plugin so that I can easily handle multiple unique forms on a Wordpress site.
On my latest project, I've got a contact form on one page, and a testimonial submission form on another. In my code, I'm just designating them as 'custom-form-a' and 'custom-form-b'.
I've got 3 main action hooks:
include_custom_form_js( $form_id )
include_custom_form_html( $form_id )
handle_custom_form_submission()
I'd like to be able to pass #3, handle_custom_form_submission(), a $form_id as well. The only thing I can think of right now is to declare two seperate functions, handle_custom_form_a_submission() and handle_custom_form_b_submission(). Is there a cleaner way?
My main plugin file:
<?php
/*
Plugin Name: Custom Form
Plugin URI:
Description: Add custom form HTML and handle its submission on the backend.
Version: 0.1
Author: James Heston
*/
require_once( dirname( __FILE__ ) . '/php/constants.php' );
class CustomForm{
// paths
private $plugin_dir = CUSTOMFORM_DIRNAME;
private $plugin_path = CUSTOMFORM_URLPATH;
// action names
private $action_include_js = 'include_custom_form_js';
private $action_include_html = 'include_custom_form_html';
private $action_handle_submission = 'handle_custom_form_submission';
// other vars
private $recipient_email = ''; // probably should pull from some WP option
public function __construct(){
$this->add_hooks();
}
private function add_hooks(){
// include necessary js with appropriate MyAjax properties on page where `do_action( 'include_custom_form_js', $form_id )` action is called
add_action( $this->action_include_js, array( $this, $this->action_include_js ), 10, 1 );
// include form html on page where `do_action( 'include_custom_form_html', $form_id )` action is called
add_action( $this->action_include_html, array( $this, $this->action_include_html ), 10, 1 );
// handle contact form submission
add_action( 'wp_ajax_' . $this->action_handle_submission, array( $this, $this->action_handle_submission ) );
add_action( 'wp_ajax_nopriv_' . $this->action_handle_submission, array( $this, $this->action_handle_submission ) );
}
function include_custom_form_js( $form_id ){
wp_enqueue_script(
$this->action_include_js,
$this->plugin_path . '/js/custom-form-' . $form_id . '.js',
array(),
false,
true
);
$localize_array = array(
'ajaxurl' => admin_url( 'admin-ajax.php' ),
'namespace' => $this->action_handle_submission
);
wp_localize_script( $this->action_include_js, 'MyAjax', $localize_array );
}
function include_custom_form_html( $form_id ){
include( $this->plugin_dir . '/html/custom-form-' . $form_id . '.html' );
}
function handle_custom_form_submission(){
$form_id = 'a';
include( $this->plugin_dir . '/php/handle-custom-form-' . $form_id . 'submission.php' );
include( $this->plugin_dir . '/php/handle-custom-form-' . $form_id . 'submission.php' );
}
function send_email(){
//
}
static function instance(){
global $CustomForm;
if(! isset($CustomForm) ){
$CustomForm = new CustomForm();
}
}
}// class CustomForm
if(! isset($CustomForm) ){
CustomForm::instance();
}
?>

Related

Why loadmore button is dissapearing aftre click instead of showing posts?

Here I've found a tutorial how to make the load more button with ajax for woocommerce. I copy almost all, except of styles.
Step 1. Load more button
global $wp_query; // you can remove this line if everything works for you
// don't display the button if there are not enough posts
if ( $wp_query->max_num_pages > 1 )
echo '<div class="misha_loadmore">More posts</div>'; // you can use <a> as well
My variant with a hook looks like that
add_action('woocommerce_after_shop_loop','cosmetic_load_more',9);
function cosmetic_load_more(){?>
<div class="d-flex justify-content-center loadmore-wrapper">
<?php
global $wp_query; // you can remove this line if everything works for you
// don't display the button if there are not enough posts
if ( $wp_query->max_num_pages > 1 )
echo '<button class="loadmore misha_loadmore">See more</button>'; // you can use <a> as well
?>
</div>
<?php }
Step 2. Enqueue jQuery and myloadmore.js. Pass query parameters to the script
function misha_my_load_more_scripts() {
global $wp_query;
// In most cases it is already included on the page and this line can be removed
wp_enqueue_script('jquery');
// register our main script but do not enqueue it yet
wp_register_script( 'my_loadmore', get_stylesheet_directory_uri() . '/myloadmore.js', array('jquery') );
// now the most interesting part
// we have to pass parameters to myloadmore.js script but we can get the parameters values only in PHP
// you can define variables directly in your HTML but I decided that the most proper way is wp_localize_script()
wp_localize_script( 'my_loadmore', 'misha_loadmore_params', array(
'ajaxurl' => site_url() . '/wp-admin/admin-ajax.php', // WordPress AJAX
'posts' => json_encode( $wp_query->query_vars ), // everything about your loop is here
'current_page' => get_query_var( 'paged' ) ? get_query_var('paged') : 1,
'max_page' => $wp_query->max_num_pages
) );
wp_enqueue_script( 'my_loadmore' );
}
add_action( 'wp_enqueue_scripts', 'misha_my_load_more_scripts' );
Step 3. myloadmore.js – what is inside?
jQuery(function($){ // use jQuery code inside this to avoid "$ is not defined" error
$('.misha_loadmore').click(function(){
var button = $(this),
data = {
'action': 'loadmore',
'query': misha_loadmore_params.posts, // that's how we get params from wp_localize_script() function
'page' : misha_loadmore_params.current_page
};
$.ajax({ // you can also use $.post here
url : misha_loadmore_params.ajaxurl, // AJAX handler
data : data,
type : 'POST',
beforeSend : function ( xhr ) {
button.text('Loading...'); // change the button text, you can also add a preloader image
},
success : function( data ){
if( data ) {
button.text( 'More posts' ).prev().before(data); // insert new posts
misha_loadmore_params.current_page++;
if ( misha_loadmore_params.current_page == misha_loadmore_params.max_page )
button.remove(); // if last page, remove the button
// you can also fire the "post-load" event here if you use a plugin that requires it
// $( document.body ).trigger( 'post-load' );
} else {
button.remove(); // if no data, remove the button as well
}
}
});
});
});
Step 4. wp_ajax_
function misha_loadmore_ajax_handler(){
// prepare our arguments for the query
$args = json_decode( stripslashes( $_POST['query'] ), true );
$args['paged'] = $_POST['page'] + 1; // we need next page to be loaded
$args['post_status'] = 'publish';
// it is always better to use WP_Query but not here
query_posts( $args );
if( have_posts() ) :
// run the loop
while( have_posts() ): the_post();
// look into your theme code how the posts are inserted, but you can use your own HTML of course
// do you remember? - my example is adapted for Twenty Seventeen theme
get_template_part( 'template-parts/post/content', get_post_format() );
// for the test purposes comment the line above and uncomment the below one
// the_title();
endwhile;
endif;
die; // here we exit the script and even no wp_reset_query() required!
}
add_action('wp_ajax_loadmore', 'misha_loadmore_ajax_handler'); // wp_ajax_{action}
add_action('wp_ajax_nopriv_loadmore', 'misha_loadmore_ajax_handler'); // wp_ajax_nopriv_{action}
Does it for for you? Where can I be wrong?

Woocommerce Ajax add to cart programmatically

I have this button:
bla bla
This button is located on my homebage and is generated by a page builder. What I want to accomplish is when somebody click on the button, it will take them on the checkout, and add a product to the cart. I know that this can be accomplished via a URL, but I need to have this button do other things as well(client idea).
So right now I'm stucked here:
JQuery
jQuery(document).ready(function($){
$(".remove-all").click(function(){
$.ajax({
url: "wp-admin/admin-ajax.php",
data: 'myajax'
});
});
});
PHP
add_action('wp_ajax_myajax', 'myajax');
add_action('wp_ajax_nopriv_myajax', 'myajax');
function myajax() {
global $woocommerce;
$product_id = 264;
$woocommerce->cart->add_to_cart($product_id);
die();
}
I'm a javascript noob, so can you please point me to the right direction, or maybe give me a hint on what I'm doing wrong.
Thanks in advance!
As I mentioned in the comments, you can pretty much borrow from core WooCommerce functions.
First, here's the button we'll be trying to ajaxify:
bla bla
Secondly, we'll load our custom script and pass it important variables such as the admin ajax and checkout urls.
add_action( 'wp_enqueue_scripts', 'so_load_script', 20 );
function so_load_script(){
wp_enqueue_script( 'so_test', plugins_url( 'js/test.js', __FILE__ ) );
$i18n = array( 'ajax_url' => admin_url( 'admin-ajax.php' ), 'checkout_url' => get_permalink( wc_get_page_id( 'checkout' ) ) );
wp_localize_script( 'so_test', 'SO_TEST_AJAX', $i18n );
}
Now we will write our ajax callbacks, which is copied almost verbatim from WooCommerce core with only a few small modifications:
add_action('wp_ajax_myajax', 'myajax_callback');
add_action('wp_ajax_nopriv_myajax', 'myajax_callback');
/**
* AJAX add to cart.
*/
function myajax_callback() {
ob_start();
//$product_id = 264;
$product_id = 34;
$quantity = 1;
$passed_validation = apply_filters( 'woocommerce_add_to_cart_validation', true, $product_id, $quantity );
$product_status = get_post_status( $product_id );
if ( $passed_validation && WC()->cart->add_to_cart( $product_id, $quantity ) && 'publish' === $product_status ) {
do_action( 'woocommerce_ajax_added_to_cart', $product_id );
wc_add_to_cart_message( $product_id );
} else {
// If there was an error adding to the cart, redirect to the product page to show any errors
$data = array(
'error' => true,
'product_url' => apply_filters( 'woocommerce_cart_redirect_after_error', get_permalink( $product_id ), $product_id )
);
wp_send_json( $data );
}
die();
}
And finally the contents of test.js:
jQuery(document).ready(function($){
$(".test-button").click(function(e){
e.preventDefault(); // Prevent the click from going to the link
$.ajax({
url: wc_add_to_cart_params.ajax_url,
method: 'post',
data: {
'action': 'myajax'
}
}).done( function (response) {
if( response.error != 'undefined' && response.error ){
//some kind of error processing or just redirect to link
// might be a good idea to link to the single product page in case JS is disabled
return true;
} else {
window.location.href = SO_TEST_AJAX.checkout_url;
}
});
});
});
You likely need to cancel/prevent the default action of clicking the link, and then redirect once the AJAX call finishes (this is just one way to do it):
jQuery(document).ready(function($){
$(".remove-all").click(function(e){
e.preventDefault(); // Prevent the click from going to the link
var $redirect = $(this).attr('href');
$.ajax({
url: "wp-admin/admin-ajax.php",
data: 'myajax',
success: function () {
window.location.href = $redirect;
}
});
});
});
PHP
add_action('wp_ajax_myajax', 'myajax');
add_action('wp_ajax_nopriv_myajax', 'myajax');
function myajax() {
$product_id = 264;
// Avoid using the global $woocommerce object
WC()->cart->add_to_cart($product_id);
die();
}

Drupal 7 - Trying to add form to list view

sorry if this has been asked before, I looked around but haven't found this specific question on StackOverFlow.com.
I have a view called 'view-post-wall' which I'm trying to add the form that submits posts to this view called 'post' via ajax submit, though I haven't begun adding ajax yet.
My module's name is 'friendicate'
I don't understand what I'm missing here, I'm following a tutorial and have been unable to get past this issue for 2 days now.
I don't get any errors either.
Here is the module code in full
function _form_post_ajax_add() {
$form = array();
$form['title'] = array(
'#type' => 'textfield',
'#title' => 'Title of post',
);
$form['body'] = array(
'#type' => 'textarea',
'#title' => 'description',
);
$form['submit'] = array(
'#type' => 'submit',
'#value' => 'Submit post',
);
return $form;
}
function post_ajax_preprocess_page(&$variables) {
//krumo($variables);
$arg = arg();
if($arg[0] == 'view-post-wall') {
$variables['page']['content']['system_main']['main']['#markup'] = drupal_render(drupal_get_form('_form_post_ajax_add'));
}
}
There are multiple ways to accomplish this, and I'll outline those methods below. Also, if nothing works from my suggestions below, it's possible that you have an invalid form function name. Im not sure if that throws off Drupal or not. The correct format for the function name should end in _form and contain the arguments $form and $form_state, like so:
_form_post_ajax_add_form($form, &$form_state) { ... }
Also, if you want to use a hook, Steff mentioned in a comment to your question that you'll need to use your module name in the function name.
friendicate_preprocess_page(&$variables) { ... }
Ok, now for a few ideas how to get the form on the page.
Block
You can create a custom block within your module, and then assign it to a region in admin/structure/blocks
<?php
/**
* Implements hook_block_info().
*/
function friendicate_block_info() {
$blocks = array();
$blocks['post_ajax'] = array(
'info' => t('Translation Set Links'),
'cache' => DRUPAL_NO_CACHE,
);
return $blocks;
}
/**
* Implements hook_block_view().
*/
function friendicate_block_view($delta = '') {
$block = array();
if ($delta == 'post_ajax') {
$form = drupal_get_form('_form_post_ajax_add_form');
$block['content'] = $form;
}
return $block;
}
Clear the cache and your block should appear in admin/structure/blocks
Views attachment before/after
You can add markup before and after a view using the Views hook hook_views_pre_render()
<?php
/**
* Implements hook_view_pre_render().
*/
function frendicate_views_pre_render(&$view) {
if($view->name == 'view_post_wall') { // the machine name of your view
$form = drupal_get_form('_form_post_ajax_add_form');
$view->attachment_before = render($form);
}
}
Or maybe use view post render
function friendicate_views_post_render(&$view, &$output, &$cache) {
//use the machine name of your view
if ($view->name == 'view_post_wall') {
$output .= drupal_render(drupal_get_form('_form_post_ajax_add'));
}
}

why is my ajax function not working as expected?

so my class looks like this:
class Myclass{
private $nonce;
public function __construct(){
if( get_current_screen()->id == 'nav-menus' ){
$this->nonce = 'my-plugin-nonce';
}
add_action( 'wp_ajax_run_action', array( $this, 'run' ) );
wp_localize_script(
'my-script',
'my_script',
array( 'nonce' => wp_create_nonce( $this->nonce ),
'ajaxurl' => admin_url('admin-ajax.php'),
)
);
}
public function run(){
if( ! wp_verify_nonce( $_POST['nonce'], $this->nonce ) )
return false;
wp_send_json_success();
}
}
new Myclass;
And here is the javascript code:
$.ajax({
type: 'POST',
dataType: 'json',
url: my_script.ajaxurl,
data: {
'action': 'run_action',
'nonce' : my_script.nonce,
},
complete: function( object ) {
console.log( object.responseJSON )
}
});
The problem is that when i try to call the run_action action from within my javascript ajax function it does not return true as it should.
Note that i have properly localized my script and any data contained in the object is being rendered correctly.
Why is that happening ?
Localization of the script must be done on the pages you are including the script on (ie. on the nav-menus.php admin page in this case) - you do not include the code that actually enqueues your javascript, but the code you did post suggests to me that you are actually attempting to localize the script in the ajax-call itself - which won't work.
I've rearranged your code below and added comments to explain each change:
class Myclass {
/**
* No reason not to assign this already (and I've renamed it to explicitly,
* let people reading the code know that it is not the nonce itself, but the
* name of the nonce action - fooled me for a minute or two :p
*/
private $nonce_action_name = 'my-plugin-nonce';
/**
* The __construct function is great for hooking everything you need to
* hook, and for setting initial variables. Pretty much anything else
* should NOT be in this function though!
*/
public function __construct(){
// Register your ajax action.
add_action( 'wp_ajax_run_action', array( $this, 'run' ) );
// Hook into the appropriate action for admin scripts
add_action( 'admin_enqueue_scripts', array( $this, 'scripts' ) );
}
public function scripts() {
/**
* I've negated your if-statement here - basically we don't want to do
* anything at all if we are not on the correct page - which is clearer
* this way - also you had it in the __construct function, which will
* actually produce a fatal error, since get_current_screen is not
* usually defined yet at that point(!)
*/
if( get_current_screen()->id !== 'nav-menus' ){
return;
}
//Now enqueue (or register) the script
wp_enqueue_script('my-script', plugins_url('/my-script.js', __FILE__));
//Then localize it
wp_localize_script(
'my-script',
'my_script',
array(
'nonce' => wp_create_nonce( $this->nonce_action_name ),
'ajaxurl' => admin_url('admin-ajax.php'),
)
);
}
/**
* Actual ajax handler
*/
public function run(){
if( ! wp_verify_nonce( $_POST['nonce'], $this->nonce_action_name ) ) {
//This is a guess, but I think you'll want to wp_send_json_error
//here to match up with your success below
wp_send_json_error();
} else {
wp_send_json_success();
}
/**
* Always recommended to explicitly die() after handling ajax - though
* the wp_send_json_* family probably does it for you.
*/
die();
}
}
new Myclass;
A final note is that ajaxurl is actually always defined in the admin, so you don't really need to add that to your localization (though it only hurts by adding a few extra bytes).
Note from the codex:
wp_localize_script() MUST be called after the script has been registered using wp_register_script() or wp_enqueue_script().
So your workflow should be as follows:
Register script
Localize it
Enqueue your localized script.
Tie it to appropriate action: wp_enqueue_scripts or admin_enqueue_scripts
For example:
Add action to your __construct method:
add_action('wp_enqueue_scripts', array($this, 'registerAjaxScript'));
Then create a method that registers and localizes your script:
function registerAjaxScript() {
wp_register_script('my-script',
string $src,
array $deps,
string or bool $ver,
bool $in_footer
);
wp_localize_script('my-script',
'my_script',
array( 'nonce' => wp_create_nonce( $this->nonce ),
'ajaxurl' => admin_url('admin-ajax.php'),
)
);
}

How to make drupal hook_form_alter run after validation hook in a module

I have made a drupal 7 module recently and I want to customise the way drupal show validation errors. I want to place error message in a popup. And I put the popup generation code in form alter hook.
But I cannot make drupal hook_form_alter run after validation hook. I have tried to clear the form cache and tried to use "#after_build" instead of form alter hook, but they are all run before the validation. It seems that the validation is last thing to run in that process. I have put my code below, please help me on this.
Thank you very much in advance.
function quote_line_menu() {
$items ['quote_line'] = array (
'page callback' => 'drupal_get_form',
'page arguments' => array (
'quote_line_form'
),
'access arguments' => array (
'access content'
),
'type' => MENU_CALLBACK
);
$items ['ajax_manually_get_price'] = array(
'page callback' => 'ajax_update_price_callback',
'access arguments' => array (
'access content'
),
'type' => MENU_CALLBACK
);
return $items;
}
function quote_line_form($form, &$form_state) {
// Initialize.
if ($form_state ['rebuild']) {
// Don't hang on to submitted data in form state input.
$form_state ['input'] = array ();
}
if (empty ( $form_state ['storage'] )) {
$form_state ['storage'] = array (
'step' => 'quote_line_form_first'
);
}
// Add general settings
$form['#attributes']['class'][] = 'separate-border';
// No cache
/*
$form['#cache'] = FALSE;
$form['#no_cache'] = TRUE;
$form_state['cache'] = FALSE;*/
$form['#after_build'][] = 'quote_line_form_after_build';
// $form['#validate'][] = 'quote_line_form_validate';
// Return the form for the current step.
$function = $form_state ['storage'] ['step'];
$form = $function ( $form, $form_state );
return $form;
}
function quote_line_form_after_build($form, &$form_state) {
error_log(0);
return $form;
}
function quote_line_form_quote_line_form_alter(&$form, &$form_state, $form_id) {
error_log(1);
quote_line_handle_form_set_error($form, $form_state, $form_id); //generate popup here
// Preset form state
if($form_state['storage']['step'] == 'quote_line_form_bike_info'){
if(isset($form_state['storage']['bike_0_oversea_travel_days']) && !empty($form_state['storage']['bike_0_oversea_travel_days'])){
$form ['default_open_oversea'] = array (
'#markup' => '<script>jQuery("a#over0-yes").click();</script>'
);
}
}
}
function quote_line_form_validate($form, &$form_state) {
error_log(2);
error_log($_POST['form_build_id']);
//cache_clear_all('form', 'cache_form', true);
$values = $form_state ['values'];
if(isset($_POST['back']) && !empty($_POST['back'])){
if(!isset($form_state ['values']['back']) || $form_state ['values']['back'] != $_POST['back']){
$form_state ['values']['back'] = $_POST['back'];
}
if(!isset($form_state ['values']['op']) || $form_state ['values']['op'] != $_POST['back']){
$form_state ['values']['op'] = $_POST['back'];
}
$function = 'quote_line_form_submit';
$function ( $form, $form_state );
return;
}
// Call step validate handler if it exists.
if (function_exists ( $form_state ['storage'] ['step'] . '_validate' )) {
$function = $form_state ['storage'] ['step'] . '_validate';
$function ( $form, $form_state );
}
return;
}
You can stop the error messages being printed to the theme using drupal_get_messages('error'). Then, display custom error messages in anyway you like.
I just found a dirty fix. The validation and form build process has been defined in drupal_process_form. And I just hacked that function to make sure the hook_form_alter is called again after the validation failed.
If anyone has a better solution, please post here. Thanks.

Resources