Adding some options to a block

After we have seen in the last block post, how we can use modern JavaScript to write a block, I want to show why we would really want to do that by adding some options to our block. I’ll show you only the changes we have to do.

Creating an “edit” component

As a first step, we remove the edit() function from the index.js file and place it in its own file. The index.js file also have the following content left:

import { registerBlockType } from '@wordpress/blocks';
import './editor.scss';
import './style.scss';
import Edit from './edit';
import metadata from './block.json';

registerBlockType(
	metadata.name,
	{
		edit: Edit
	}
);

We import the new Edit component and assign it to options when registering the block. We also remove the import for the ServerSideRender component, as it’s no longer needed in the index.js file.

Implementing the component

Not that we have our new file imported, let’s add the ServerSideRender component again and all the things we would need for our option. The result would look like this:

import ServerSideRender from '@wordpress/server-side-render';
import { InspectorControls } from '@wordpress/block-editor';
import { Disabled, PanelBody, RangeControl } from '@wordpress/components';
import { __ } from '@wordpress/i18n';
import { useBlockProps } from '@wordpress/block-editor';
import './editor.scss';

export default function Edit( props ) {
	const {
		attributes,
		setAttributes,
	} = props;
	const {
		postsToShow,
	} = attributes;

	return (
		<>
			<InspectorControls>
				<PanelBody
					title={ __( 'Settings', 'rpssrb' ) }
				>
					<RangeControl
						label={ __( 'Number of items', 'rpssrb' ) }
						value={ postsToShow }
						onChange={ ( value ) =>
							setAttributes( { postsToShow: value } )
						}
						min="1"
						max="100"
					/>
				</PanelBody>
			</InspectorControls>
			<div { ...useBlockProps() }>
				<Disabled>
					<ServerSideRender
						block="rpssrb/random-posts"
						attributes={ attributes }
					/>
				</Disabled>
			</div>
		</>
	);
}

OK, this is a whole lot more than we had before. But let’s look at each part to understand what’s happening here.

Step 1: Importing components

In our new code, we need some React components. We will get them from different WordPress packages. We import all of them in the lines 2-5.

Step 2: Creating a function and getting the component properties

We create a function Edit and also export it as the default function right away, so it can be used in our index.js file, as we have seen in the first code snippet. The “edit” and “save” function automatically get some properties. We can “extract” those properties we need. The attributes property is an object with the attributes to the specific block. It uses a React state to live update the UI in the block editor every time a value changes. The setAttribute property is a function that is used in combination with attributes to update the state object.

As attributes is an object as well, we can also get some values from it and assign it to separate variables. We will do that with the postsToShow attribute, we will use later.

Step 3: Implementing the inspector controls

Now that we have the attribute we want to change, we can implement the controls to adjust them. The first thing that might look a little weird is that “empty tag” <> at line 12. This is necessary as the function can only have one root tag in its return statement. We basically just wrap the rest of the JSX tags wight this empty tag.

Next, we open the <InspectorControls> component, which holds all the item in the block sidebar. Inside of that, we create a <PanelBody> to have a nice title for our option, and then we add a <RangeControl> component. The result will look like this:

The InspectorControls with a PanelBody and the title "Settings" as well as a RangeControl with the label "Number of item" with a selected value of 5.

Now we have a nice little “range slider” with which we can select the number of posts to show. With the min and max attributes of the component, we define the limits of the control. The initial value will be retrieved from the previously defined postsToShow attribute and on the onChange event, we use the setAttribues function to update the value in the state. If some of these things don’t make sense at first, you’ll get familiar with them the longer you use them.

Step 4: Rendering the block

Now that we have our controls to adjust the number of posts, we need to render them in our new Edit component. We will again use the <ServerSideRender> component, but this time we add the attributes to the tag, so that the component will refresh, every time we change the value of the posts to show.

We also wrap the component inside a <div>, so we can attach all the blog properties, and we also use a <Disabled> components, so we are able to select our block in the block editor (and not opening the permalinks instead).

Step 5: Registering the attribute for the block

Any time you use an attribute for a block, you have to register it. This is done in the block.json file. The new file looks like this:

{
	"$schema": "https://schemas.wp.org/trunk/block.json",
	"apiVersion": 2,
	"name": "rpssrb/random-posts",
	"version": "0.1.0",
	"title": "Random Posts",
	"category": "widgets",
	"icon": "smiley",
	"description": "An example for a ServerSideRender block using minimal ES6 code.",
	"supports": {
		"html": false
	},
	"textdomain": "random-posts-server-side-render-block-es6",
	"attributes": {
		"postsToShow": {
			"type": "number",
			"default": 5
		}
	},
	"editorScript": "file:./index.js",
	"editorStyle": "file:./index.css",
	"style": "file:./style-index.css"
}

