Redirect Certain Categories to new site

Sometimes you want to redirect posts in certain categories to a new location / domain name. You do not want to redirect the entire website to a new website, but only part of it. This because you perhaps split up your website into multiple websites. Well you are in luck. With WordPress this is possible with the use of template_redirect.

Template Redirect

Thanks to Ryan’s post we came to realize how easy this redirecting based on category is with WordPress built in template_redirect . This function is normally used to redirect / decide what template to load, but it can also be used to redirect to that same category on another website.

Example Category Redirect

Here is an example of two sets of redirect we used before. One redirecting to this current website and one to our partner WooAid:

// redirect WP stuff to WP Villain and Woo  stuff to Wooaid

add_action('template_redirect', __NAMESPACE__ . '\\post_redirect_by_custom_filters');
function post_redirect_by_custom_filters() {
    global $post;
    // this array can contain category names, slugs or even IDs.
    $catArray = ['Sage Starter Theme', 'Sage', 'WordPress', 'Trellis', 'Bedrock', 'Plugins', 'Themes'];
    if (is_single($post->ID) && has_category($catArray, $post)) {
        $new_url = "https://wpvilla.in/{$post->post_name}/";  
        wp_redirect($new_url, 301);
        exit;
    }
}

add_action('template_redirect', __NAMESPACE__ . '\\woo_post_redirect_by_custom_filters');
function woo_post_redirect_by_custom_filters() {
    global $post;
    // this array can contain category names, slugs or even IDs.
    $catArray = ['WooCommerce'];
    if (is_single($post->ID) && has_category($catArray, $post)) {
        $new_url = "https://wooaid.com/{$post->post_name}/";  
        wp_redirect($new_url, 301);
        exit;
    }
}

These two examples are used in a Sage 8 based theme so namespacing is mandatory. For themes that do not use namespacing you can leave that part out.

Block Password Recovery Attacks

Been annoyed by the recent barrage of brute force password recovery attacks. In WordPress you can block these kind of attacks by turning off password recovery. Now this is obviously not for everyone as you will not be able to reset your password if you ever lose it. And that would mean adjusting the code to reactivate this or do some cool database tweaks with knowledge of password hashing and or other stuff like salts and whatnot. But I can manage. So I added

// Block Password Recovery
function disable_reset_lost_password()
{
return false;
}
add_filter( 'allow_password_reset', 'disable_reset_lost_password');

to functions.php

WordPress Setup Digital Ocean

Digital Ocean Account Setup

To work with Digital Ocean you need an account. It can be one run by developer, but preferably one client runs and to which he invites you as a developer and or DevOps. So you need the following for starters

  • Setup of account by client with DO
  • Team Setup with access for developer

Droplet Setup

Droplet’s setup is relatively easy using the Digital Ocean’s interface. You need to choose the proper droplet, your preferred region, Droplet size and Droplet base OS.

  • Droplet Size – At least 1 GB
  • Region Choice
  • Ubuntu 18.0.4 Setup with SSH keys attached
  • FQDN domain name connected to ip Droplet
  • Domain name must be directed to Digital Ocean DNS Server from Domain Service Provider
  • Domain at Digital Ocean must be directed with A Record to Droplet’s IP
  • Digital Ocean LEMP with WordPress

WordPress Setup Digital Ocean

Once you have taken care of the Droplet and Domain setup we move onto the actual guide for setting up a WordPress LEMP. This guide will be your base. It explains all and refers to requirements needed like:

  • initial Ubuntu 18.0.4 Server Setup
  • LEMP Setup
  • Let’s Encrypt SSL Setup

before moving onto setting things up like the

  • actual database installation,
  • PHP extensions,
  • WordPress Download
  • WordPress Config Tweaks
  • WordPress GUI Installation

Digital Ocean Initial Server Setup

Digital Ocean has a great guide mentioning where need be all extra steps to take to setup a basic Ubuntu Server to get going

It will guide you through setting up a server with

  • Sudo User,
  • basic firewall,
  • external access

Digital Ocean LEMP Setup

