Add filter to WooCommerce category metabox on Product Set “edit” page

If you have a lot of categories (like I have on geargrabber.net) you may find that scrolling and finding the category you are looking for is time-consuming. Here’s what the default WooCommerce category metabox looks like on a Product Set “edit” page:

You can use the Post Category Filter plugin to add a filter to your WooCommerce category box to make it easy to filter your long list of WooCommerce categories to something more manageable.

Here’s what the end result looks like:

Simple as that!

GDPR, WordPress & You – A Webinar

This morning SiteGround did a 1-hour webinar about GDPR. In the webinar their senior legal advisor Maya Stoyanova talked about the procedures you need to follow in order to run a GDPR-compliant business and the changes you may need to undertake to comply with the new regulations.

If you are running a WordPress website (which you probably are), there were many relevant questions regarding GDPR and WordPress and what steps you need to take to get compliant.

The webinar was NOT SiteGround-specific so even if you are not hosted with SiteGround, it doesn’t matter. It’s all very relevant.

Check it out: https://www.crowdcast.io/e/what-is-gdpr

I would also keep an eye on SiteGround’s Blog for a summary and transcripts of the webinar in the next few days.

Here is the related blog post.

Custom WooCommerce Buy Buttons

There are lots of fun things you can do to WooCommerce [Buy Now] and [Add to Cart] buttons.

Here I’ll show you a few ways you can customize WooCommerce buttons which appear in The Loop (Shop Frontpage, Shop Category pages, etc…). You can add the code snippets below to a Custom Plugin.

First, let’s see how buttons look like be default. Here is a screenshot of 3 products on a category page. All 3 of these products are External (or Affiliate) products so they display a greyed out [Buy Now] button.

Default Button Display in WooCommerce Loop

Add Highlighting to Buttons for Products On Sale

This one will format the WooCommerce buttons by highlighting the buttons for products which are on sale.

Here’s what that would look like:

The custom code for that looks like this:

<?php /** * Add highlighting to an add to cart button for WooCommerce products which are on sale. * * @param string $html * @param WC_Product $product * @param array $args * * @return string */ function mycode_display_highlighted_add_to_cart_button_for_products_on_sale( $html, $product, $args ) { if ( ! $product->is_on_sale() ) { return $html; } $extra_class = ' alt'; $extra_text = ' SALE'; $url = $product->add_to_cart_url(); $quantity = isset( $args['quantity'] ) ? $args['quantity'] : 1; $class = isset( $args['class'] ) ? $args['class'] . $extra_class : 'button' . $extra_class; $attributes = isset( $args['attributes'] ) ? wc_implode_html_attributes( $args['attributes'] ) : ''; $text = $product->add_to_cart_text() . $extra_text; $format = '<a href="%1$s" data-quantity="%2$s" class="%3$s" %4$s>%5$s</a>'; return sprintf( $format, esc_url( $url ), esc_attr( $quantity ), esc_attr( $class ), $attributes, esc_html( $text ) ); } add_filter( 'woocommerce_loop_add_to_cart_link', 'mycode_display_highlighted_add_to_cart_button_for_products_on_sale', 20, 3 );

Remove Buy/Add Buttons

In some cases you may want to remove the buttons altogether. This might be useful for the next few code snippets.

Here’s how to remove the Buy/Add buttons from your WooCommerce Loops.

<?php /** * Remove the action which adds [Add to Cart] & [Buy Now] buttons * to the WooCommerce Product Loop */ function mycode_remove_add_to_cart_from_loop() { remove_action( 'woocommerce_after_shop_loop_item', 'woocommerce_template_loop_add_to_cart' ); } add_action( 'init', 'mycode_remove_add_to_cart_from_loop' );

Add [More Details] Button Linking to Single Product Page

Now that the Buy/Add button is gone, we can add some other buttons to The Loop.

Here we will add a [More Details] button which links to the single product page.

