Categories
WordPress Tips

Remove WordPress REST API links

When using the WordPress REST API, it might be needed to hide some WP REST API links from the page source to the outside world. Here’s how to do that.

Remove WordPress REST API Links

Let’s say you have added your own endpoint to the WP REST API, and do not want to publicly show it in the page source. There are two places the WP REST API link is added to the page source:

To remove these links we simply remove the functions that are hooked to wp_head and template_redirect

// Hide WP REST API links in page headers
remove_action( 'wp_head', 'rest_output_link_wp_head', 10);
remove_action( 'template_redirect', 'rest_output_link_header', 11);

Be aware that if REST API calls are done via AJAX, the browser shows the URL that is called, and it is visible to the public.

Bonus: Clear the WP-JSON index

If you have the WP REST API active, and don’t want people to see what endpoints are available, use below code.

// Hide WP REST API JSON index
add_filter('rest_index', 'nstrm_hide_wp_json_index');
function nstrm_hide_wp_json_index( $response ){
	return array();
}

If you want to further customize the WP REST API, use the plugin Disable REST API. This plugin disables all endpoint of the WP REST API and allows you to enable individual endpoints.

Screenshot of settings for the Disable REST API plugin
Settings of the Disable REST API plugin.
Categories
WordPress Tips

Using FacetWP to show featured posts first

Want to show featured posts first in FacetWP results? Here’s how to do it.

The requirement

A client, using FacetWP to show posts filtered by a few Advanced Custom Fields, wanted to introduce featured posts. These posts need to be show first, and the rest of the results should be shown random.

Since FacetWP does an AJAX request every time a page is requested, or a facet is changed, we need to do something to prevent every request to show random posts, since that could show posts on multiple result pages.

Thinking about a solution

When a client drops an idea, actually any idea, my brain starts to think on how to implement this. With my experience I know some filters of my favorite plugins, but not all. So I draft a solution first.

Posts need to have an extra field, where the client can indicate a post is featured. Since the posts already have some ACF fields, I will add the featured field to it.

Next up is filtering the query to feed FacetWP the $post_ids in the right order, with featured posts first, followed by the other posts in a random order. I found out the facetwp_filtered_post_ids filter can be used for that.

Coding the solution

Here’s my favorite part of implementing a clients request. The actual coding. I start with writing the facetwp_filtered_post_ids filter function. This function has to return all post ids, in the right order, with the featured post on the front of the array.

The query arguments are self explanatory, except maybe for the meta_compare argument. LIKE is used here, becuase ACF stores the value of the uitgelicht (Featured) field in a serialized way. Be careful when using this, since this might give you unexpected results. In this case, the uitgelicht field can only have a value of 'ja' (yes), or '' (empty), so we can safely use this.

The we launch the query and get the results on line 16. NOw we need to remove the featured posts from the $posts_ids variable that is passed to this function through the filter. So we walk through the $post_ids and put the featured post id in the $matches array, and remove the post id from the $post_ids array. This gives us two arrays, $matches which holds the featured posts, and $post_ids which holds all other posts, without the featured posts.

Now we need to randomize the posts in $post_ids. As stated before, we don’t want to randomize on every request. With the client I discussed this, and came to a solution where results would be randomized every hour.

First I used the shuffle() function to randomize the $post_ids array. Sadly, this did not give me an option to keep the random order for a certain amount of time. So, here comes array_multisort().

We first set a duration in minutes, get the current time in minutes and subtract the duration from this time, to set the seed. Then we use the mt_srand() function to create seed. Next is seting the order and use that to re-order the $post_ids array with array_multisort().

The final thing we need to do in this filter function is merge the randomized post ids, together with the featured posts and return the array.

🚀 Performance tip: If you only need post ids, add 'fields' => 'ids' to the query args. This results in a query that only asks for ids, and not all other fields available in the posts table.

Apply the sorting to the FacetWP template

One final step is needed to get the results right. We need to set the order and orderby parameters on the FacetWP query for the template we want this to be used for. Here’s the facetwp_query_args filter. We add the query parameters to the original query parameters, and return the $query_args variable.

Here the code on Github, feel free to comment if you want.

Photo by Susan Yin on Unsplash

Categories
WordPress Tips

Preselecting a facet and hiding it automatically with FacetWP

In this short howto I will show you how you can preselect a facet, and hide it when the page renders.

Preselecting facets

For this case the client has a few facets that are filled by custom fields. The fields are specialism and level. These a are related to a custom post type Mentor.

To preselect a facet, we can use the facetwp_preload_url_vars filter. But, during execution the $post global variable is not available, so we need to first get the post. By using the get_uri() method of the FWP->helper class, we can get the uri. This can be mapped to the page name which a separate function nostromo_get_post_id_by_post_name() uses to get the post id.

When we have the post id, it’s possible to get the custom fields and use them as values for the preselects. We just have to add them to the url_vars[] array with the names of the facets.

At the end of the function we return the url_vars array, so the filter can be applied.

Hiding the preselected facet