For the LEMP setup there is another great guide. It will go through all the necessary steps to set up a LEMP with:

  • Nginx webserver,
  • MySQL Database server,
  • PHP-FPM PHP Parser

on your Ubuntu Linux Droplet.

SSL Setup

Let’s Encrypt SSL Setup to get a free SSL certificate is not hard anymore these days and DO also gots your back here with this guide. It explains the DNS needs to set you up with LE SSL using Certbot.

Database Setup

MySQL Database installation on MYSQL Server is explained in the base tutorial. It basically tells you how to add the database from the command line with needed privileges and such.

PHP Extensions

Installing the necessary PHP Extensions is also explained in the base guide just like the database setup. Just follow it, install the necessary extensions with apt:

  • sudo apt update
  • sudo apt install php-curl php-gd php-intl php-mbstring php-soap php-xml php-xmlrpc php-zip

See guide for all the details

WordPress Installation

Downloading of the needed WordPress files to run the WordPress CMS is what you need to do here. Also explained in base guide as well as on WordPress. We can use curl for this step as you will see in the guide.

WordPress Configuration

Configuring the WordPress configuration file to work with the set up database, necessary constants for security like the AUTH_KEY.

Installation via Interface

Installation of WordPress following the instructions in the browser. Explained in base guide as well as on WordPress and actually pretty self explanatory.

 

New WordPress Admin User w/ PHP

To add a New WordPress Admin User w/ PHP you can tweak the database, but you can also simply do it with PHP code. I prefer the latter as I am better at PHP than MySQL and as messing with the database once a site is up is always tricky.

PHP Snippet

Here is a snippet from WP Scholar to achieve this as a WordPress Must Use Plugin:

<?php

add_action( 'init', function () {
  
	$username = 'admin';
	$password = 'password';
	$email_address = 'webmaster@mydomain.com';

	if ( ! username_exists( $username ) ) {
		$user_id = wp_create_user( $username, $password, $email_address );
		$user = new WP_User( $user_id );
		$user->set_role( 'administrator' );
	}
	
} );

As you can see it adds three variables:

  • user name
  • password
  • email address

It also makes sure to only add it when it exists and assigns it the role of administrator which is what we were aiming for.

Application

Just add it to the following directory:

wp-content/mu-plugins

If the mu plugins folder is missing add it. This setup loads it as a must use plugin that wil be loaded on the fly by WordPress. Once that is done you should be able to log in with your new details. Once you checked it worked do not forget to remove this file!

And there you have it. A new admin level user with a new password without touching existing users nor the database.

Laravel Valet Nginx 502 errors

Sometimes your WordPress website won’t load properly in Laravel Valet. You keep on getting Laravel Valet Nginx 502 errors. What can be done about it?

Telltale Signs

In your .valet/Log/nginx-error.log  you will see something like:

HTTP/2.0", upstream
2018/07/24 09:18:54 [error] 15573#0: *328 upstream sent too big header while reading response header from upstream, client: 127.0.0.1, server:

That means that your Nginx defaults are not good enough to handle the needs of the local setup.

Nginx Configuration Tweaks

To deal with this you can change your Nginx confix under .valet/Nginx/site.test and add the following to the php block:

# 24 july 2018 parameters to avoid Nginx 502 errors
  fastcgi_temp_file_write_size 10m;
  fastcgi_busy_buffers_size 512k;
  fastcgi_buffer_size 512k;
  fastcgi_buffers 16 512k;
  fastcgi_connect_timeout 300;
  fastcgi_send_timeout 300;
  fastcgi_read_timeout 300;
  fastcgi_intercept_errors on;
  fastcgi_next_upstream error invalid_header timeout http_500;

source SO pacofelc

Restart Brew Nginx Services

You will then also need to restart Nginx for Valet and you can do this like so:

brew services restart nginx

Digital Ocean Volume for WordPress Media

Adding Digital Ocean Volume for WordPress Media can be very useful. Especially if you have a lot of images and or other media and need a cheap way to store them. Block Storage at Digital Ocean is fast and easy to work with. It allows you to get a cheap Droplet and add cheap storage.

We already wrote about Setting up Trellis on Digital Ocean. Now you can expand upon this with a volume for your media!

