How Frontenberg Works

Frontenberg is a theme that lets you test out the new Gutenberg editor for WordPress. No login necessary, you just visit the site and there it is.

I’m going to document how I built it, what it is and isn’t, and the problems I encountered and overcame

Getting Gutenberg Showing

First, I had no intention of forking Gutenberg. In the same way you can call wp_editor and get a TinyMCE instance on the frontend, I wanted to do the same with Gutenberg.

So, I took a look at what the Gutenberg plugin does in its admin page. I discovered I had to do 2 things:

  • Add a root element in my markup for the Gutenberg javascript to latch on to
  • Enqueue the Gutenberg javascript

To do this, I needed to add this root element, that defines were the Gutenberg editor got loaded:

<div class="gutenberg">
<div id="editor" class="gutenberg__editor"></div>
</div>

Then enqueue the Gutenberg js code on the init action:

add_action( 'wp_enqueue_scripts', 'gutenberg_editor_scripts_and_styles' );

The Gutenberg plugin does this in its startup code, and I’ve done the same, reusing Gutenberg code from the plugin.

The Problems

This leads us to a number of problems though:

  • A logged out user can’t do anything with the editor
  • Button styles in the editor are broken
  • The media library cannot load attachments 

Fixing Button Styles

I wanted the frontend to look as close to WP Admin as possible, and this turned out to be the solution. The buttons and other areas were broken because they relied on WP Admin styling, so I added those styles:

	add_action( 'wp_enqueue_scripts', function() {
		//gutenberg.test/wp-admin/load-styles.php?c=1&dir=ltr&load%5B%5D=dashicons,admin-bar,common,forms,admin-menu,dashboard,list-tables,edit,revisions,media,themes,about,nav-menus,wp-pointer,widgets&load%5B%5D=,site-icon,l10n,buttons,wp-auth-check&ver=4.9-RC1-42056
		wp_enqueue_style('dashicons');
		wp_enqueue_style('common');
		wp_enqueue_style('forms');
		wp_enqueue_style('dashboard');
		wp_enqueue_style('list-tables');
		wp_enqueue_style('edit');
		wp_enqueue_style('revisions');
		wp_enqueue_style('media');
		wp_enqueue_style('admin-menu');
		wp_enqueue_style('admin-bar');
		wp_enqueue_style('themes');
		wp_enqueue_style('about');
		wp_enqueue_style('nav-menus');
		wp_enqueue_style('wp-pointer');
		wp_enqueue_style('widgets');
		wp_enqueue_style('l10n');
		wp_enqueue_style('buttons');
		/*wp_enqueue_style('');
		wp_enqueue_style('');
		wp_enqueue_style('');*/
	} );

I also had to include 3 files from the wp-admin folder:

require( ABSPATH.'/wp-admin/includes/class-wp-screen.php' );
require( ABSPATH.'/wp-admin/includes/screen.php' );
require( ABSPATH.'/wp-admin/includes/template.php' );

A Logged Out User Can’t Do Anything With The Editor

Clearly, I can just use the  user_has_cap to grant logged out users everything, but then this is going on a live site! I don’t want users to make edit, else I could be hosting pornography, or a defaced personal site!

Additionally, Gutenberg talks to WordPress via the REST API. This means that I can control what it’s capable of via capabilities and the REST API. Internal checks in the Gutenberg plugins PHP also force the site to bail via wp_die if certain capabilities aren’t present

So I start with that filter:

add_filter( 'user_has_cap', 'frontenberg_give_permissions', 10, 3 );

Next, I bypass the filter if the user is already logged in, afterall I still want to administrate the site:

function frontenberg_give_permissions( $allcaps, $cap, $args ) {
if ( is_user_logged_in() ) {
return $allcaps;
}

Next, I handle logged out users. I need them to be able to do some things, but I have some things I don’t want them to do:

  • I want them to edit posts
  • Be able to edit categories
  • I don’t want them to be able to save any of those edits
  • No access that could hack the site

So I modify the $allcaps array and return it, with a few modifications:

 // give author some permissions
$allcaps['read'] = true;
$allcaps['manage_categories'] = true;
$allcaps['edit_post'] = true;
$allcaps['edit_posts'] = true;
$allcaps['edit_others_posts'] = true;
$allcaps['edit_published_posts'] = true; // better safe than sorry $allcaps['edit_pages'] = false;
$allcaps['switch_themes'] = false;
$allcaps['edit_themes'] = false;
$allcaps['edit_pages'] = false;
$allcaps['activate_plugins'] = false;
$allcaps['edit_plugins'] = false;
$allcaps['edit_users'] = false;
$allcaps['import'] = false;
$allcaps['unfiltered_html'] = false;
$allcaps['edit_plugins'] = false;
$allcaps['unfiltered_upload'] = false;
return $allcaps;

Finally, this lets users edit posts but I don’t want them to be able to save changes, so I use a simple trick:

if ( ! is_user_logged_in() ) {
add_filter( 'wp_insert_post_empty_content', '__return_true', PHP_INT_MAX -1, 2 );
}

The Media Library

Now we have a working frontend instance of Gutenberg. Logged out users can modify and add to the post, but there changes are never saved. Those of you following so far will have noticed a problem when editing galleries or trying to add images though.

While I don’t want users to be able to upload images, I do want them to access existing images. Having uploaded a collection of stock images I realised nobody could use them.

It turns out the media library does not use the REST API, but uses an WP Admin AJAX action that only works for logged in users.

The solution, is to copy the wp_ajax_query_attachments function, and remove the login checks, then add it as the logged out version of that action:

add_action( 'wp_ajax_nopriv_query-attachments', 'frontenberg_wp_ajax_nopriv_query_attachments' );

Now the media library works!

The Rest

Most of the rest of the plugin is unrelated boilerplate, copied from WP Admin so that the styling makes the frontend look like the WP Admin.

I added some side menu links, and I added a function that always shows the admin bar, but removes the default admin bar items so that inaccessible links to the admin area aren’t present.

What Frontenberg Isn’t

Some may think that Frontenberg is a frontend editor fork of Gutenberg. Sure, if you are logged in and use the trick at the start of this post, you’ll have a Gutenberg instance. However, you might have other issues:

  • It always loads the current post. The frontenberg sites frontpage is what’s being edited, and if there were other pages on the site, visiting them would show those instead
  • I never figured out metaboxes, and I don’t know if custom blocks will be loaded. There aren’t many plugins I can use to test this around at the moment
  • Those plugins that use classic metaboxes such as Yoast, don’t load the metabox code on the frontend anyway. I did try to force Yoast SEO to register metaboxes on the frontend, but it was hacky and didn’t work
  • The Gutenberg plugin is needed
  • You can’t create brand new posts with my solution, that requires some extra thought
  • Styling will be broken without the WP Admin styles loaded on the frontend

Some of those, particularly styling, can likely be fixed by a future Gutenberg release. I also envisage that if you take the Gutenberg javascript and reuse the React components, you can have a Gutenberg editor that doesn’t require a post to edit

Next Steps

I’d really like to add a second page to the frontenberg site, with blocks that use post meta rather than content to store their data. There’s a lot of confusion from developers who don’t understand there’s a disconnect between the post content and the editor.

These developers might think that moving from a metabox to a metablock means that post meta is stored in post content, which is not true.

Instead, a block might have a price field, and store that price in post meta, but appear inline in a post. It could be that a post type that has no content defines a collection of meta blocks, that appear in place, but cannot be moved/added/removed. This can give non-content posts a Gutenberg interface superior to those with only meta boxes.

I’d like to be able to demonstrate that. Sadly I don’t have a good example to show right now.

So in the meantime, visit Frontenberg!