To hide the preselected facet(s), all we have to is add a piece of CSS to the page, when the url_vars are set. Here’s when a hook comes in very handy. You can see the code on lines 25 and 36. You might need to tweak the CSS, or add some classes to make the CSS specific enough to work. Or use !important, just don’t tell anyone I gave you that advice.

Need technical help with WordPress?

Do you need technical help with WordPress? Please let me know.

Categories
WordPress Tips

Using FacetWP with post to post relations

On a client site some posts have a relationship with other posts, set in meta fields. The client wanted to have a category filter for products on a brand page. In this blog I will explain what I did to achieve this.

The Relationship

Let me first explain what the post to post relationship is.

There are two Custom Post Types: Brand and Product. Products can have exactly one brand, and these are stored in a custom field made with ACF.

The Requirement

The client was showing all products of a brand on the brand page. This was the initial requirement, but as we all know, requirements can change. And so did this one. The client wants visitors to be able to filter the products by category. This allows them to better find a product they want, for example a desk, a chair or a separator wall.

The solution in words

When a client tells me about a requirement, or a change, I immediately start to browse through my known plugins library in my head, and fire up my code engine. Also in my head.

I made up the following:

  • Use FacetWP
  • Create a taxonomy product-category for product
  • Create a facet based on the product-category
  • Create a FacetWP template to show the products
  • Done

Well, that looks easy, and in the end it was, but I had some struggles to get it right. The brand page only shows products of that brand. So I thought it would be handy to create a FacetWP template file in which I try to limit the WP query to only get products of the shown brand. And that’s where the s^%# hit the fan. But hey, making mistakes equals learning.

The solution in code

The FacetWP template for this case is pretty simple. Just a default loop, with some code on how to show posts (products in this case). This file is stored in the root directory of the theme as facetwp-products.php so FacetWP can find it.

<?php
echo '<div class="products">';
while ( have_posts() ) {
the_post();
echo '<div class="product">';
echo '<div class="product-image"><a title="' . get_the_title() . '" href="' . get_the_permalink() . '">' . get_the_post_thumbnail( get_the_ID(), 'rectangle-landscape' ) . '<h2 class="product-title">' . get_the_title() . '</h2></a></div>';
echo '</div>';
}
echo '</div>';

?>

The template and facets are loaded in the page template for brands. And that’s done with this code:

<?php

// Add brand collection to single-brand with facets and facet template
add_action( 'genesis_entry_content', 'nstrm_add_brand_products', 15 );
function nstrm_add_brand_products() {
  if ( is_singular( 'brand' ) ) {
  // load facets
  echo facetwp_display( 'facet', 'productcategory' );

// load default template
  echo facetwp_display( 'template', 'products' );
}
}

?>

The above code is used in a Genesis theme, but can be used in other themes as well.

When we stop after these steps, products will be shown and the facets will be active (if product-categories have been assigned to products). But, all products will be shown, and not just the products that belong on this brand page.

Adding brand awareness

We need to make sure FacetWP is aware that it should only show products that have the product-category set to the brand of the currently active brand page. This is where the facetwp_query_args filter comes in handy. We need to add some arguments to the query, being the brand. To do that we need the brand id.

On a ‘normal’ page or post, it;s easy to get the post ID. But, when using FacetWP, which uses AJAX calls, the post ID can sometimes not be present. So, we need to help FacetWP by adding the post ID to the page in a little snippet of JavaScript, and use that in the facetwp_query_args filter function.

<?php 
// facetwp - pass current post_id to FacetWP
add_action( 'wp_head', function () {
  if ( is_singular( 'brand' ) ) {
global $post;
?><script>
(function ($) {
$(document).on('facetwp-refresh', function () {
FWP_HTTP.post_id = '<?php echo $post->ID; ?>';
});
})(jQuery);
</script>
<?php
}
}, 100 );

?>

And let’s create the filter function so we can use the brand id in the query that FacetWP uses.

<?php

//facetwp - add facetwp_query_args filter for limiting products by brand
add_filter( 'facetwp_query_args', function ( $query_args, $class ) {

  // get post_id from Ajax request
$post_id = (int) $class->http_params[ 'post_id' ];
if ( empty( $post_id ) ) {
$post_id = get_queried_object_id();
}

$query_args[ 'meta_key' ] = 'merk';
$query_args[ 'meta_value' ] = $post_id;
$query_args[ 'posts_per_page' ] = '-1';

return $query_args;
}, 10, 2 );

?>

That’s it. Happy coding and thanks for reading. If you have any remarks or questions, please let me know in the comments.

Photo by rawpixel on Unsplash

Categories
WordPress Tips

Randomized content and caching – How to do it right

For a client I had to display partners on the site. To give them all equal visibility, they needed to be displayed in random order. I had a solution, but then came WP Rocket. That reduced page loading times, but also eliminated the random display of the partners since WP Rocket generates static pages.

Initial solution

In the initial solution I used the WP_Query  orderby argument and gave it the value rand. This gave me a randomized set results from the database, which were display through a widget. This works great, but it also requires database calls, every time the partners needed to be displayed. Which is on every page view. Yeah, not smart, I know. But we all need to learn right? And making mistakes is the best way of learning.

