Wordpress

How to ‘wp package install’ in wpcli docker container

Using docker-compose with a config such as

# ...
  wpcli:
    depends_on:
      - wordpress
    image: wordpress:cli
    user: 1000:1000
    command: tail -f /dev/null
    volumes:
      - ./wordpress:/var/www/html
    environment:
        - WORDPRESS_DB_HOST=mariadb:3306
        - WORDPRESS_DB_USER=wordpress
        - WORDPRESS_DB_NAME=wordpress
        - WORDPRESS_DB_PASSWORD=${MARIADB_PASSWORD}

You need to install the package in the container as root to avoid

Error: Composer directory '/.wp-cli/packages' for packages couldn't be created: mkdir(): Permission denied

Use the following command to install a package

docker-compose exec -u root wpcli wp package install wp-cli/profile-command:@stable --allow-root
Posted by Uli Köhler in Docker, Wordpress

How to change admin password using wpcli on docker-compose

docker-compose exec wpcli wp user update admin --user_pass='ee7saeNg9Moh5shoodocaixaingoov'

This works, for example with a docker-compose wpcli config like:

wpcli:
    depends_on:
      - wordpress
    image: wordpress:cli
    user: 1000:1000
    command: tail -f /dev/null
    volumes:
      - ./wordpress:/var/www/html
    environment:
        - WORDPRESS_DB_HOST=mariadb:3306
        - WORDPRESS_DB_USER=wordpress
        - WORDPRESS_DB_NAME=wordpress
        - WORDPRESS_DB_PASSWORD=${MARIADB_PASSWORD}

 

Posted by Uli Köhler in Docker, Wordpress

How to force-reinstall WordPress plugin using wpcli (with or without Docker)

Without docker:

wpcli wp plugin install [plugin-name] --force

With docker:

docker-compose exec -e HOME=/tmp --user 33:33 wpcli wp plugin install [plugin-name] --force

 

Posted by Uli Köhler in Wordpress

How to fix docker wpcli Warning: Failed to create directory “/var/www/html/wp-content/upgrade/…”

Problem:

When trying to update a plugin or similar action using the wordpress:cli wpcli docker image, for example using a command such as

docker-compose exec wpcli wp plugin update google-sitemap-generator

you see an error message such as

Warning: Failed to create directory. "/var/www/html/wp-content/upgrade/google-sitemap-generator.4.1.16"
+--------------------------+-------------+-------------+--------+
| name                     | old_version | new_version | status |
+--------------------------+-------------+-------------+--------+
| google-sitemap-generator | 4.1.13      | 4.1.16      | Error  |
+--------------------------+-------------+-------------+--------+
Error: No plugins updated (1 failed).

Solution:

This error occurs because the wordpress image (without :cli!) is based on Debian and the wordpress:cli image is based on Alpine Linux. Debian uses the UID 33 for the www-data user whereas Alpine Linux uses 83. So to fix the permission problem, you need to force the cli image to use 33:

This is documented on the wordpress docker page.

docker-compose exec -e HOME=/tmp --user 33:33 wpcli wp plugin update google-sitemap-generator

 

Posted by Uli Köhler in Docker, Wordpress

How to check WordPress site integrity using wpcli

Check the integrity of the WordPress core:

wp core verify-checksums

Check the integrity of all plugins:

wp plugin verify-checksums --all

docker-compose variant

docker-compose exec wpcli wp core verify-checksums
docker-compose exec wpcli wp plugin verify-checksums --all

 

Posted by Uli Köhler in Wordpress

How to check database connection using wpcli

wpcli wp db check

Or, with docker-compose, if you have a wpcli container:

docker-compose exec wpcli wp db check

Example output:

wordpress.wp_commentmeta                           OK
wordpress.wp_comments                              OK
wordpress.wp_icl_content_status                    OK
wordpress.wp_icl_core_status                       OK
wordpress.wp_icl_flags                             OK
wordpress.wp_icl_languages                         OK
wordpress.wp_icl_languages_translations            OK
wordpress.wp_icl_locale_map                        OK
wordpress.wp_icl_message_status                    OK
wordpress.wp_icl_mo_files_domains                  OK
wordpress.wp_icl_node                              OK
wordpress.wp_icl_reminders                         OK
wordpress.wp_icl_string_batches                    OK
wordpress.wp_icl_string_packages                   OK
wordpress.wp_icl_string_pages                      OK
wordpress.wp_icl_string_positions                  OK
wordpress.wp_icl_string_status                     OK
wordpress.wp_icl_string_translations               OK
wordpress.wp_icl_string_urls                       OK
wordpress.wp_icl_strings                           OK
Success: Database checked.

 