Step 6: Using the options in the PHP callback function

Now that we can set the option and save it, we have to use it in our PHP callback function. But fortunately that is really easy, as it is passed automatically into the function:

function rpssrb_render_callback( $atts ) {
	$args = [
		'post_type'      => 'post',
		'orderby'        => 'rand',
		'posts_per_page' => (int) $atts['postsToShow'],
	];

	// Run the query and render the posts as in the previous blog posts ...
}

This was it! I’d suggest you to look into the different components you can use in the block editor handbook. There are many you may find really useful to build your block options. Just make sure you register those new attributes as well.

Conclusion

I know this was a lot more code than in the previous two blog posts. But once you have the basics set up for the “inspector controls” you can add new controls as you want.

As I’ve explained in the first blog post to this small series, you can also split up files as much as you want. If the inspector controls get really complex, you might want to move them to their own file. But for this example I’ve wanted to keep it simple again and only use the one edit.js file.

A first simple block with some ES6 code – it’s not as scary as it sounds

I’m currently on vacation and had some tech free days, so my blog post comes a bit later this week. In the previous post, I’ve shown you, how you can write a block without any ES6/React code. This week I want to show you the minimum you have to know to get the full potential of modern JavaScript while still keeping it simple.

Create a block with the “create-block” script

As I’ve also mentioned, that I would highly recommend using the create-block package to create your first little plugin. This will create a lot of files for you, and you are ready to go. We create the example from the last post by running this command in the wp-content/plugins folder:

npx @wordpress/create-block random-posts-server-side-render-block-es6

This will give you the following files in the newly created plugin directory:

$ tree
.
├── node_modules
├── package.json
├── random-posts-server-side-render-block-es6.php
├── readme.txt
└── src
    ├── block.json
    ├── edit.js
    ├── editor.scss
    ├── index.js
    ├── save.js
    └── style.scss

In the node_modules folder we will have all the files we would need for compiling the ES6 code so browsers can use them. The package.json defines all the dependecies that are installed as well the scripts that would run.

The block definition

The most important file in a “block plugin” is the src/block.json file. It defines the name, title, icon and other details:

{
    "$schema": "https://schemas.wp.org/trunk/block.json",
    "apiVersion": 2,
    "name": "rpssrb/random-posts",
    "version": "0.1.0",
    "title": "Random Posts",
    "category": "widgets",
    "icon": "smiley",
    "description": "An example for a ServerSideRender block using minimal ES6 code.",
    "supports": {
        "html": false
    },
    "textdomain": "random-posts-server-side-render-block-es6",
    "editorScript": "file:./index.js",
    "editorStyle": "file:./index.css",
    "style": "file:./style-index.css"
}

At the end of the file you will find three really useful lines. They define where the asset files are located. So while we had to use wp_enqueue_scripts in the last blog post, we get this all for free. No need to do that in PHP and handling the check for this file, it’s dependecies and the timestamp for caching ourselves. Just this one feature make it worth using a bit more of modern block development.

Now you could still write only ES5 code into the JavaScript files, but let’s take a look on how our simple example look like with modern JavaScript.

Registering a server side rendered block with ES6

As you have seen in the file system output above, you usually get three JS files. The index.js file will register the block. The edit.js file is used for rendering the block in the editor including controls for it’s options. The save.js will finally hold the functions to save the block content into the post content. Those two extra files can be used, but for a simple block you can also just use the index.js file. And for really complex blocks you would even split up the edit.js into smaller (reusable) parts. To keep it simple again, we will just use the index.js file:

import ServerSideRender from '@wordpress/server-side-render';
import { registerBlockType } from '@wordpress/blocks';
import './editor.scss';
import './style.scss';
import metadata from './block.json';

registerBlockType(
    metadata.name,
    {
        edit() {
            return (
                <ServerSideRender
                    block="rpssrb/random-posts"
                />
            );
        }
    }
);

In order to use components and functions in ES6, we have to import them. We are going to use the ServerSideRender component as well as the registerBlockType function. When you compare this code snippet with the ES5 one from the last blog post, you will recognize that it does not look all that different. This “weirdly looking HTML tag” is JSX, an extension to HTML that React is using to build its components. When passing parameters to those components, you are using attributes similar to HTML (instead of using a JSON object, as in the ES5 variant) and in those attributes you can even use JavaScript. For our ServerSideRender component, we only need to pass the static block name.