Optimizing the site

When optimizing the site I installed and configured WP Rocket. A great plugin which helps site owners to make their website load fast. Really fast. Another plugin/service I installed was Imagify. This service reduces image sizes, and since this site uses a lot of images, the reduction of the image sizes (as in file size) resulted in faster loading.

One challenge remained, the random display of partners didn’t work anymore. Every page was cached, and the order of partners on that page was fixed. And that’s not what we wanted.

The final solution

Thinking about this, I asked the friendly WP Rocket support desk if it was possible to not cache a certain part (fragment) of a page. I got a fast answer from Caspar Hübinger who told me that this was not possible, since WP Rocket stores the whole HTML document and fragments cannot be excluded. He also mentioned that dynamic features should be done in JavaScript and not in PHP. And I totally agree on that.

So, I decided to lookup some samples of randomizing the display of elements on a page, and came across a very cool post on Stackoverflow. A user had a similar challenge and another user came up with a really cool solution. A jQuery function randomize is created to randomize elements in a certain container. In the following code example div.band is the container we want to check for occurrences of table tr td and the elements to randomize are div.member


$('button').click(function() {
   $("div.band").randomize("table tr td", "div.member");
});

$.fn.randomize = function(tree, childElem) {
   return this.each(function() {
   var $this = $(this);
   if (tree) $this = $(this).find(tree);
   var unsortedElems = $this.children(childElem);
   var elems = unsortedElems.clone();

   elems.sort(function() { return (Math.round(Math.random())-0.5); });

   for(var i=0; i < elems.length; i++)
      unsortedElems.eq(i).replaceWith(elems[i]);
   });
};

In this case the randomize function is  triggered by a button, but in my case I want to execute the function when the page is loaded. So I adapted the code a little, changed the element names and came up with this:

$(document).ready(function () {
   $('section.widget_partner_widget').randomize('div.partners', 'div.supplier');
});

Now, the partner divs are randomly displayed. I’m happy and the client is happy.

If you have any comments, improvements, let me know in the comments.

Categories
WordPress Tips

Exporting / importing Gravity Forms MailChimp feeds

Introduction

During a project for nostromo, I had to configure lots of Gravity Forms which were connected to MailChimp lists. I set up all the forms and MailChimp feeds (thanks to the MailChimp Add-on) on the development environment, and included the migration steps in the ‘go-live-docs’.  In this case the project included a WordPress multisite network with three existing sites and one new site. Challenge accepted!

So, when testing the ‘go-live-docs’, I was ready to export and import the forms. The export was done with the standard Gravity Forms export feature, found in your WordPress dashboard under Forms -> Import/Export. The next step was importing the forms. Again this was a flawless task. When checking the forms however, I saw the MailChimp feeds were not imported. Hmm, that’s odd, or is it just how the import/export of Gravity Forms is supposed to work?

I contacted Gravity Forms support and got a fast, clear, disappointing answer:

I’m afraid that exporting feeds is not currently possible. We do have this in our feature request list so I have added your vote. I’m sorry for any inconvenience that this may cause you.

This did not bring hope, and I was preparing to manually copy the feeds. Until I realized I was part of a great community, the WordPress community. A community in which you can ask questions, no matter what the level is, and where someone is always available to answer your question. I decided to go to the Post Status Slack and post my question in the #heavydev channel.

After about 40 minutes I got a reply from Naomi C. Bush from gravityplus.pro. They also have a Slack team, which I have joined. Naomi directed me to a plugin, Import/Export Add-On Feeds for Gravity Forms. She had no experience with it, and I decided to give it a try. Since I have written this blog, you can conclude it works. Thanks Naomi, and thanks Anthony Montalbano for developing the plugin and sharing it with the WordPress community.

Let’s go to the details, how to use this plugin.

Installing the plugin

Install the plugin from your WordPress dashboard, by going to Plugins -> Add New. Search for the plugin by entering import export feeds gravity forms in the search box in the top right corner. Install and activate (or network activate if you need it in all sites in a network). The plugin is now active and we can use it.

How to export MailChimp feeds

To export feeds, we need to navigate to Forms -> Import/Export. Besides the default export options, we see two new options: Export Feeds, and Import Feeds.Export feedsClick on Export Feeds. In most cases you can select all feeds, or just select the feeds you want to export and click Download Export File. If you have chosen smart names for your feeds, selecting will be easy. If not, well, lesson learned right?

How to import MailChimp feeds

Go to Forms -> Import/Export and click on the Import Feeds link on the site where you want your feeds to be imported. Select the .jsonfile that you have exported and click on import.

Import MailChimp feeds

After the import is done, you will see a comforting notification on your screen:

Import succeeded

See the result

To see the result, go to your form. In the Settings tab go to Mailchimp. You will see your imported feed(s) in the MailChimp feeds list.

Import feeds result

 

If you have any questions about this, please let me know, I’m happy to help.