Format the Volume

To format the volume I followed Digital Ocean’s instructions. These you will get once a volume has been setup. In a popup you will get the line needed for your volume in your region. Idid a

sudo mkfs.ext4 -F /dev/disk/by-id/scsi-0DO_Volume_volume-lon1-01

Add directory to store media

If you have this WordPress setup from scratch and just start a website you can skip this as the media directory is yours to use

If you do not already have the media directory where you store the media you can create one. Most likely you do. We however moved the old sites one inside the wp-content/uploads for our multisite media to a backup location and then recreated it using:

sudo mkdir -p sites

This as we would later sync all that data from the production server.

Mounting the Volume

Next we had to mount that directory so that it would load all the data from the volume instead of the actual Droplet. We did that using:

sudo mount -o discard,defaults /dev/disk/by-id/scsi-0DO_Volume_volume-lon1-01 /srv/www/staging.domain.com/shared/uploads/sites;

And to make the mount permanent do a:

echo /dev/disk/by-id/scsi-0DO_Volume_volume-lon1-01 /srv/www/staging.domain.com/shared/uploads/sites ext4 defaults,nofail,discard 0 0 | sudo tee -a /etc/fstab

Then we checked if the mounting was successful:

cat /proc/mounts |grep staging
/dev/sda /srv/www/staging.domain.com/shared/uploads/sites ext4 rw,relatime,discard,data=ordered 0 0

As you see it was. And this means for most of you that you are done. You will have a Digital Ocean Volume for WordPress Media. We however needed to get the media copied over still.

Rsync Data

If you have this setup from scratch and just start a website you can skip this

Normally if you have a media volume already and need one for staging or another clone of the server you can just clone the volume and set up a new one and attach it. We only had an issue as the production server and volume were in a different region than staging so we had to set up a volume from scratch at both locations and sync the data between them:

ssh -o ForwardAgent=yes web@xxx.xxx.xxx.xx "rsync -aze 'ssh -o StrictHostKeyChecking=no' --progress /srv/www/domain.com/current/web/app/uploads/sites/ web@xx.xxx.xx.xx:/srv/www/staging.domain.com/shared/uploads/sites/"

Responsive Images with Width Based on Actual Image Width

WordPress has responsive images built in these days. Since 4.4 as a matter of fact. But what if we want to have WordPress Responsive images with width based on actual image width?

Current State and Needs

Can we generate something like WordPress responsive image (comments added to html):

<img width="1024" height="445" 
src="https://example.com/app/uploads/2016/04/name.jpg" // old browser support using src
class="d-block w-100" alt="" 
srcset="https://example.com/app/uploads/2016/04/name.jpg 1024w, // 1024 width image 
https://example.com/app/uploads/2016/04/name-300x130.jpg 300w, // 300px width image
https://example.com/app/uploads/2016/04/name-768x334.jpg 768w" / /768px width image
sizes="(max-width: 1024px) 100vw, 1024px">

where the 1024px can be based on the actual width of the image? That was the question we had and so we started looking around.

Additional Width

Somehow we had quite a few images that were smaller than 1024px on our site and these now got stretched as they are not 1024px wide.
As WordPress sees that the full image is 1024px by 445px that is what is uses to generate the images and though this works well it does not work for all. Looking for solution as suggested by Mark Root-Wiley like:

<img
 src="example.gif",
 srcset="example.gif 200w"
 sizes="(min-width: 400px) 400px, 100vw"
 width="200" /* <=== TA-DA! */
 class="logo">

This would be a possible solution if the width was not static and the $attr variable would not overwrite the image attributes. Still we looked further.

Custom Sizes

Well, WordPress did suggest a solution for custom sizes:

<?php
$img_src = wp_get_attachment_image_url( $attachment_id, 'medium' );
$img_srcset = wp_get_attachment_image_srcset( $attachment_id, 'medium' );
?>
<img src="<?php echo esc_url( $img_src ); ?>"
     srcset="<?php echo esc_attr( $img_srcset ); ?>"
     sizes="(max-width: 50em) 87vw, 680px" alt="A rad wolf">