As you might have recognized, we are getting the name for the block in the registerBlockType function from our metadata we also import. This will give us the opportunity to get any of the declared values from this file. In this very simple example, we only need the name. We could also use this in the block attribute of the ServerSideRender component, but here we just use a static string.

The two lines I have not yet covered are the imports for the SCSS files. When you use the @wordpress/scripts package that being installed with the npx command above, you will get Sass compilation for free. You just name the two files editor.scss and style.scss and they will be compiled for either just the block editor or for the front end as well.

Now, after you have written this beautiful code, you finally have to compile it. If you only want to do it once (in a minified way, suitable for production use), you run npm run build or while actively developing you use npm run start so you always have a (verbose version, which is better for debugging) of the code you just write.

Registering the PHP callback function for the server side rendered block

As we are still writing a block that would get its content from the PHP backend, we also still have to register the callback function. It looks quite similar to our previous code. But instead of using static string, we can also use the index.js file to register the block in PHP and adding the callback function parameter:

function create_block_rpssrb_block_init() {
	register_block_type(
		__DIR__ . '/build',
		[
			'render_callback' => 'rpssrb_render_callback',
		]
	);
}
add_action( 'init', 'create_block_rpssrb_block_init' );

Instead of using the blocks name, we use the path to our build directory that is being created using the build scripts. In this folder, the PHP function will look for an index.js file and use the block name from this file.

We also use the same implementation for our callback function you can find in the previous blog post. And again you can also use the “bonus” of having a shortcode for the callback as well.

Conclusion

It’s really not that hard to create a block using ES6 syntax, and using the create-block package (or using the @wordpress/script package) make the hard part of getting the compilation running a lot easier. The only thing you have to do – and what might be a bit though – is installing node and then installing npm on your machine. Even then, you have to keep these two up to date and working with the packages you are using. This can really be annoying from time to time.

The reason why it’s worth to do all that, I will cover in my next blog post, when we add some simple controls to our block to make it more usable.

Replace a shortcode with a block using as little and simple code as possible

Earlier this week, Topher asked an interesting question on Twitter regarding the current state of block development:

Click here to display content from Twitter.
Learn more in Twitter’s privacy policy.

This made me think about I would answer this question. When I first started with block development, I primarily wrote “server side rendered blocks”. These blocks will not generate the content directly in the editor, but use some PHP code and a callback function to render the content, just as a shortcode would do.

The other difficulty with block development is the need to learn many new things: React, JSX, modern JavaScript compilation with webpack and much more. This all combined does not make it “as easy” as it was with shortcodes. But still I wanted to show, how little code you need to create a block that could possibly replace a shortcode without the need to learn all these new things.

Writing a server side rendered block – the PHP part

The easy part is registering the block and the render callback in PHP. While you would use the function add_shortcode() for a shortcode callback, you use a parameter when registering the block in PHP:

function rpssrb_init() {
	register_block_type(
		'rpssrb/random-posts',
		[
			'render_callback' => 'rpssrb_render_callback',
		]
	);
}
add_action( 'init', 'rpssrb_init' );

For this blog post, we will write a block that will render some random posts. The content of the callback would look just like with a shortcode. You can also use the same helper functions you would use for a shortcode. The callback might look like this:

function rpssrb_render_callback( $atts ) {
	$atts = shortcode_atts(
		[
			'post_type'      => 'post',
			'orderby'        => 'rand',
			'posts_per_page' => 5,
		],
		$atts
	);

	$query = new WP_Query( $atts );

	$output = '';
	if ( $query->have_posts() ) {
		$output .= '<ul>';
		while ( $query->have_posts() ) {
			$query->the_post();
			$output .= sprintf(
				'<li><a href=%s>%s</a></li>',
				get_permalink(),
				get_the_title()
			);
		}
		$output .= '</ul>';
	}

	return $output;
}

We set some default attributes and then run a WP_Query to get 5 random posts. We render them in a simple unordered list with only the title linked to the blog post.

Implementing the block in JavaScript – using ES5 code only

As mentioned before, one of the hardest parts of writing a block is need to learn React, JSX, etc. and compile this code. If you are not familiar with these technologies, it can be really hard to get started. This is why I want to show you how you can use some ES5 (“old JavaScript”) to write a block. All you really need is this:

wp.blocks.registerBlockType(
	'rpssrb/random-posts',
	{
		title: 'Random Posts',
		edit: function () {
			return wp.element.createElement(
				wp.serverSideRender,
				{
					block: 'rpssrb/random-posts'
				}
			);
		}
	}
);

We use the wp.blocks.registerBlockType function in ES5 style and only provide a title and the edit function. In this function, we create a React element with the wp.serverSideRender component that will render our block.

