Things I’m learning while playing with Gutenberg (part I)

I’ve been having fun working heavily with Gutenberg over the last several weeks. As I ran into a couple things, I started tracking them in a draft post so I could work through and publish them later.

Here’s the first set. ūüôā

How to access className from a block’s save() function

The classname¬†attribute is available to a block’s¬†edit()¬†function by default, but it is not¬†available to the block’s save()¬†function. I learned from this GitHub issue that registering className¬†as one of the block’s attributes will provide it to the save()¬†function.

registerBlockType( 'my/block', {

	attributes: {
		className: {
			type: 'string',
			default: '',
		},
	},

	edit( props ) {
		// className via props
		const { attributes, className } = props;
		const { customData } = attributes;

		return ( // ... );
	},

	save( { attributes } ) {
		// className via attributes 
		const { className, customData } = attributes;

		return ( // ... );
	},
} );

Be careful importing Lodash modules

If you want to use something from Lodash, like find():

// Import the module like this
import find from 'lodash/find';

// Not like this...
import { find } from 'lodash';

The latter brings in the global _ from Lodash, which overrides the global _ provided by Underscore and causes trouble.

A this.activateMode is not a function error that prevented media blocks from being used led me to this issue, which helped me.

Beware of non-changing arrays

I ran into this a couple different ways recently and I’m still working on understanding it completely, but things like Array().push and Array().pop modify the reference value of an existing array and may not trigger the state change you’re expecting.

let apples = [];
let oranges = apples;

oranges; // []

apples.push( 1 );
oranges; // [ 1 ]

oranges === apples; // true
oranges === apples.concat( 1 ); //false

I picked up on better approaches for this via this issue when a block’s attributes weren’t saving as expected.

Remove an administrator from 58 networks at once with WP-CLI

Ok, so this is super specific and probably won’t come in handy for you. But it’s another example of how quickly you can perform a task using WP-CLI.

At WSU, we currently have 58 networks configured in WordPress multisite.

  • Global administrators are users that have been set under the site_admins¬†option on network 1.
  • Network administrators are users that have been set under the site_admins¬†option on any other network.

Depending on the page view, these are used to make up what WordPress validates as is_super_admin(). See WordPress core ticket #37616 for an eye into how this will get a lot better soon.

One of our excellent interns has graduated and we needed to remove him from ~25 different networks. Because I rushed the original UI on adding network administrators, there’s no great way to remove them.

Enter WP-CLI:

wp site option list --field=site_id --search=site_admins | xargs -n1 -I % wp eval 'if ( $sa = get_network_option( %, "site_admins" ) ) { if ( $tk = array_search( "old.username", $sa ) ) { unset( $sa[ $tk ] ); update_network_option( %, "site_admins", $sa ); } }'

WP-CLI isn’t really multi-network aware out of the box, so the wp site option list¬†command will output all options for all networks. We can use this to our advantage by having only site_id¬†output for only the site_admins¬†option on each network.

We then pipe this to xargs and use wp eval to run arbitrary PHP code. This code retrieves the current site_admins data for the given network, removes the old user from the array, and then updates the network option with the new value.

This is a prime candidate for packing into some code and turning it into an actual command‚ÄĒwp network admin remove or something‚ÄĒbut it does the trick for now!

And if we haven’t written a better UI or WP-CLI command by the time somebody else needs to be removed, then referring to this post and using the command will take 10 seconds instead of the 5 minutes it ¬†took to make it in the first place. ūüôā

Update: Daniel pointed out wp super-admin, which I hadn’t noticed before. This will take a --url¬†argument, so if you know a URL on the network, then you can pass that and it will work. There’s no great way (that I know of yet) to generate list of URLs that covers all networks, but I started an issue on the WP-CLI repo to start figuring that out.

Add users from one site to another on multisite by role with WP-CLI

Today I wanted to make sure a bunch of editors from one site existed as editors of a new staging site that we’re building out. Both sites exist as part of the same multisite network.

Thanks to WP-CLI and xargs, this is pretty straight forward:

wp user list --role=editor --url=prod.site.edu --field=user_login | xargs -n1 -I % wp --url=stage.site.edu user set-role % editor

This tells WP-CLI to list only the user_login field for all of the editors on prod.site.edu. It then passes this list via pipe to xargs, which runs another wp command that tells WP-CLI to set the role of each user as editor on stage.site.edu.

Because users are already “created” at the global level in multisite, they are added to other sites by setting their role with wp user set-role.

I’d estimate that with a list of 15 users, this probably saved closed to 15 minutes and didn’t require a whole bunch of clicking and typing with two browser windows open side by side.

Props to Daniel’s runcommand post for providing an easy¬†framework.