But that still did not load a width based on actual image width. And that set the srcset image to a another image size which we didn’t really need. Full width was fine for us.

WordPress Image Width

We could use

$image_data = wp_get_attachment_image_src( get_post_thumbnail_id( $post->ID ), "thumbnail" ); ?>

to grab the image meta data and then the width using:

$image_width = $image_data[1];

NB Great Explanation by Jonathan here

Custom Responsive Image with Actual Width

So something like:

<?php
$alt_text = get_post_meta($post->ID, '_wp_attachment_image_alt', true);
$image_data = wp_get_attachment_image_src( get_post_thumbnail_id( $post->ID ), "thumbnail" );
$image_width = $image_data[1];
$img_src = wp_get_attachment_image_url( $attachment_id, 'full' ); 
$img_srcset = wp_get_attachment_image_srcset( $attachment_id, 'full' ); ?> 
<img src="<?php echo esc_url( $img_src ); ?>" srcset="<?php echo esc_attr( $img_srcset ); ?>" sizes="(max-width:<?php $echo image_width;?>) 100vw,<?php$ echo image_width;?>" alt="<?php echo $alt_text;?>">

NB Not tested yet!

should do the trick. This would allow a fully customized loading of the image with the actual width loaded instead of the full image width as set in the theme.

To be continued…

Quick Local WordPress Setup with Valet

Often you just want to do a quick WordPress setup locally on your Mac for trying something out. I always do a quick local WordPress setup with Valet. Laravel Valet is an application that is created by the developers behind Laravel. It can be used to run all sorts of apps and WordPress is one of them. Thanks to the Evan Mattson’s CLI Valet Command package you can use Valet to create WordPress sites with a single command.

Prerequisites

So what do you need? You need to setup Laravel Valet and for that you need Homebrew (MacOs package manager) including PHP and MariaDB. You also need Composer to install the Laravel Valet package itself.. Let’s go through settings all these up.

Homebrew

So what do you need? You need to setup Laravel Valet and for that you need Homebrew (MacOs package manager). So install Homebrew using:

/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

Homebrew Packages

Then you need to install PHP 7.2 (currently).

brew install homebrew/php/php72

And you also need to install MariaDB

brew install mariadb

Composer & Laravel Valet

Then you can install Laravel Valet with composer. Most of you using Laravel should have it already, but just in case. To install Composer do the following in a directory of your choice

php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
php -r "if (hash_file('SHA384', 'composer-setup.php') === '544e09ee996cdf60ece3804abc52599c22b1f40f4323403c44d44fdfdd586475ca9813a858088ffbc1f233e9b180f061') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;"
php composer-setup.php
php -r "unlink('composer-setup.php');"

Next step is to install the composer package for Laravel Valet

composer global require laravel/valet

WP CLI & WP CLI Valet

You should have WordPress Command Line Interface installed too. You can read about it here. But on top of that you need to install the WP Valet package. This package does all the magic of using the coolness of Laravel Valet and WP CLI. You can do the installation using this wp command:

wp package install git@github.com:aaemnnosttv/wp-cli-valet-command.git

That will get you going.

WordPress Website Installation

Once that is done you can install WordPress using Valet and Bedrock as a setup using:

wp valet new my-project --project=bedrock

 

NextGen Gallery Custom Field Rotating Images

NextGen Gallery Custom Field Rotating Images was created by us about 5 years ago for Ultimate Fiji Vacations. Since then the NextGen gallery has gone through some major changes. It has been taken over by Imagely and it has had some major upgrades.

Shortcode Changes

Some of these NextGen Gallery plugin changes were also shortcode changes. And these caused a not loading or not loading of some galleries (old post on setup) of a client of ours. We had to dig deep into the code changes to figure out what to do. We realized templates were loaded differently and that also the shortcode to load a gallery had changed. It now calles source and container_ids to load a gallery with images properly. The code

$gal = apply_filters('the_content', '[nggallery id='.$gal.' ' . $nggtemplate. ']' );
is therefore no longer applicable.

Full Code