The edit function is used to show the content of the block (in our case the result of the callback function written in PHP). You don’t even need this, but then you would not see anything in the block editor (the block also does not take up any space as it’s empty), but it would render in the frontend.

Loading the JavaScript file

This JavaScript code has to be loaded when the block editor is used. If you already have some files being loaded for the block editor, just place it in this file. If not, you would enqueue it in the same way as usual, but with a different hook:

function rpssrb_register_scripts() {
	wp_enqueue_script(
		'random-posts-server-side-render-block',
		plugin_dir_url( __FILE__ ) . 'index.js',
		[ 'wp-blocks', 'wp-server-side-render' ],
		filemtime( plugin_dir_path( __FILE__ ) . 'index.js' ),
		true
	);
}
add_action( 'enqueue_block_editor_assets', 'rpssrb_register_scripts' );

In the dependencies, we add wp-blocks and wp-server-side-render, but they are usually loaded anyway when the block editor is used.

Bonus: use a shortcode as well

As we have written a callback function that we already know from shortcodes, we can simply add another line to make it usable as a shortcode as well (e.g. using the shortcode block):

add_shortcode( 'rpssrb_random_posts', 'rpssrb_render_callback' );

Caveat: using attributes if complicated!

The block we have built here cannot use any attributes. Adding them to the block would be easy, but implementing the controls to make them usable is not that easy. And while it’s doable using only ES5, I would not suggest doing this.

Consequence: Learn JavaScript, deeply.

As Matt has given us all as homework at WCUS 2015 I would highly suggest to start learning some modern JavaScript. The hardest part is getting started is the compilation using webpack. If you have no idea where to start, I would agree with the answer from Birgit to the tweet: use the create-block package.

The create-block package will not only help you to create a running block inside a WordPress plugin. It will also install all the JavaScript packages needed to compile the modern JavaScript. For this it’s using the @wordpress/scripts package and the main script you would use is the start script. Give it a try, read some documentation on how to add attributes and make your block even easier to use.

Conclusion: creating a block can be easy, but it might not be enough

While you can write a block as a simple replacement for a shortcode using only one PHP and one JavaScript file using ES5 syntax, I would not recommend it. Once you made yourself familiar with some basics, you can create blocks that improve usability dramatically. I know it sounds a bit intimidating, and you will run into all kinds of problems, but it’s worth it.

If you want to see the complete code of this blog post, you can find it on GitHub as a plugin.

WordCamp Europe 2022 – The community finally meets in Porto

It’s been three year. Three years after we had our last WordCamp Europe with attendees meeting in person, 2019 in Berlin. Back then I was the Local Lead and joined the next organizing team as one of three Global Leads. Then the pandemic changed the world and WordCamps became online events. While this year’s WCEU was not the first in person event, it was the biggest one, and it felt – almost – as in the past.

My first trip to Portugal

In 2020, I was supposed to visit Porto to see the venue we would use for WCEU 2020, but the pandemic hit us all, and we moved the WordCamp online and cancelled all local organizing work. Then this year in February there was another venue visit, this time for the new organizing team, but unfortunately I got COVID just some days before that. So on Monday, 30 May, I finally made it to Portugal for the first time.

Preparing the event

On Tuesday, I had the chance to get a first view of the amazing venue, the Super Bock Arena – Pavilhão Rosa Mota. The crew was already preparing the expo area and the screen for track 1:

Click here to display content from Twitter.
Learn more in Twitter’s privacy policy.

I had also the chance to meet some members of the organizing team for the first time in person and had lunch with some of them. As most things were already done or managed by the various teams, I had some time on Wednesday evening to visit the “Pirate Party” including a cruise on the river:

Click here to display content from Twitter.
Learn more in Twitter’s privacy policy.

Kicking it off with the Contributor Day

Like many other WordCamps we also had a Contributor Day organized on Thursday.

Click here to display content from Twitter.
Learn more in Twitter’s privacy policy.

The Contributor Day took place in front of the stage of track 1. We had around 800 contributors, many of them first time contributors. This was a new record for any WordCamp!

Click here to display content from Twitter.
Learn more in Twitter’s privacy policy.

The first conference day

Friday was the first day with talks and workshops. The contributing tables were replaced by seating for the talks:

Click here to display content from Twitter.
Learn more in Twitter’s privacy policy.

I had the honor to open WordCamp Europe 2022 with our Local Team Jose Freitas and the three other Global Lesley Molecke, Moncho (José Ramón Padrón García) and Taeke Reijenga:

Click here to display content from Twitter.
Learn more in Twitter’s privacy policy.