While it’s not that exciting, we will build on this in the following examples. But if you like this, here’s the code:

<?php /** * Adds an action to the loop to display a [More Details] button * to the WooCommerce Product Loop which links to the Single Product Page. */ function mycode_add_more_details_button_to_single() { add_action( 'woocommerce_after_shop_loop_item', 'mycode_display_more_details_button_to_single' ); } add_action( 'init', 'mycode_add_more_details_button_to_single' ); /** * Display [More Details] button HTML with a link to the Single Product Page. * * @global WC_Product $product */ function mycode_display_more_details_button_to_single() { global $product; $url = $product->get_permalink(); $text = __( 'More Details', 'mycode' ); $title = sprintf( __( 'View more details about %s', 'mycode' ), $product->get_name() ); $class = sprintf( 'button add_to_cart_button product_type_%s', $product->get_type() ); $format = '<a href="%1$s" title="%2$s" class="%3$s">%4$s</a>'; printf( $format, esc_url( $url ), esc_attr( $title ), esc_attr( $class ), esc_html( $text ) ); }

Link Directly to External/Affiliate Site from [More Details] Button

The one is strictly for External/Affiliate products. This adds a new [More Details] button which links directly to the external/affiliate website instead of your single product page.

This looks very similar to the default functionality of WooCommerce.

And here’s the custom code:

<?php /** * Adds an action to the loop to display a [More Details] button * to the WooCommerce Product Loop which links to an External/Affiliate product. */ function mycode_add_more_details_button_to_external() { add_action( 'woocommerce_after_shop_loop_item', 'mycode_display_more_details_button_to_external' ); } add_action( 'init', 'mycode_add_more_details_button_to_external' ); /** * Display [More Details] button HTML with a link to an External/Affiliate product. * * @global WC_Product_External $product */ function mycode_display_more_details_button_to_external() { /** * @var WC_Product_External $product */ global $product; if ( 'external' != $product->get_type() ) { return; } $url = $product->get_product_url(); $text = __( 'Buy Now', 'mycode' ); $title = sprintf( __( 'View more details about %s', 'mycode' ), $product->get_name() ); $class = sprintf( 'button add_to_cart_button product_type_%s', $product->get_type() ); $format = '<a href="%1$s" title="%2$s" class="%3$s" target="_blank">%4$s</a>'; printf( $format, esc_url( $url ), esc_attr( $title ), esc_attr( $class ), esc_html( $text ) ); }

Add Price to Button

Here we will add the product’s price (regular or sale price) to the button. This helps prices stand out a bit more.

Here’s the custom code for that:

<?php /** * Adds an action to the loop to display a [Buy $199.99] button containing the price * to the WooCommerce Product Loop which links to an External/Affiliate product. */ function mycode_add_more_details_button_to_external_with_price() { add_action( 'woocommerce_after_shop_loop_item', 'mycode_display_more_details_button_to_external_with_price' ); } add_action( 'init', 'mycode_add_more_details_button_to_external_with_price' ); /** * Display [Buy $199.99] button HTML containing the price with a link to an External/Affiliate product. * * @global WC_Product_External $product */ function mycode_display_more_details_button_to_external_with_price() { /** * @var WC_Product_External $product */ global $product; if ( 'external' != $product->get_type() ) { return; } $url = $product->get_product_url(); $price = wc_price( $product->get_price() ); $text = sprintf( __( 'Buy %s', 'mycode' ), $price ); $title = sprintf( __( 'Buy %s', 'mycode' ), $product->get_name() ); $class = sprintf( 'button add_to_cart_button product_type_%s', $product->get_type() ); $format = '<a href="%1$s" title="%2$s" class="%3$s" target="_blank">%4$s</a>'; printf( $format, esc_url( $url ), esc_attr( $title ), esc_attr( $class ), $text ); }

Add Merchant’s Name to Button

Here’s another one that works well for External/Affiliate products. We can also add the merchant’s name to the button.