Posted by Uli Köhler in Wordpress

How to fix WPCLI Error: Role doesn’t exist: admin

Problem:

While trying to create a user using wpcli using a command such as

wp user create uli [email protected] --role=admin --user_pass=abc123abc

you see the following error message:

Error: Role doesn't exist: admin

Solution:

Run

wp role list

to list all roles. By default, these roles are:

+---------------+---------------+
| name          | role          |
+---------------+---------------+
| Administrator | administrator |
| Editor        | editor        |
| Author        | author        |
| Contributor   | contributor   |
| Subscriber    | subscriber    |
| SEO Manager   | wpseo_manager |
| SEO Editor    | wpseo_editor  |
+---------------+---------------+

You have to use the value from the second column in the wp user create command.

For example, in order to create an admin user, use --role=administrator, e.g.:

wp user create uli [email protected] --role=administrator --user_pass=abc123abc

 

Posted by Uli Köhler in Wordpress

How I fixed WordPress category page title: Category <span>…</span>

One one of my WordPress sites, the title of category pages was literally

Category <span>My category</span>

Searching through the theme source code didn’t yield a solution quickly, so I added this custom JS using a plugin:

jQuery(document).ready(function( $ ){
    if(jQuery("h1.page-title").text().includes("Category: <span>")) {
        var input = jQuery("h1.page-title").text();
        var output = /<span>(.*)<\/span>/g.exec(input)[1];
        jQuery("h1.page-title").text(output);
    }
});

This will just replace the title by the content of the span tag. You might need to adjust the CSS classes for your theme.

Posted by Uli Köhler in Javascript, Wordpress

How to setup & use WordPress REST API authentication using Python

WordPress authentication plugin setup

First, install the WordPress REST API Authentication wordpress plugin, which you can find by searching for WordPress REST API Authentication:

Then you need to open the plugin configuration page. Open Plugins in the WordPress admin panel, locate the  WordPress REST API Authentication plugin and click Configure

Select Basic Authentication:

Then click Next on the top right:

and click Finish on the next page:

Setup in Python

Assuming you have a WordPress user admin with password abc123 we can modify our code from How to get WordPress posts as JSON using Python & the WordPress REST API in order to query a non-public endpoint:

import requests
import base64

# Compute basic authentication header
auth_header = b"Basic " + base64.b64encode(b"admin:abc123")

# posts is a list of JSON objects, each representing a post
posts = requests.get("https://mydomain.com/wp-json/wp/v2/posts",
                     params={"context": "edit"},
                     headers={"Authorization": auth_header}).json()

 

Posted by Uli Köhler in Python, Wordpress

WordPress backup script using bup (bup remote)

This script backups a WordPress installation (including data,base files & directories, excluding cache) to a bup remote server running on 10.1.2.3. You need to ensure passwordless access to that server.

It is based on automated extraction of database host, username & password, see How to grep for WordPress DB_NAME, DB_USER, DB_PASSWORD and DB_HOST in wp-config.php for more details.

#!/bin/bash
export NAME=$(basename $(pwd))
export BUP_DIR=/var/bup/$NAME.bup
export REMOTE_BUP_DIR=/bup-backups/$NAME.bup
export REMOTE_SERVER=10.1.2.3
export BUP_REMOTE=$REMOTE_SERVER:$REMOTE_BUP_DIR

# Init
bup -d $BUP_DIR init -r $BUP_REMOTE
# Save MariaDB dump (extract MariaDB config from wp-config.php)
DB_NAME=$(grep -oP "define\(['\"]DB_NAME['\"],\s*['\"]\K[^'\"]+(?=[\'\"]\s*\)\s*;)" wp-config.php)
DB_USER=$(grep -oP "define\(['\"]DB_USER['\"],\s*['\"]\K[^'\"]+(?=[\'\"]\s*\)\s*;)" wp-config.php)
DB_PASSWORD=$(grep -oP "define\(['\"]DB_PASSWORD['\"],\s*['\"]\K[^'\"]+(?=[\'\"]\s*\)\s*;)" wp-config.php)
DB_HOST=$(grep -oP "define\(['\"]DB_HOST['\"],\s*['\"]\K[^'\"]+(?=[\'\"]\s*\)\s*;)" wp-config.php)
mysqldump -h$DB_HOST -u$DB_USER -p$DB_PASSWORD $DB_NAME | bup -d $BUP_DIR split -n $NAME-$DB_NAME.sql

