Categories
Wordpress

How to selectively enable/disable plugins on a per page/post basis using Plugin Organizer

I am in perpetual pursuite of reducing page time. I think it not only helps your SEO ranking but also provides a better user experience. So it is a win-win. That said, when I ran a Speed Test on one of the page, I noticed it was loading some external 3rd party scripts that were slowing down the TTFB and First Paint time.

Upon further digging using webpagetest.org and gtmetrix.com, it was clear that Contact Form 7 (with Google Captcha enabled) was adding a few external scripts that were not needed. For example, I did not need the Contact Form 7 plugin loaded on the Cart and Checkout page.

What You Need

Process

Without further ado, I will present below the steps I followed. But before we do that, let me first acknowledge that these are the steps provided to me by the author of the Plugin Organizer plugin. I do not take credit for these. The plugin author is very helpful and guided me through this process. I hope you will find it useful.

Step 1: Determine what scripts/styles need to be excluded

First, you have to determine what scripts/styles are slowing down your page. For this you can use gtmetrix.com or any other speed test site if your preference. Usually, I use both gtmetrix.com and webpagetest.org together because they complement each other quite well.

Here is an example of the summary. Pay attention to the numbers highlighted. The page size and number of requests is very important for determining whether your un-needed scripts/styles are loading.

Fully Loaded Time, Total Page Size and Requests are important metrics that we will monitor

Now, let us dig a bit deeper. The Waterfall view of gtmetrix.com is where I find the scripts that I need to unload. The screenshot shows below the JS and CSS resources loaded for the page, sorted by Size. I find the Sorting feature useful because I can attain significant optimization by removing the bigger scripts/styles first.

All the CSS and JS resources loaded by the page (sorted by size)

After the speed analysis, I determined that I did not want Contact Form 7 to be loaded on all the pages.

Step 2: Configuration of Plugin Organizer

Brace yourself, this is not as easy as it seems. Plugin Organizer is a very powerful tool. It allows incredibly fine control over how and which plugins can be enabled/disabled on a certain page/post.

  • Install Plugin Organizer
  • Activate Plugin Organizer
  • Setup Plugin Organizer

Here is the screenshot for my settings for Plugin Organizer. Of note is the highlighted post types that I wanted to use Plugin Organizer for. Also, note the global settings. You can read up on each of these settings on the Plugin Organizer documentation site.

For me, the “Fuzzy URL Matching” and “Ignore URL Arguments” parameters need to be ON.

Disable the affected plugin globally. Here is the screenshot for my case – Contact Form 7 has been disabled Globally, i.e. it will not load at all on any pages/posts.

Step 3: Setup Plugin Filters

Here is the crux of it all. The logic, so to speak. In my case, I am enabling the CF 7 plugin on the 2 pages. Note the priority of filters.

Filter 1: CF 7

The 2 URL’s (permalinks) are the ones that this filter affects.

Filter 2: CF 7 JSON

In this case, there is only 1 URL that will be impacted. It is a specially formulated URL to select the URL that CF7 submits the form to. Notice the priority set to 4 in this case. The priority is important for this to work correctly, as it controls the execution order of the filters.

Step 4: Clear your cache(s)

After you have made the above changes, make sure to clear your cache(s) completely. Run the speed test again and verify that the un-needed scripts/styles are not being loaded anymore.

Conclusion

There you have it! With this configuration, I can successfully exclude a plugin from loading on a certain page/post. You may have to tinker with the priorities and Permalinks to get it working for your specific use case. Let me know in the comments if you think this is useful. For me, this was a great tool in easily reducing the page load time across the site in one shot. It is a clean way to exclude plugins from loading on every page.

Categories
Wordpress

[SOLVED] WordPress/WooCommerce Reset Password form not working

I am not sure when this started occurring, but recently a few of my users complained that they are unable to change their passwords. They click on the "Lost Password" link. They receive the email with the link to reset the password.

Upon clicking on the link, they are redirected to "https://…./my-account/lost-password/?show-reset-form=true". However, the Reset Password form does not show up. Instead, they receive the original Lost Password screen prompting them to enter username/email so the Lost Password email can be sent to them. 

So the users are in this circular loop. They are unable to change the password.