Unfortunately, I couldn’t see any talks on the first day. Only the first a half of Maja’s talk, before I had to rush out for some organizing work to be done.

Click here to display content from Twitter.
Learn more in Twitter’s privacy policy.

Just as in Berlin in 2019 we had more than just talks and workshops. We continued with the “Wellness Track”, offering yoga and meditation in the beautiful park around the venue, had the WP Café format where attendees could discuss topics, and we even had a live-streaming studio. Here we had interviews with different people from the event, which were live-streamed to the feed of track 1 in the breaks between talks. I also had the chance to be interviewed in one break:

Click here to display content from Twitter.
Learn more in Twitter’s privacy policy.

The second conference day

Saturday was the last day of WordCamp Europe. That day usually flies by pretty fast for organizers. At the end of the day we had around 2304 attendees in Porto and just before lunch many of them came together for a family photo:

Click here to display content from Twitter.
Learn more in Twitter’s privacy policy.

The last talk of WordCamp Europe 2022 was a Q&A session with Josepha and Matt:

Click here to display content from Twitter.
Learn more in Twitter’s privacy policy.

After this talk, it was time for the closing remarks. We had 91 organizers and 164 volunteers helping us making this all possible and having a great in-person event:

Click here to display content from Twitter.
Learn more in Twitter’s privacy policy.

As always, we announced the city for the next WordCamp Europe at the end of the closing remarks and welcomed the new team …

WordCamp Europe 2022 Athens

Next year, the WordPress community will meet in Athens, Greece! Another country I have not yet visited.

Click here to display content from Twitter.
Learn more in Twitter’s privacy policy.

Time to “hang my hat”

For me, this was the last WordCamp Europe as an organizer. I’ve joined the team back in 2017 and only took a break last year for the second online edition. Following the “tradition” of being a Local Lead and then a Global Lead, I will probably be speaking next year, maybe sharing the stage with my fellow Global Leads from 2020 and 2022. As much as I loved organizing all these events, it’s time to make room for new community members to step up and join the team. I’m very much looking forward to what the WCEU 2023 crew will surprise us with!

Make changes to PHP ini values without losing them on an update

If you have ever set up a server yourself, you probably realized, that the default values for PHP don’t really work for modern websites. The upload_max_filesize is set to only 2 MB, which means that you cannot upload anything into the media library of WordPress that is larger than 2 MB. This value is usually one you want to change for any site. But how can you change the value and not risking your modifications to be reverted, one you upgrade PHP or your server.

Changing the global PHP ini values

This blog is running on Ubuntu 22.04 with PHP-FPM running alongside the nginx web server. If I want to make changes to any PHP ini variable, I would make those changes to the file /etc/php/8.1/fpm/php.ini on a global level. As you can see in the file path, you would have to make these changes to the php.ini file of each installed PHP version. You would also need to do the change for any “server API”, so if you also use CGI/FastCGI for some site, you have to make the same changes to the file /etc/php/8.1/fpm/php.ini as well.

The advantage of this change is that any website hosted on this server will use these settings, so you can set good defaults for any site. But as you make changes to global configuration files, you will run into issues once you upgrade your server. When the package manager wants to upgrade the configuration file, it detects changes and asks you to either use the new file, use your modified file or to examine the changes and resolve them yourself. Depending on how many changes you have made, this might be difficult to make.

In order to modify PHP ini values globally, I have added a new file called /etc/php/conf.d/90-custom.ini with only the variables I want to change and them symlink them into the different configuration paths:

$ tree /etc/php/   
/etc/php/
|-- 7.3
|   |-- cgi
|   |   |-- conf.d
|   |   |   |-- 10-mysqlnd.ini -> /etc/php/7.3/mods-available/mysqlnd.ini
|   |   |   |-- ...
|   |   |   `-- 90-custom.ini -> /etc/php/conf.d/90-custom.ini
|   |   `-- php.ini
|   |-- cli
|   |   |-- conf.d
|   |   |   |-- 10-mysqlnd.ini -> /etc/php/7.3/mods-available/mysqlnd.ini
|   |   |   |-- ...
|   |   |   `-- 90-custom.ini -> /etc/php/conf.d/90-custom.ini
|   |   `-- php.ini
|   |-- fpm
|   |   |-- conf.d
|   |   |   |-- 10-mysqlnd.ini -> /etc/php/7.3/mods-available/mysqlnd.ini
|   |   |   |-- ...
|   |   |   `-- 90-custom.ini -> /etc/php/conf.d/90-custom.ini
|   |   |-- php-fpm.conf
|   |   |-- php.ini
|   |   `-- ...
|-- ...
|-- 8.1
|   |-- cgi
|   |   |-- conf.d
|   |   |   |-- 10-mysqlnd.ini -> /etc/php/8.1/mods-available/mysqlnd.ini
|   |   |   |-- ...
|   |   |   `-- 90-custom.ini -> /etc/php/conf.d/90-custom.ini
|   |   `-- php.ini
|   |-- cli
|   |   |-- conf.d
|   |   |   |-- 10-mysqlnd.ini -> /etc/php/8.1/mods-available/mysqlnd.ini
|   |   |   |-- ...
|   |   |   `-- 90-custom.ini -> /etc/php/conf.d/90-custom.ini
|   |   `-- php.ini
|   |-- fpm
|   |   |-- conf.d
|   |   |   |-- 10-mysqlnd.ini -> /etc/php/8.1/mods-available/mysqlnd.ini
|   |   |   |-- ...
|   |   |   `-- 90-custom.ini -> /etc/php/conf.d/90-custom.ini
|   |   |-- php-fpm.conf
|   |   |-- php.ini
|   |   `-- ...
|-- ...
|-- conf.d
|   `-- 90-custom.ini

As the new 90-custom.ini is not a file that package manager would add/update, any changes made in this file would be safe from upgrades.

Changing PHP ini values per site

Sometimes you want or need to change values for a specific site. Then using the global file would not work. If you are using the Apache web server, you might be able to change values using the Apache configuration files or even using the .htaccess file. In this file, you would use something like php_value upload_max_filesize 64M to increase the upload limit. This, although, will only work if you use PHP as an Apache module.

If you use Apache with PHP-FPM or different web server – like I am using nginx – you cannot use the .htaccess file to make any changes to the PHP ini values.

Using the “user_ini” file for per site changes

Fortunately, you have another way to change PHP ini values. This can be done with a “user_ini” file, you usually store in the document root of the site. First, you have to find out what the filename for this file should be. You can find out with the phpinfo() function or by running php -i and searching for the user_ini.filename value. You then create this file and add any changes writing it in the same way as in the php.ini files, so like upload_max_filesize = 64M to change the upload limit.

When you make changes to this file and check if they work, they probably don’t! This does not mean that, that you’ve made a mistake. It probably means that the file was not read by the PHP process. This is because the file is cached. You can find out for how long by looking at the user_ini.cache_ttl value. This is the time, in seconds, the file is cached. If the value is 300, you have to wait up to 5 minutes for changes to be used for the site. If you need the changes to be used immediately, you have to restart the PHP process (like PHP-FPM) or the web server (when you use Apache mit mod_php).

Conclusion

There are multiple ways to make changes to PHP ini variables. When you have to make changes, always make sure to use a method that would not break upgrades (or the changes getting lost with the upgrade). For my own server, I prefer to use the first approach with a global custom ini file. The user_ini file has the advantage that these value could easily be transferred to a new server when migrating a site, so they might be better for PHP systems that need specific values.

Fixing the Matomo opt-out iframe when using secure headers

Last year, I wrote a blog post about how to secure your website with server headers. When changing the security headers, there is always the chance that some things stop working, as they need “more unsecure” settings. One of these example is the Matomo opt-out iframe and in this blog post I want to show you how to solve it.

The opt-out gets blocked by the browser

If you use Matomo to track your visitors and collect statistics on how they use your site, it’s best to always use the most privacy-friendly settings. You should run the tracking without cookies and also enable the “Do Not Track” option. But even with these settings, you should allow your visitors to opt-out from tracking. To do this, you can create an iframe in the privacy settings of Matomo. This iframe is usually added to your privacy page. If you do this with secure headers set for Matomo, you might get the following error message in browsers:

Refused to display ‘https://matomo.example.com/’ in a frame because it set ‘X-Frame-Options’ to ‘sameorigin’.

This happens when the security headers are too tight not allowing a website with a different domain to embed them.

Allow external domains

One thing you could do now is changing the value of the X-Frame-Options to something more insecure. But we don’t really want that. Instead, you can explicitly allow some domains to embed the iframe. This can be done using the Content-Security-Policy header:

Apache

Header set Content-Security-Policy "frame-ancestors 'self' https://example.com/ https://www.example.com/"

nginx

add_header Content-Security-Policy "frame-ancestors 'self' https://example.com/ https://www.example.com/"

You might already have a Content-Security-Policy set in your server headers. In this case, you can also add these options to the existing header. If you don’t know how to combine different options, the generator from Report UI may be you with this.

Conclusion

Securing a website is very important. But sometimes a higher level of security can interfere with other things. Always make sure to test all things after changing something on the server headers. If visitors are unable to opt-out from tracking, you might have solved a potential security issue, but now you have a privacy issue.

Allow co-editing of content by multiple users

WordPress comes with different roles, and it’s usually best not to give everyone the “Administrator” role. The next best role would be the “Editor” role, which enables a user to edit all contents, their own one and the content from other users. But in some cases, this role is also too permissive.

Allow a user to edit their content only

When you want to limit a user to edit only the posts and pages they have created, assign them to the “Author” role. With this role, they can see content of user users, but they cannot edit them. This is perfect, if you have a page where you want to invite many external users to edit “their pages”, maybe a page with a profile of their own company. They can either create this profile themselves, or you would create it and assign them as authors to that profile page.

But sooner or later you have the request from one of these users, that they want to have another person being able to edit this page as well. As WordPress can only have one author assigned to a page, this is not easily possible and as you probably don’t want to ask them to share the login password, we need a better solution for this.

The “Co-Authors Plus” plugin to the rescue

Through a tweet mentioning some plugins someone was regularly using, I found the plugin Co-Authors Plus, which has been around for many years, which tackles this need. It allows you to add more than one author to a post or page. The first author in the list will be shown at the single view of the post, but the plugin also offers functions to show the other authors as well (be adding those functions to a child-theme template).

Those different authors can even have different roles. But any user, who is either the primary/first author or an additional author, can edit the page, as long as their role would allow them to do so.

As WordPress usually stores the author of a post or page in a single column in the wp_posts table I was curious to see, how they solved it, and found out, that they use a custom post type to assign the authors to a post.

Conclusion

While the basic roles of WordPress are usually enough for various requirements, the issue of co-editing was always something, I was wishing for a core solution. But with the addition of this plugin (which still gets updates and even integrates nicely into the block editor and “Quick Edit”), this can also be established quite easily.

Overcome the “ERROR 1153” when importing large databases

Migrating a WordPress site is something I do a lot on a weekly basis. Many migrations happen between local environments or production and staging. The source and target systems don’t necessarily have the same settings. From time to time you have to migrate a site with a large database. In these cases, you might run into this error:

ERROR 1153 (08S01) at line 56: Got a packet bigger than 'max_allowed_packet' bytes

This can happen when a single INSERT statement on the import is too large. In this blog post, I want to give you tips on how to import the database.

Create a new backup with smaller imports

When you export your databases, you probably use either the mysqldump command, the wp db export or a database management tool such as Adminer or phpMyAdmin. In the default settings, they will create INSERT statements that would not only import a single line into a table, but multiple of them. This is usually a good strategy, as it speeds up the import. But if one of those statements become too long, you will run into the error.

To solve it, you can try to create an export of the database, that only has one single row in any INSERT statement. This will make the import run longer, but it’s more likely it succeeds.

To create an export like this, you can use the WP-CLI with some additional mysqldump arguments:

wp db export --extended-insert=FALSE --complete-insert=TRUE

This should create such an export file. Now you should hopefully be able to import that database dump.

Increase the “max_allowed_packet” value

If you are still unable to import the database dump file, you probably have to increase the value of “max_allowed_packet”. The default value can usually only be changed by the server administrator. But you can often increase the value temporarily. For this, you connect to the MySQL server, increase the value and import the file. I will use the wp db cli command from the WP-CLI. This will connect me to the correct database of my WordPress installation. Now I can run the commands:

SET GLOBAL max_allowed_packet=1*1024*1024*1024;
SOURCE db_dump_filename.sql;

The first command will set the variable globally to 1 GB. Unlike in the MySQL configuration file, you can not use a value of “1G” in this statement, but you have to use a number in bytes. But as you can also use a calculation while setting the value, in the above notation it’s easier to see, what the value in bytes will be.

Conclusion

Importing large databases can be an issue. But with these tips you can hopefully import it yourself. If not, ask a server administrator to help you out. They probably have tools to do the import for you. And if you yourself write data into the database, please make sure rows don’t get too large.

How to remove the “update lock” after a broken WordPress update

Updating WordPress and it’s plugins and themes is usually straightforward. You navigate to the “Dashboard | Updates” and update the components. If an error happens while updating WordPress Core or plugins/themes, WordPress can usually handle this and roll back, but sometimes things go wrong.

Failed updates break the site

If the update fails, your site might end up being broken. In this case, you will probably get the following message:

Briefly unavailable for scheduled maintenance. Check back in a minute.

When WordPress performs updates for core (or multiple plugins), it will enable a maintenance mode and inserts a lock into the database.

Repairing the site

The two locks will be automatically removed after 15 minutes. As it will not only break the backend, but also the frontend of your site, you probably don’t want to wait 15 minutes.

Deactivating the maintenance mode

The first thing you can do is searching for the file .maintenance file in the root folder of your WordPress installation. This file contains a single line of PHP code with the UNIX timestamp on when the installation process of the update started:

<?php $upgrading = 1648994440; ?>

If you delete this file, the message will disappear and WordPress will show the frontend and backend of your site again.

Resuming the previous update

Even after removing the .maintenance file, you can’t continue updating WordPress core. If you try to update it right away, you will probably see the following message:

Another update is currently in progress.

This is caused by the core_updater.lock option WordPress sets when starting the core update process:

$ wp option get core_updater.lock
1648994423

As you can see, it has a slightly earlier time (it’s set before the ZIP is being downloaded and extracted). When you are sure that the previous core update process really stopped, you can remove this option either using a database management tool or with the WP-CLI:

$ wp option delete core_updater.lock
Success: Deleted 'core_updater.lock' option.

Now you should be able to restart the update process, this time hopefully without an error causing your site to break again.

Conclusion

While WordPress updates usually don’t cause issues and run quite smoothly, an error can always happen. In these cases, you should know how to quickly get your site working again and not having it broken for 15 minutes.

Repair a broken Git Repository file system

Some weeks ago, I had quite an unusual issue with Git. In a repository I hadn’t used for quite a while, I simply wanted to see, if I had any changes, so I ran a git status on it with the following result:

$ git status
error: object file .git/objects/6e/eab7d4770c705a0491cafbc95830af69d5c6a2 is empty
error: object file .git/objects/6e/eab7d4770c705a0491cafbc95830af69d5c6a2 is empty
fatal: loose object 6eeab7d4770c705a0491cafbc95830af69d5c6a2 (stored in .git/objects/6e/eab7d4770c705a0491cafbc95830af69d5c6a2) is corrupt

I’ve never experienced that before. Fortunately, Git offers some commands to check a Git repository, so I did a file system check:

$ git fsck --full
error: object file .git/objects/6e/eab7d4770c705a0491cafbc95830af69d5c6a2 is empty
error: unable to mmap .git/objects/6e/eab7d4770c705a0491cafbc95830af69d5c6a2: No such file or directory
error: 6eeab7d4770c705a0491cafbc95830af69d5c6a2: object corrupt or missing: .git/objects/6e/eab7d4770c705a0491cafbc95830af69d5c6a2
Checking object directories: 100% (256/256), done.
error: object file .git/objects/6e/eab7d4770c705a0491cafbc95830af69d5c6a2 is empty
error: object file .git/objects/6e/eab7d4770c705a0491cafbc95830af69d5c6a2 is empty
fatal: loose object 6eeab7d4770c705a0491cafbc95830af69d5c6a2 (stored in .git/objects/6e/eab7d4770c705a0491cafbc95830af69d5c6a2) is corrupt

This gave me a bit more verbose information that one object was corrupt, but still no help in how to solve it, which Git usually gives you when using a command.

The Solution

With this new information, I was able to find a solution on Stack Overflow. I just had to delete the corrupt/empty file. In my case, it was really just this one file. In other repositories, there might be multiple files. To quickly delete them all, we can search the Git folder for all “empty” files and delete them:

$ find .git/objects/ -type f -empty | xargs rm

After this command, all corrupt files are missing from the repository. We can get them back by fetching the data from the remote:

$ git fetch -p
remote: Enumerating objects: 228, done.
remote: Counting objects: 100% (228/228), done.
remote: Compressing objects: 100% (91/91), done.
remote: Total 210 (delta 121), reused 188 (delta 99), pack-reused 0
Receiving objects: 100% (210/210), 90.23 KiB | 2.65 MiB/s, done.
Resolving deltas: 100% (121/121), completed with 11 local objects.

Finally, we make another file system check to see if all errors are gone:

$ git fsck --full
Checking object directories: 100% (256/256), done.
Checking objects: 100% (221/221), done.
dangling blob c5446110414804bbba2a5316a3e996ff37666bb9
dangling blob 45dd1301284105bcfc7e183bc805b65bf1465f47
dangling blob 70376fcbe5060d0db11490249bed5b553c0d04cc

Conclusion

Usually, Git gives us quite useful error messages, when we do something wrong. In this case I had to research a bit but fortunately was not the first one to encounter this issue.

Our fix only worked without any losses, because we were able to fetch the deleted corrupt/empty objects from a remote. This is why I always advise people to always have their code in a remote repository as well, and commit and push often.