Half-Elf on Tech

Thoughts From a Professional Lesbian

Author: Ipstenu (Mika Epstein)

  • ES5, ESNext, and a Headache

    ES5, ESNext, and a Headache

    You may have noticed, reading the Gutenberg documentation, that there are two ways to add new blocks. There’s ES5 and ESNext. The two code bases are similar, but they certainly can confuse new developers. And while it’s possible to migrate from one to the other, that too can be a bit of a headache.

    Consistently naming versions? HAH!

    Providing a consistent naming pattern is important for people to be able to understand what version of a software they’re using, and what’s next. Whatever you pick, when you decide how to go. you’re pretty much stuck with it forever. There are exceptions, but even Apple and Microsoft had semi-logical explanations for their names. I can’t really justify Windows ME to anyone, though.

    In Open Source land, people love to complain that WordPress itself doesn’t use semantic versioning (aka SemVer). That is, a jump from 4.2 to 4.3 is a major release, where as if it were SemVer, that would be a minor change.

    SemVer uses the concept of MAJOR.MINOR.PATCH with regards to numbers, which means you increment:

    1. MAJOR – when you make incompatible API changes,
    2. MINOR – when you add functionality in a backwards-compatible manner
    3. PATCH – when you make backwards-compatible bug fixes

    WordPress does MAJOR.MAJOR.MINOR-OR-PATCH which really confuses a lot of people, and I understand that.

    Of course, then you look at the history of JavaScript and you cry a little.

    JavaScript isn’t Java, and neither are coffee.

    We have to go back a while here. In 1995, Netscape Navigator was releasing a new coding language called LiveScript. In 1996 they renamed it to JavaScript, presumably to capitalize on the whole ‘Java’ craze. I actually took some Java classes back in those days. Anyway, Netscape tossed the deal over to ECMA International for some standardization, and got us ECMAScript.

    ECMAScript is the language, Javascript is the most popular implementation of the language. Its like HTML and XHTML, and when you get down to brass tacks, most people don’t care. They use the terms interchangeably. And that’s okay.

    From 1996 to around 2010, nothing changed. Javascript trucked along doing what it did, and ECMAScript didn’t change much at all. There was ECMAScript2 and ECMAScript3, but after that, we had a decade of nothing. The astute reader now has gone “ECMAScript… ECMA Script. ES?” And they would be correct.

    What is ESNext?

    ECMAScript 5 (aka ES5) came out in 2009, but really it didn’t get picked up until 2012. This is because of our old nemesis, Internet Explorer. In the last 6 years, developers have pretty much stuck to ES5, since it works in all modern browsers. We have ES6, also known as ES2015, but not every browser supports it yet. Which is why we have ESNext.

    To put it simply, ESNext (or ES.Next) is the future version of ECMAScript which is yet to be released. If you hear ES7 or ES2016, that’s actually the same thing. The naming system is a little janky and confusing, if you hadn’t already noticed.

    And this is why you’ll hear it called ESNext in WordPress. It encompasses ES6/ES2015, ES7/ES2016, and whatever comes next. Aaaaaah you see? 

    ES4 was abandoned by the way.

    It’s Not Dangerous.

    While not all browsers support ESNext, there’s good news for WordPress. It doesn’t matter because we transpile (I’ll get there in a second). For WordPress and Gutenberg, the primary difference is going to be in the ‘style’ of code and the build process. The break down is as follows:

    • ES5 is more obscure to write, but runs immediately
    • ESNext is more clean to write, but requires post compilation (transpilation) to run Gutenberg

    That probably didn’t help. Okay, how about this. There a way to write CSS (called SASS) which lets you add programatic features to your CSS. ES5 and ESNext are the same way. You totally can write ESNext without compiling, but not yet for Gutenberg. This is kind of the same thing, except when we transpile ESNext, we’re converting it to ES5.

    Now, we use ES5 in the end for a couple reasons, but primarily it’s because Gutenberg uses React, and React JSX (which translates Javascript to XHTML). That requires us to transpile back to ES5 in order to be used by all browsers. For now.

    Browsers update a lot faster than they did when we were trying to get rid of IE5. Still, Internet Explorer is around and will be for a while at financial institutions, so don’t get super excited yet.

    Where does this leave us?

    When you go to write your first Gutenblocks, I recommend ES5 for the simple ones and ESNext for the complex ones. ESNext is more semantic, in that it’s laid out in a more human readable way. ES5 is faster to edit and test.

  • Blocks: Another Way to Adjust Settings

    Blocks: Another Way to Adjust Settings

    As I’ve mentioned a few times, one of the ways you can adjust settings on a block in Gutenberg is via the sidebar. In fact, this is the default way most people will interact with block settings.

    If you make a paragraph today, you can see it like this:

    This is the default paragraph sidebar from Gutenberg. No customizations.

    But. I don’t actually like it very much. I appreciate that I have it, and I love that I can get it out of the way. But sometimes I like settings to be a little more contextual.

    It’s Easy to Add Sidebar Settings

    One of the reasons we all use sidebar settings is that, well, they’re easy. When I built out my listicles plugin, I could use the inspector controls and automagically I have my own settings.

    This:

    <InspectorControls>
    	<PanelBody title={ 'Listicle Settings' }>
    		<RangeControl
    			label={ 'Items' }
    			value={ items }
    			onChange={ ( value ) => setAttributes( { items: value } ) }
    			min={ 1 }
    			max={ MAX_ITEMS }
    		/>
    		<ToggleControl
    			label={ 'Reversed' }
    			help={ ( checked ) => checked ? 'Reversed order (10 - 1)' : 'Numerical order (1 - 10)' }
    			checked={ props.attributes.reversed }
    			onChange={ () => props.setAttributes( { reversed: ! props.attributes.reversed } ) }
    		/>
    	</PanelBody>
    </InspectorControls>

    Looks like this:

    A screenshot of the listicle settings, which are a slider for the items and a toggle to reverse the order display.

    But like I said, I don’t like it very much. It’s clunky, it’s touchy, if you delete the number and type in a new one it’ll wipe all your data. And worst of all, my partner in crime, Tracy, hates it. If your work partners hate a tool, then it’s serious problem. I’ll put up with annoyances to me, but I’ll learn new code for the team.

    Think About What’s Easier

    Before I get into the code I used to solve the issue, I want to take a moment to talk about theory and understanding usage.

    One of the critiques about Gutenberg is that it’s changing too much too quickly, and it’s not listening to users. The problem with that complaint is it lacks context. I’m big on context because I believe that only with understanding the usage and context can we as a whole make the correct decisions going forward. What changes, when, and why depends entirely on what’s being used, for what, and why.

    It’s much more direct to understand this when I look at my little listicles block. You see, we use it for one thing: to make lists with a specific format. And we have few requirements.

    • Add and remove items
    • Add content of myriad types to each item
    • Be able to reverse the item count (1 to 10 or 10 to 1)

    That’s really it. Except now I’m adding one more thing:

    • An easier, inline, way to add/remove/toggle items.

    So I sat and I thought about what would be the easiest to use, and I came up with a simple solution. Three buttons, one to add an item, one to remove, and one to toggle the order. Have those show perpetually at the bottom of the list, and you could easily add and remove as needed.

    It’s Actually Easy To Add Buttons

    Once I knew what I wanted, I took a page from some work Yoast is doing and sketched my idea to look like this:

    Three buttons: Add item, remove item, and toggle order

    In order to do this, I needed to add some code to the bottom of my <dl> code:

    <div className='listicles-buttons'>
    	<IconButton
    		icon='insert'
    		onClick={ () => setAttributes( { items: parseInt(`${ items }`)+1 } ) }
    		className='editor-inserter__toggle'
    	>Add Item</IconButton>
    
    	<IconButton
    		icon='dismiss'
    		onClick={ () => setAttributes( { items: parseInt(`${ items }`)-1 } ) }
    		className='editor-inserter__toggle'
    	>Remove Item</IconButton>
    
    	<IconButton
    		icon='controls-repeat'
    		onClick={ () => setAttributes( { reversed: ! reversed } ) }
    		className='editor-inserter__toggle'
    	>Toggle Order</IconButton>
    </div>

    This sits inside the <dl> and just below my <InnerBlocks...> insert. It generates the buttons, which change when you hover by default.

    Now it’s not perfect. If you deleted all the items and pressed delete again, it would sure try to delete. I didn’t put in checks to make sure we didn’t go below 0 or above 18 (which is my current limits). But this is the start to make sure we can keep improving and iterating.

  • Shortcodes Aren’t Leaving Us

    Shortcodes Aren’t Leaving Us

    In a recent post, someone asked:

    We have this scenario a lot:

    Lorem ipsum some text [shortcode]affected text[/shortcode] more text, all within one parapgraph.

    How can this be solved with Gutenberg?

    Quick answer? It won’t.

    Just like [shortcode] that outputs a single content, or [shortcode text="affected text"], there will remain a need for singe, inline, in context, shortcode for a while.

    I know it’s confusing. This is because there are different kinds of shortcodes for different purposes. Some, yes, will no longer be needed as much.

    Shortcodes for Inline Content

    This is the easiest one to understand. Think about videos. Or my example of inline editable blocks. Those are a very clear one to one. If you’re inserting a shortcode (or an embed) on it’s own line today, that will be ‘replaced’ in time with a block.

    Embeds are already supported, by the way. If you create your own embeds, they too are automatically blocked.

    Shortcodes for Layout

    This is harder to understand, since it’s not fully ready yet. But if you’ve used a plugin or a theme that told you to use a lot of shortcodes to make a two column layout, guess what you’re not going to be doing anymore in the future? That’s right, using shortcodes. Instead, we’re creating listicles, tables, how-to directions and more.

    Example of Yoast SEO’s How To Block — coming soon. (Credit: Yoast.com)

    Their looks a lot better than my basic blocks, but it shows you what we can do and how great we can do it. Those simple and complex layouts will become blocks, where we will be able to actually see what it’s supposed to look like. This includes the Gallery shortcode we all know and love.

    Shortcodes for Text Insertion

    And finally we have the ‘classic’ block. This is the sort the poster was asking me about. Guess what? Nothing’s going to change with those any time soon. Oh, I’m sure eventually someone will take my inline-editable-block concept and make it so when we add the shortcode, it’ll adjust the text accordingly right before our eyes.

    And really, if we look at the concept of [shortcode]affected text[/shortcode] we realize how much more we can do already with Gutenberg. What are we affecting the text with? We can already make it bold. But can we make it blue? Not yet. But I can see this coming soon.

    But right now? Those inline shortcodes, in the middle of your block, are staying put.

  • Context is Everything

    Context is Everything

    In the uptick of automated scans, we come to the place where we realize it’s not just the quality of content that matters in our success, but the context.

    Context in Content

    When you write content, the body of your work depends on the literary context of the words. Writing about technology on a non-tech site requires you to step back and explain the tech in a little more detail than you normally might. For example, if I were to post about shortcodes here, I would not bother to give you the history of what they were or why they’re used. I would trust you to know those things, or be ready and able to research them.

    By contrast, when writing about code used on a journalism site, and explaining we had a nifty new shortcode to do a thing, I absolutely would take time to explain. I would not expect my readers there, who care about the goings on of television, to understand about the weirdness of a shortcode. At the same time, I may not need to delve into details quite so much. I could just say “We have a new, faster way to add whatever, which will make it easier for us to report on X.”

    In short, I consider the audience when I write the content. I write contextually.

    Context in Code

    When it comes to writing code, there is a similar mindset. The code should make sense contextually and be consistent. If you’re using underscores for filenames, always use underscores, just to give one example. But this goes further than having a same prefix or formatting (tabs or spaces, eh?). It also means that when data is processed, it should be done so contextually.

    If you have a form, and you allow people to enter data to send to you, and that data is saved to a database, you have to sanitize the data. That’s a no-brainer for every developer worth the time of day. Never save unsanitized data, and sanitize as early as possible to minimize the possible damage. But deciding how best to sanitize can be tricky. PHP comes with stripslashes() for example, however consider that PHP says this:

    An example use of stripslashes() is when the PHP directive magic_quotes_gpc is on (it was on by default before PHP 5.4), and you aren’t inserting this data into a place (such as a database) that requires escaping. For example, if you’re simply outputting data straight from an HTML form.

    In other words, you shouldn’t use that to save data. Thankfully in WordPress (and Drupal and everything else) there are many ways to sanitize your inputted data based on … you guessed it, context. You don’t sanitize a URL as a plain text field, and you don’t sanitize an HTML form as a filename.

    When you write your code, sanitize, validate, and escape it contextually based on what it is.

    The Bottom Line: Context Matters

    This is the thing that automated checkers can’t quite do. They don’t know what the input is supposed to be unless you tell them, so they can’t verify your sanitization as well as a human can. Even grammar checkers can’t tell you when it’s okay to use slang and when it’s not, when you’re trying to explain a new concept.

    In the end? We need humans.

  • Show Feedback in “Right Now”

    Show Feedback in “Right Now”

    The “Right Now” section of the WordPress dashboard is a great way to get an overview of the goings on of your site. But it doesn’t quite list everything. What if you could add things like ‘messages’ to the the “At a Glance” section like this:

    The "At a Glance" section, with messages added in.

    Guess what? You can!

    The Code

    Presmuing you’re using Jetpack’s contact form module, you automatically get a new kind of post called ‘Feedback.’ In order to make it display it’s count in “At a Glance,” there are two parts. First we add the CSS, which does the styling. Then we add the PHP that counts and displays the number of posts in feedback.

    add_action( 'dashboard_glance_items', 'helf_dashboard_glance' );
    add_action( 'admin_head', 'helf_dashboard_glance_css' );
    
    /*
     * Show Feedback in "Right Now"
     */
    function helf_dashboard_glance() {
    	if ( class_exists( 'Jetpack' ) && Jetpack::is_module_active( 'contact-form' ) ) {
    		foreach ( array( 'feedback' ) as $post_type ) {
    			$num_posts   = wp_count_posts( $post_type );
    			$count_posts = ( isset( $num_posts->publish ) ) ? $num_posts->publish : '0';
    			if ( 0 !== $count_posts ) {
    				if ( 'feedback' === $post_type ) {
    					// translators: %s is the number of messages
    					$text = _n( '%s Message', '%s Messages', $count_posts );
    				}
    				$text = sprintf( $text, number_format_i18n( $count_posts ) );
    				printf( '<li class="%1$s-count"><a href="edit.php?post_type=%1$s">%2$s</a></li>', esc_attr( $post_type ), wp_kses_post( $text ) );
    			}
    		}
    	}
    }
    
    /*
     * Custom Icon for Feedback in "Right Now"
     */
    function helf_dashboard_glance_css() {
    	if ( class_exists( 'Jetpack' ) && Jetpack::is_module_active( 'contact-form' ) ) {
    		?>
    		<style type='text/css'>
    			#adminmenu #menu-posts-feedback div.wp-menu-image:before, #dashboard_right_now li.feedback-count a:before {
    				content: '\f466';
    				margin-left: -1px;
    			}
    		</style>
    		<?php
    	}
    }
  • Selective DeGutenberging

    Selective DeGutenberging

    Okay let’s be honest, friends. Not everything is ready for Gutenberg. In fact, I myself have certain sites that aren’t ready for it today. That’s why there exists an excellent plugin known as the Classic Editor. This allows you to decide it you want to block Gutenberg (which is the default option) or if you want to allow users to chose between Gutenberg and Classic editors.

    The settings page for the Classic editor.

    But what if you want to go a step further?

    What if you want to have Gutenberg on by default for posts and pages, but not for a custom post type?

    Don’t worry. We can do that.

    The Code

    The basic idea is that if WP is 5.0 or higher, make sure that the Classic Editor is installed. If it’s less than 5.0, make sure Gutenberg is installed. Then block the code as needed:

    class HELF_Gutenberg {
    
    	public $gutenfree = array();
    
    	public function __construct() {
    		$this->gutenfree = array( 'post_type_ONE', 'post_type_TWO', 'post_type_THREE' );
    		add_action( 'current_screen', array( $this, 'gutenberg_removal' ) );
    	}
    
    	public function gutenberg_removal() {
    
    		// WP 5.0+ requires Classic Editor
    		// WP 4.9- requires Gutenberg
    		if ( ( version_compare( get_bloginfo( 'version' ), 5.0, '<=' ) && ! is_plugin_active( 'gutenberg/gutenberg.php' ) ) || ( version_compare( get_bloginfo( 'version' ), 5.0, '=>' ) && ! is_plugin_active( 'classic-editor/classic-editor.php' ) ) ) {
    			return;
    		}
    
    		// Intercept Post Type
    		$current_screen    = get_current_screen();
    		$current_post_type = $current_screen->post_type;
    
    		// If this is one of our custom post types, we don't gutenize
    		if ( in_array( $current_post_type, $this->gutenfree, true ) ) {
    			remove_filter( 'replace_editor', 'gutenberg_init' );
    			remove_action( 'load-post.php', 'gutenberg_intercept_edit_post' );
    			remove_action( 'load-post-new.php', 'gutenberg_intercept_post_new' );
    			remove_action( 'admin_init', 'gutenberg_add_edit_link_filters' );
    			remove_filter( 'admin_url', 'gutenberg_modify_add_new_button_url' );
    			remove_action( 'admin_print_scripts-edit.php', 'gutenberg_replace_default_add_new_button' );
    			remove_action( 'admin_enqueue_scripts', 'gutenberg_editor_scripts_and_styles' );
    			remove_filter( 'screen_options_show_screen', '__return_false' );
    		}
    	}
    
    }
    
    new HELF_Gutenberg();