# Save wordpress directory
bup -d $BUP_DIR index --exclude wp-content/cache --exclude wp-content/uploads/cache . && bup save -r $BUP_REMOTE -9 --strip-path $(pwd) -n $NAME .

# OPTIONAL: Add par2 information
#   This is only recommended for backup on unreliable storage or for extremely critical backups
#   If you already have bitrot protection (like BTRFS with regular scrubbing), this might be overkill.
# Uncomment this line to enable:
# bup on $REMOTE_SERVER -d $REMOTE_BUP_DIR fsck -g

# OPTIONAL: Cleanup old backups
bup on $REMOTE_SERVER -d $REMOTE_BUP_DIR prune-older --keep-all-for 1m --keep-dailies-for 6m --keep-monthlies-for forever -9 --unsafe

 

Posted by Uli Köhler in bup, Wordpress

How to get all WordPress posts as JSON using Python & the WordPress REST API

In our previous post How to get WordPress posts as JSON using Python & the WordPress REST API we showed how to fetch a single page of 10 posts using the WordPress REST API in Python.

In this post, we’ll use the pagination in order to fetch a list of all the posts.

Firstly, we observe that once we query an invalid page such as ?page=1000000, the returned JSON will be

{'code': 'rest_post_invalid_page_number',
 'message': 'The page number requested is larger than the number of pages available.',
 'data': {'status': 400}}

instead of the JSON array representing the list of posts.

Using this information, we can write a fetcher that fetches pages of 100 posts each until this error message is encountered:

from tqdm import tqdm
import requests

def page_numbers():
    """Infinite generate of page numbers"""
    num = 1
    while True:
        yield num
        num += 1

posts = []
for page in tqdm(page_numbers()):
    # Fetch the next [pagesize=10] posts
    posts_page = requests.get("https://mydomain.com/wp-json/wp/v2/posts", params={"page": page, "per_page": 100}).json()
    # Check for "last page" error code
    if isinstance(posts_page, dict) and posts_page["code"] == "rest_post_invalid_page_number": # Found last page
        break
    # No error code -> add posts
    posts += posts_page

 

 

Posted by Uli Köhler in Python, Wordpress

How to get WordPress posts as JSON using Python & the WordPress REST API

You can use the requests library to fetch the posts as JSON from /wp-json/wp/v2/posts

On the wordpress site, you typically don’t need to configure this – the JSON API is enabled by default and accessing publically available info can be done without authentication.

import requests

# posts is a list of JSON objects, each representing a post
posts = requests.get("https://mydomain.com/wp-json/wp/v2/posts").json()

This will, by default, fetch the most recent 10 posts. See TODO for more info on how to fetch all posts using pagination

Posted by Uli Köhler in Python, Wordpress

Free wordpress plugin for searching categories while editing posts & pages

WP Admin Category Search is a free search plugin for searching categories while editing your posts or pages. It allows you to quickly find categories without scrolling through a long list of categories:

Step 1: Install WP Admin Category Search

Go to Plugins -> Install and search for WP Admin Category Search. Install and activate it by clicking the button

Step 2: When editing posts, search for categories…

Step 3: … and find and select them

Posted by Uli Köhler in Wordpress

Recommended local Google Font hosting plugin for WordPress

I tested different local Google Fonts (GPDR) plugins and for some websites especially with Elementor/WPBakery etc, out of all plugins tested, only the OMGF (optimize my google fonts) plugin really worked in removing all fonts for GPDR compliance.

Therefore, I can recommend installing OMGF specifically, even though most other plugins like Self hosted Google Fonts will work for most websites.

You can installing it by opening the WordPress admin panel, clicking Plugins, clicking Install, and then entering OMGF in the search field.

Posted by Uli Köhler in Wordpress

How to fix WordPress docker image upload size 2M limit

Problem:

You are running your WordPress instance using the official WordPress Apache image.

However, the WordPress Media page has a maximum upload size of 2 Megabytes.

Solution:

This setting is configured in the php.ini used by the WordPress docker image internally. While it is possible to use a custom php.ini, it’s much easier to edit .htaccess . Just edit .htaccess in the wordpress directory where wp-config.php is located and append this after # END WordPress to set the upload limit to 256 Megabytes:

php_value upload_max_filesize 256M
php_value post_max_size 256M
php_value max_execution_time 300
php_value max_input_time 300