Here is the full code to now load the gallery based on custom post type ID:
/* NG Gallery Custom Field Rotating Images */ 
$gallery = get_post_meta($post-&gt;ID, 'Gallery ID Number', true); 
$gal = $gallery; 
if( !empty($gal) ) { 
$gal = apply_filters('the_content', '[ngg_images source="galleries" container_ids=' .$gal.' display_type="photocrati-nextgen_basic_imagebrowser"]');
echo $gal;
}

As you can see the source=”” has been added and we removed the old fashioned way loading of the template as it no longer worked.

Template Loading

As the template is no longer loaded the old fashioned way and as this code allowed it to be loaded right away we decided do ditch loading a template by absolute path like so:

template="/home/user/domain.com/wp-content/plugins/nextgen-gallery/products/photocrati_nextgen/modules/ngglegacy/view/imagebrowser.php"

Instead we added the necessary JavaScript for the styling in the code where the slider was generated. This saved us the loading of another file and worked just as well!

Hostnet.nl MainWP Issues

Been having Hostnet.nl MainWP Issues lately. The MainWP Dashboard cannot connect to the child theme. Never been happy with hostnet really as they throttle RAM all the time. This causes issues doing BackupBuddy backups and even issues with Wordfence.

MainWP – Hostnet Connection Severed

But now for two months I have not been able to connect the dashboard to the child theme. I tried:

  • deactivating and activating the MainWP Child theme,
  • reconnecting multiple times, no joy

HTTP Error

I keep on getting

ERROR: HTTP error - Connection timed out after 10001 milliseconds

errors. Funnily the error nor access logs in the hostnet.nl control panel showed me little:

[Wed Feb 21 03:50:08 2018] [error] [client 216.244.66.250] cgid daemon is gone; is Apache terminating?: /usr/bin/php-cgi
[Wed Feb 21 05:20:37 2018] [error] [client 193.106.30.99] ap_pass_brigade failed with error 103: Software caused connection abort

The app_pass_bridge can be fixed by raising the PHP_FCGI_MAX_REQUESTS limit or limiting the FcgidMaxRequestsPerProcess to 500 – SO url. And frankly I am not sure if that is related to MainWP knocking on the server’s door.

SSL Errors

Then we checked the SSL error logs and found:

[Wed Feb 21 17:33:15 2018] [error] [client 141.135.85.55] ap_pass_brigade failed with error 103: Software caused connection abort, referer: https://domain.nl/en/author/author/
[Wed Feb 21 17:33:17 2018] [error] [client 141.135.85.55] ap_pass_brigade failed with error 103: Software caused connection abort, referer: https://domain.nl/en/author/domain/
[Wed Feb 21 17:33:18 2018] [error] [client 141.135.85.55] ap_pass_brigade failed with error 103: Software caused connection abort, referer: https://domain.nl/en/author/author/
[Wed Feb 21 17:33:19 2018] [error] [client 141.135.85.55] ap_pass_brigade failed with error 103: Software caused connection abort, referer: https://domain.nl/en/author/author/
[Wed Feb 21 18:28:13 2018] [error] [client 85.144.168.68] Premature end of script headers: php-cgi, referer: https://domain.nl/kamers/
[Wed Feb 21 19:08:42 2018] [error] [client 207.46.13.176] cgid daemon is gone; is Apache terminating?: /usr/bin/php-cgi
[Wed Feb 21 22:41:30 2018] [error] [client 81.206.1.201] cgid daemon is gone; is Apache terminating?: /usr/bin/php-cgi
[Thu Feb 22 01:07:16 2018] [error] [client 84.107.148.60] ap_pass_brigade failed with error 103: Software caused connection abort, referer: https://domain.nl/wp-content/themes/cosily-child/style.css?ver=a35f4bf8ee43d6c4ef1d7ba1ab5faaa4

More errors there, but nothing again on the immediate connectivity issues, but a strong indicator on server errors as ap_pass_brigade errors popup a lot.

PHP_FCGI_MAX_REQUESTS or RAM

What I mainly read upon it is that it is either an issue I can solve by raising RAM or by increasing PHP FCGI MAX REQUESTS. I opened a ticket with Hostnet.nl on it.