And here’s the code:

<?php /** * Adds an action to the loop to display a [Buy at Acme] button containing the merchant's name * to the WooCommerce Product Loop which links to an External/Affiliate product. */ function mycode_add_more_details_button_to_external_with_merchant_name() { add_action( 'woocommerce_after_shop_loop_item', 'mycode_display_more_details_button_to_external_with_merchant_name' ); } add_action( 'init', 'mycode_add_more_details_button_to_external_with_merchant_name' ); /** * Display [Buy at Acme] button containing the merchant's name in the * WooCommerce Product Loop which links to an External/Affiliate product. * * @global WC_Product_External $product */ function mycode_display_more_details_button_to_external_with_merchant_name() { /** * @var WC_Product_External $product */ global $product; if ( 'external' != $product->get_type() ) { return; } $url = $product->get_product_url(); $merchant = $product->get_attribute( 'pa_merchant' ); $text = $merchant ? sprintf( __( 'Buy at %s', 'mycode' ), $merchant ) : __( 'Buy Now', 'mycode' ); $title = sprintf( __( 'View more details about %s', 'mycode' ), $product->get_name() ); $class = sprintf( 'button add_to_cart_button product_type_%s', $product->get_type() ); $format = '<a href="%1$s" title="%2$s" class="%3$s" target="_blank">%4$s</a>'; printf( $format, esc_url( $url ), esc_attr( $title ), esc_attr( $class ), esc_html( $text ) ); }

Those are all for the WooCommerce Loop but if you want to make similar customizations on the Single Product Page, you would want to use the woocommerce_after_single_product_summary hook.

Using OAuth 1.0 with the WordPress API & Custom Endpoints

This very long article covers setting up OAuth 1.0 to work with the core version of the WordPress API.

We will be generating client keys, oauth tokens, a custom endpoint and allowing access to our API only from those whom we have manually authenticated via OAuth.

Before we begin, I have 2 local installs of WordPress:

http://wp-api-main/

This is our main API website that our client will make requests to.

http://wp-api-client/

This is our client website that will make requests to the API website.

I have listed quite a few resources that I used to put together this tutorial at the bottom of the article. In some cases, I’ve just copied the code or steps. If you get stuck or want more info, I highly encourage you to read those resources. They will definitely be worth your time!

Get some coffee and let’s begin!

Test to make sure you can connect to the API.

Add the following code to your plugin on your client site.

Change the $url value to match the URL of the API site’s URL. So it will be something like http://example.com/wp-json

Mine is installed on my local machine that’s why the URL is http://wp-api-main/wp-json

Then create a new page and add this shortcode to the page [test_api_connection]

Open the page and you should see content like this:

Array
(
    [name] => WP-API Main
    [description] => Just another WordPress site
    [url] => http://wp-api-main
    [home] => http://wp-api-main
    [namespaces] => Array
        (
            [0] => oembed/1.0
            [1] => wp/v2
        )

    [authentication] => Array
        (
        )

    [routes] => Array
        (
            [/] => Array
                (
                    [namespace] => 
                    [methods] => Array
                        (
                            [0] => GET
                        )

                    [endpoints] => Array
                        (
                            [0] => Array
                                (
                                    [methods] => Array
                                        (
                                            [0] => GET
                                        )
// ...

Here’s what mine looks like:

The thing to notice is that the [authentication] is an empty array. Let’s change that part.

Now go to your main API site and install and activate the “WordPress REST API – OAuth 1.0a Server” plugin.

Once installed and activated, go to Users > Applications and click the [Add New] button.

On the “Add Application” page you will fill in some values. “Consumer Name” and “Description” can be whatever you wish. Just put something that makes sense.

I’m not sure what the Callback does at this time so I just inserted something like “http://wp-api-main/auth/success”.

After you have filled in all 3 fields, click the [Add Consumer] button.

Now you will see that the Client has a Client Key and Client Secret under the “OAuth Credentials” section.

Copy those for later.

Client Key (consumer_key):

CUQVVujOraZd

Client Secret (consumer_secret):

dcsgQT9B99f2bMSj2IWwMpbmr9dZFE0wzLCK4sZue60jZ9v9

Now if you go back to the new page you created on your Client site which has the shortcode, the [authentication] array should have some values in it.

Now we need to hook up these keys with our client so that our client can then make subsequent authenticated API requests to our API site.

The details behind this process are complicated and covered in detail in some of the resource links I share at the bottom of this article especially on nobil.cc.

The method I found the easiest to use the above keys is with an app like Postman or Paw. Postman is free and also has a Chrome extension if you aren’t on a Mac. I’ll use Postman in this tutorial because it’s free.

For these next steps, I relied heavily on nobil.cc. It contains more background on this process. Definitely worth the read!

If you have questions about these next few steps, check that article, too.

Open the Postman app.

In the “Get” request URL field enter the URL of the main API website’s /oauth1/request path. I have entered

http://wp-api-main/oauth1/request

Under Authorization Type, choose OAuth 1.0

Enter the Consumer Key (ie. the Client Key) and Consumer Secret (ie. the Client Secret).

Click the [Update Request] button. This will automatically fill in the timestamp field and the Nonce field.

Your request URL will have been update to something like this:

http://wp-api-main/oauth1/request?oauth_consumer_key=CUQVVujOraZd&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1481140823&oauth_nonce=vRxe3poywrw&oauth_version=1.0&oauth_signature=csft+J7hOAR97WhE6nZoRFhUfZs=

Then click the [Send] button.

Scroll down to see the response. You should see something like this:

oauth_token=OXjySsfBdsbWa04BMFAJy6ox&oauth_token_secret=8O20H7lyZ2s0vaGwnF4QrkdNt26iFXbpRsoigMFiwylwgqLz&oauth_callback_confirmed=true

Save that string somewhere. We will need these 2 initial OAuth tokens:

oauth_token

OXjySsfBdsbWa04BMFAJy6ox

oauth_token_secret

8O20H7lyZ2s0vaGwnF4QrkdNt26iFXbpRsoigMFiwylwgqLz

Next, we will use the “oauth_token” value we just got and construct a URL like this (on our Main API site):

http://wp-api-main/oauth1/authorize?oauth_token=OXjySsfBdsbWa04BMFAJy6ox

Load the URL in your browser.

You will get a login-type screen which has a message like “Howdy eric,
“Client API Connection” would like to connect to WP-API Main.”

Click the [Authorize] button.

The next screen will have a message like this: “Your verification token is PvHs1j8ol5FqVd4QPv0eGt9t

verification token

PvHs1j8ol5FqVd4QPv0eGt9t

Copy that token down… we’ll need that later, too!

The last step is to get our access token.

So in Postman, enter this URL into the request URL field:

http://wp-api-main/oauth1/access

This time you will need to fill in your:

  • Consumer Key
  • Consumer Secret
  • Token
  • Token Secret

Clicking the [Update Request] button will fill in the nonce and timestamp and your request URL will be transformed into something like this:

http://wp-api-main/oauth1/access?oauth_consumer_key=CUQVVujOraZd&oauth_token=OXjySsfBdsbWa04BMFAJy6ox&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1481141563&oauth_nonce=dJemNhotMRa&oauth_version=1.0&oauth_signature=08eCfNrGqovw4OF6P2uNrymLSdE=

Next click the [Params] button to the right of the request URL.

Add 1 new row to the params. Set the key to “oauth_verifier” and the value to “PvHs1j8ol5FqVd4QPv0eGt9t” (which is the value we got in the previous step.

Then click the [Send] button. Note that you may need to click the [Update Request] again and then click [Send].

After you scroll down you will see your new tokens.

oauth_token=LLnMXOF4aYlktf4X6DhAy0fr&oauth_token_secret=Ig5jaC1GmavfWrvUgN7w16LJT5yZT03DHRddmqpKM71us8Rn

oauth_token

LLnMXOF4aYlktf4X6DhAy0fr

oauth_token_secret

Ig5jaC1GmavfWrvUgN7w16LJT5yZT03DHRddmqpKM71us8Rn

You can now disregard your old oauth_token and oauth_token_secret tokens. You won’t need the old ones anymore. You only need these new ones. Additionally, you can disregard your oauth_verifier code as well. You shouldn’t need that code any more.

OK, we are done with Postman and we now have the necessary OAuth tokens we need in order to make an authenticated API request.

The only thing is, our main API site does not require authentication yet. So let’s force it to require authentication.

On your main API site, install the Disable Rest API plugin.

So now we have 2 plugins installed on our main API site:

  • WP REST API – OAuth 1.0a Server
  • Disable REST API

Let’s make sure our API now requires authentication. Go to the test page you created on your client site and load the page again.

You should now see a 401 “rest_cannot_access” message like this.

Excellent! I bet you’ve never been so happy to see a 401 message. 🙂

So, now that we have the keys to get around that restriction and authenticate, let’s do so.

But we’re not out of the woods yet… unfortunately, not even close.

The next bit of code mostly comes from this Sitepoint article. Please refer to that for more details. In our case, we will make a class that handles sending the proper headers to an API that requires authentication. Time to get more coffee… I’ll be here when you get back.

We are going to create a new shortcode so that we can add it to our page with the other shortcode.

Let’s first go to our page and modify our page so it looks like this:

<h2>test_api_connection</h2>
[test_api_connection]

<h2>test_api_oauth_connection</h2>
[test_api_oauth_connection]

Then we need to add a new shortcode to our plugin so we can start testing our OAuth connection.

Here’s our new shortcode:

A few things to take note of…

We added a $method variable and set it to ‘GET‘. That matches our wp_remote_get() call.

We added the various keys that we created to a $keys array.

Then we passed the $keys, $url and $method variables as parameters to our new OAuth_Authorization_Header() class. More on that in a second.

We get the $header value from the get_header() method and then added our authorization header array to a new $args array.

Lastly, we modified our call to wp_remote_get() by adding the $args array as the second parameter to the function.

The rest of the shortcode code is the same as our first one.

OK, so what about the new OAuth_Authorization_Header class?

Well, it’s a modified and simplified version of the class in the Sitepoint article which is itself a modified and simplified version of the code from a Twitter WordPress HTTP client. Love me some open source!

Here’s the basic OAuth_Authorization_Header class without documentation or checks to ensure all the required data is present… it’s a starting point. Be sure to validate the incoming arguments.

So now when you load the page with your shortcodes on your client site, you should have one ‘access denied’ message and one with a successful response.

The last thing I want to do is set up a custom endpoint on my API site and make that custom endpoint protected. Currently, all of our endpoints are private because we installed the Disable REST API plugin. But what if you just want a single custom endpoint to be private, not all endpoints?

Here’s how we can do that.

First, disable the Disable REST API plugin on your main API site.

After you do that, now both your shortcodes on your client site should load a successful response like this:

Next, create a custom plugin on your main API site and add the following code to your plugin:

Basically, this creates a new endpoint and returns an array if successfully called.

Let’s tweak our URLs in our client plugin:

In the client plugins we have this:

$url = 'http://wp-api-main/wp-json';

Let’s change that to this to make a request to a specific endpoint:

$url = 'http://wp-api-main/wp-json/myapiplugin/v2/greeting';

Be sure to change the $url variable in BOTH shortcodes.

Then refresh the page on your client site with the shortcodes. You should now see 2 successful responses.

Perfect! Now, let’s make that new endpoint private so only authenticated clients can access it.

Change the Main API plugin to this:

You see we have added a new item to array in the third parameter of the call to register_rest_route(). We have added:

'permission_callback' => 'myapiplugin_permission_callback',

Then we added a new function to our plugin that returns ‘true‘ if the current user can update core WordPress or false if the user can not. Who is this user that “current_user_can” is referring to? That user is our client who we have granted permissions to through the OAuth key exchange (back in the Postman section of this article).

So now that we have added this new authentication code to our plugin, let’s go back to our client shortcode page and reload that page.

Now you should see 1 access denied message and one success message.

And now you have successfully set up OAuth1 with your WordPress API so that a client that you have explicitly granted access to can call a private endpoint that you have set up.

Resources

Here are some of the articles I referenced when putting my article together. They were a huge help so if you are looking for more information about some of these steps, do check these out:

2 Normalizing brand names when importing affiliate data feed into WooCommerce

WooCommerce is great for importing data feeds into and using for your datafeed-driven affiliate store. However, one of the frustrations of using data feeds is data inaccuracy. We (datafeedr) try to normalize data before it hits our database, we can’t normalize everything.

That’s why it’s important for users to be able to normalize information before it gets inserted into your store.

In the normalizing brand names documentation article we just published, you can learn how to normalize all of your product’s brand names.

So instead of those new shoes you just imported being listed under the brand names “Nike Co.” or “Nike Inc” or “Nike International”, now they can all simply be listed with the brand name “Nike”.

6 Modifying WooCommerce Product URLs

Before WooCommerce 2.5, it was possible to change/modify/override the URL to the product page using a filter on the the_permalink hook. For example, you could do this:

[php] add_filter( ‘the_permalink’, ‘mycode_the_permalink’ );
function mycode_the_permalink( $url ) {
global $product;
if ( ! empty( $product ) ) {
$url = ‘http://www.google.com’;
}

return $url;
}
[/php]

That would change all of the links to your product pages to a link to Google instead. If you felt compelled to give Google more Page Rank, you could do that. However, in version 2.5 of WooCommerce, that code no longer works.

Instead you need to remove WooCommerce’s woocommerce_before_shop_loop_item action and then re-add it again with your own function. Here’s a version:

[php] remove_action( ‘woocommerce_before_shop_loop_item’, ‘woocommerce_template_loop_product_link_open’, 10 );
add_action( ‘woocommerce_before_shop_loop_item’, ‘mycode_woocommerce_template_loop_product_link_open’, 20 );
function mycode_woocommerce_template_loop_product_link_open() {

$url = ‘https://www.datafeedr.com/’;

echo ‘<a href="’ . $url . ‘">’;
}
[/php]

Now all links to the product pages which are generated within the Loop on your site will be linked to Datafeedr! Yay! We could use a little help with Page Rank.

New “Media Library Filter” plugin released today

When your WordPress site starts to grow and you have lots of images in your library from Posts, Pages, Products and other WordPress content types, filtering the the media in your library becomes very difficult.

I thought for sure there would be a plugin available that would enable filtering of the media in my Media Library… but there wasn’t much. What I did find was a post on StackExchange from 2013 with a quick solution. So I spent a couple of days and put together the Media Library Filter plugin.

banner-1544x500

The Media Library Filter plugin allows you to filter the media in your library by taxonomy or term.

A taxonomy would be the type of category. For example some taxonomies might be Blog Categories, Product Categories, Post Tags, etc.

A term would be the name of an item in the category. For example the taxonomy Product Categories might contain the terms “Rock Climbing Shoes” and “Rock Climbing Helmets”.

If you have a suggestion or way to improve, please let us know.

Download the plugin

Enjoy!

Exclude duplicates based on Merchant ID

We’ve added a new option to the Exclude Duplicates filter in the Factory search form. Now you can also exclude duplicates based on the same merchant ID. This is useful if you want to create a list of the same product from multiple merchants without repeating any one merchant.

exclude-duplicates-by-merchant-id

1 2 3 4