The change should be effective immediately after reloading the page. Note that you still might need to configure your reverse proxy (if any) to allow larger uploads. My recommendation is to just try it out as is and if large uploads fail, it’s likely that your reverse proxy is at fault.

Full .htaccess example:

# BEGIN WordPress
# Die Anweisungen (Zeilen) zwischen „BEGIN WordPress“ und „END WordPress“ sind
# dynamisch generiert und sollten nur über WordPress-Filter geändert werden.
# Alle Änderungen an den Anweisungen zwischen diesen Markierungen werden überschrieben.
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
RewriteBase /
RewriteRule ^index\.php$ - [L]
RewriteRule ^en/wp-login.php /wp-login.php [QSA,L]
RewriteRule ^de/wp-login.php /wp-login.php [QSA,L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
</IfModule>

# END WordPress

php_value upload_max_filesize 256M
php_value post_max_size 256M
php_value max_execution_time 300
php_value max_input_time 300

 

Posted by Uli Köhler in Container, Docker, Wordpress

How to set X-Forwarded-Proto header in nginx

Directly after any proxy_pass line add

proxy_set_header X-Forwarded-Proto $scheme;

Typically X-Forwarded-Proto is used together with X-Forwarded-Host like this:

proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Proto $scheme;

 

Posted by Uli Köhler in Networking, nginx, Wordpress

How to allow ALL filetypes for Media upload in WordPress

On your own blog or website, you often don’t care about the WordPress upload restriction and just want to be able to upload all filetypes, to just upload any filetype.

In order to do that, append this line to wp-config.php:

define('ALLOW_UNFILTERED_UPLOADS', true);

and then, log out from WordPress and log back in again.

After that, you should be able to upload any file, no matter its file type.

Posted by Uli Köhler in Wordpress

How to grep for WordPress DB_NAME, DB_USER, DB_PASSWORD and DB_HOST in wp-config.php

This grep statement filters out the DB_NAME, DB_USER, DB_PASSWORD and DB_HOST contents from a wp-config.php:

grep -oP "define\(['\"]DB_NAME['\"],\s*['\"]\K[^'\"]+(?=[\'\"]\s*\)\s*;)" wp-config.php

For example, from the wp-config.php line

define('DB_NAME', 'techoverflow');

it will extract techoverflow.

Here are the statements for DB_NAME, DB_USER, DB_PASSWORD and DB_HOST:

DB_NAME=$(grep -oP "define\(['\"]DB_NAME['\"],\s*['\"]\K[^'\"]+(?=[\'\"]\s*\)\s*;)" wp-config.php)
DB_USER=$(grep -oP "define\(['\"]DB_USER['\"],\s*['\"]\K[^'\"]+(?=[\'\"]\s*\)\s*;)" wp-config.php)
DB_PASSWORD=$(grep -oP "define\(['\"]DB_PASSWORD['\"],\s*['\"]\K[^'\"]+(?=[\'\"]\s*\)\s*;)" wp-config.php)
DB_HOST=$(grep -oP "define\(['\"]DB_HOST['\"],\s*['\"]\K[^'\"]+(?=[\'\"]\s*\)\s*;)" wp-config.php)

You can use this, for example, in automatic backup scripts without manually copying the password to the backup script

Posted by Uli Köhler in PHP, Wordpress

How to fix “OTGS Installer, responsible for receiving automated updates for WPML and Toolset, requires the following PHP component(s) in order to function:cURL” on Ubuntu 20.04

 Problem:

Your WordPress installation running on Ubuntu 20.04 shows you this error message:

OTGS Installer, responsible for receiving automated updates for WPML and Toolset, requires the following PHP component(s) in order to function:cURL

Learn more: Minimum WPML requirements

Solution:

You need to install the curl package for your PHP version.

Typically you can use

sudo apt -y install php7.4-curl

Doing this should immediately fix the issue (just reload your WordPress dashboard). In case you’re using PHP 7.2 you need to sudo apt -y install php7.2-curl instead.

In case the issue is still not fixed after doing this, try restarting your webserver (e.g. sudo service apache restart or sudo service nginx restart), try restarting PHP-FPM by using sudo service php7.4-fpm restart or sudo service php7.2-fpm restart or try rebooting your server. In case that doesn’t help either, you need to manually check which PHP instance the wordpress site is using and install the curl package for that PHP instance.

 

Posted by Uli Köhler in PHP, Wordpress