Looking at the relevant code (https://github.com/woocommerce/woocommerce/blob/a3ec0bf85aa6a55ed3241fc2a30a397e4b1273dc/includes/shortcodes/class-wc-shortcode-my-account.php#L215), it seems that the "wp-resetpass-" cookie is not being set. 

Ask your hosting provider if somehow they are caching the "my-account" and "lost-password" pages. If they are, that can explain this behaviour. Atleast, that was the case with me. Once those URL's were excluded from cache, the Reset Password" form showed up successfully. 

Have you experienced this issue before? What did you find?

Categories
ecommerce WooCommerce Wordpress

How to move the Archive Text in Genesis

For a while, I have struggled with how to remove or change location of the Archive Intro Text in a Genesis-based theme. Essentially, the idea is to move the Archive Intro Text towards the bottom of the page mostly for SEO purposes.

When I asked the questions on the StudioPress forums, I did not receive a satisfactory answer. So I did what most coders do: hack it till you discover how to do this way. Worry not, because this is not a hack. It is actually the recommended way to make a change like this.

Here is the code

/* Remove the Archive Headline and Text from the default location */
remove_action( 'genesis_archive_title_descriptions', 'genesis_do_archive_headings_open', 5);
remove_action( 'genesis_archive_title_descriptions', 'genesis_do_archive_headings_close', 15);
remove_action( 'genesis_archive_title_descriptions', 'genesis_do_archive_headings_headline', 10);
remove_action( 'genesis_archive_title_descriptions', 'genesis_do_archive_headings_intro_text', 12);
/* Now, add it back at the bottom */
add_action( 'woocommerce_after_shop_loop', 'ar_add_custom_hook_archive_description', 8);
add_action( 'ar_add_wc_archive_descr_text' , 'ar_do_custom_archive_description_text', 7, 3);
function ar_add_custom_hook_archive_description() {
global $wp_query;
if ( ! is_category() && ! is_tag() && ! is_tax() ) {
return;
}
$term = is_tax() ? get_term_by( 'slug', get_query_var( 'term' ), get_query_var( 'taxonomy' ) ) : $wp_query->get_queried_object();
if ( ! $term ) {
return;
}
$heading = get_term_meta( $term->term_id, 'headline', true );
if ( empty( $heading ) && genesis_a11y( 'headings' ) ) {
$heading = $term->name;
}
$intro_text = get_term_meta( $term->term_id, 'intro_text', true );
$intro_text = apply_filters( 'genesis_term_intro_text_output', $intro_text ? $intro_text : '' );
do_action( 'ar_add_wc_archive_descr_text', $heading, $intro_text, 'taxonomy-archive-description' );
}
function ar_do_custom_archive_description_text( $heading = '', $intro_text = '', $context = '' ) {
if ( $heading || $intro_text ) {
genesis_markup( array(
'open' => '<div %s>',
'context' => $context,
) );
}
if ( $context && $intro_text ) {
echo $intro_text;
}
if ( $heading || $intro_text ) {
genesis_markup( array(
'close' => '</div>',
'context' => $context,
) );
}
}

view raw
functions.php
hosted with ❤ by GitHub

The first 4 lines simply unhook the actions responsible for showing the Archive Intro Text at its default position. This will make sure the Archive Intro Text does not show up in its usual location.

The rest of the code moves the Archive Intro Text down to the bottom of the page, by hooking on to the “woocommerce_after_shop_loop” action. The trick is to get the relevant parameters passed to the function that eventually displays the Archive Intro Text.

This example is limited only to moving the Archive Intro Text. You will have to extend it to display the Archive Headline to the bottom.

Do let me know if you have any questions!

Categories
Wordpress

How to Setup Algolia Search on WordPress with Genesis Theme

For those of you new to Algolia, this is another Instant Search solution for WordPress search. It is a 3rd party solution. The plugin is Free. The service is based on the free plugin on the client side communicating with the Algolia servers to retrieve search results for users.

The Algolia setup is very simple. You download the plugin from https://github.com/algolia/algoliasearch-wordpress. Install the plugin manually via your WordPress Admin. Create a new account on www.algolia.com.

For some reason, my drop down box containing the Search Results was not showing. It would only happen when using a Genesis theme. For other themes (I verified it worked with Storefront), it worked just fine. I posted in the StudioPress forums, but no luck. I inspected the Chrome Inspect Console but saw no JS errors.

I even tried disabling one plugin at a time, trying to figure out a possible conflict. No luck there either.

I decided to seek the help of Algolia’s support team. Sylvain Utard, the VP of Algolia Search came to my rescue. After a few experiements and emails back and forth, he figured out the issue. The issue was CSS related.

From my understanding, the Search Form overflow property was not visible by default in Genesis. The drop down for Algolia Search Results was always there, just invisible!

Here is the fix
.search-form {
overflow: visible !important;
}

Voila! That CSS change did the trick.

Categories
Wordpress

WooCommerce: Replace “Add To Cart” with “Ask” Form for Custom Products

When you Google the phrase “replace Add to Cart WooCommerce” (or any variants thereof), there are a lot of SERP’s suggesting the same way of hiding/not displaying the Add To Cart button. The idea is to remove the “woocommerce_template_single_add_to_cart” action.

Replace “Add to Cart” for Custom Products

However, in my case, I wanted to do this for only certain specific products. I have products that are marked as “Custom”, i.e. not stocked products. These are products that can be customized as per the customer/client. They usually take 6-8 weeks from order to delivery. The idea for the landing pages for these “Custom” products is not for them to make the purchase outright, but get them to contact the store owner. This way, the process of designing the custom product can be taken offline.

How to Designate a Product as “Custom”

The first step is to identify the products as “Custom” in WooCommerce. Following this guide by Remy Corson, I added a Checkbox under the General tab in the Product Edit Screen. When this Checkbox is checked, it means that the Product is “Custom”.

"Custom" product
“Custom” product

As you can see in the image above, this product is defined as “Custom”.

Next up, we have to hook into the correct action for marking all “Custom” products as not being allowed for Purchase. We will use the is_purchaseable() hook for that purpose.

Alternative CTA for “Custom” Products

I wanted a form to take the place of the “Add To Cart” button. In my case, I use Contact Form 7 for my forms. I just added a small form specifically for the purpose of this CTA. The visitors will see this form in place of the Add to Cart button. The store owner receives an email containing the lead.

We are simply checking if the Product is “Custom”. If it is “Custom”, we will set it as being non-purchaseable. This will hide the “Add To Cart” button for such “Custom” products.

Full Code Listing

Here is the full code. Notice that 2 other custom fields were added. You do not need those, so you can easily delete the relevant lines of code.

add_action( 'woocommerce_product_options_general_product_data', 'woo_add_custom_general_fields' );
function woo_add_custom_general_fields() {
global $woocommerce, $post;
echo '<div class="options_group">';
woocommerce_wp_checkbox(
array(
'id' => '_no_free_shipping_checkbox',
'wrapper_class' => '',
'label' => __('Exclude From Free Shipping', 'woocommerce' ),
'description' => __( 'Dis-allow Free Shipping', 'woocommerce' )
)
);
woocommerce_wp_checkbox(
array(
'id' => '_discontinued_product_checkbox',
'wrapper_class' => '',
'label' => __('Discontinued Product', 'woocommerce' ),
'description' => __( 'No longer in Production', 'woocommerce' )
)
);
woocommerce_wp_checkbox(
array(
'id' => '_custom_product',
'wrapper_class' => '',
'label' => __('Custom Order Product', 'woocommerce' ),
'description' => __( 'Product can be customized', 'woocommerce' )
)
);
echo '</div>';
}
// Save Fields
add_action( 'woocommerce_process_product_meta', 'woo_add_custom_general_fields_save' );
function woo_add_custom_general_fields_save( $post_id ){
// Checkbox
$woocommerce_checkbox = isset( $_POST['_no_free_shipping_checkbox'] ) ? 'yes' : 'no';
update_post_meta( $post_id, '_no_free_shipping_checkbox', $woocommerce_checkbox );
$woocommerce_product_checkbox = isset( $_POST['_discontinued_product_checkbox'] ) ? 'yes' : 'no';
update_post_meta( $post_id, '_discontinued_product_checkbox', $woocommerce_product_checkbox );
$woocommerce_custom_product_checkbox = isset( $_POST['_custom_product'] ) ? 'yes' : 'no';
update_post_meta( $post_id, '_custom_product', $woocommerce_custom_product_checkbox );
}
add_filter('woocommerce_is_purchasable', 'ar_custom_is_purchasable', 10, 2);
function ar_custom_is_purchasable( $is_purchasable, $object ) {
// get the product id first
$product_id = $object->get_id();
// get the product meta data
$is_custom = get_post_meta($product_id, '_custom_product', true);
if ($is_custom == "yes"){
return false;
}
else {
return true;
}
}
add_action( 'woocommerce_single_product_summary', 'ar_custom_product_cta', 60);
function ar_custom_product_cta()
{
global $product;
// get the product id first
$product_id = $product->get_id();
// get the product meta data
$is_custom = get_post_meta($product_id, '_custom_product', true);
// Show the Form if product is Custom
if ($is_custom == "yes"){
echo '<h5> Questions? We respond in minutes! </h5>';
echo do_shortcode( '[contact-form-7 404 "Not Found"]' );
}
}

view raw
functions.php
hosted with ❤ by GitHub

Questions/Comments? Type away in the comments below.