Avoid printing empty blocks in Headway

I’ve talked about qTransalte language chooser block to a great extent and I’m using it here as an example and the code examples will use its code.

The objective

Sometimes a block added in design time could yield, in the dynamic environment of a live WordPress site, empty results.
In the example I’m making here the block will return a message when the plugin it gets its content from is not active or not installed

<?php
namespace qtblock;

class Block extends \HeadwayBlockAPI
{
    public $id = 'qtblock';
    public $name = 'qTranslate language chooser';
    public $options_class = '\qtblock\BlockOptions';
    public $description = 'An Headway block to display qTranslate plugin language chooser on the page.';
    public function content($block)
    {
        if (function_exists('qtrans_generateLanguageSelectCode')) {
            $displayMode = \HeadwayBlocksData::get_block_setting($block, 'display-mode', 'dropdown');
            $out = qtrans_generateLanguageSelectCode($displayMode);
            echo $out;
            return;
        }
        echo 'Either qTranslate plugin is not installed or it\'s not activated';
    }

and in the site will output something like

Not interesting to visitors, hide!
Not interesting to visitors, hide!

which is informative to a WordPress administrator but not the nicest thing to see. I can imagine the same happening for a block that displays related pages or some other kind of information that might actually be not present or empty at the time of that particular page request.
Once a block is added to a page layout Headway will print it, and along with it its title and subtitle, to the page.
The block in the page layout
The block in the page layout

If the block should return a list of current promotions and no promotions are present then printing “There are no promotions at the moment” to the page could not be the best result and no option is added to the blocks by default to prevent such behaviour.

Empty output

The bare minimum Headway will print to the page, when title and subtitle are set, is

<div id="block-9" class="block block-type-qtblock block-fluid-height">
    <div class="block-content">
        <hgroup>
            <h1 class="block-title"><span>Choose language</span></h1>
            <h2 class="block-subtitle">You not speak english?</h2>
        </hgroup>
    </div><!-- .block-content -->
</div>

while this is what will be printed to the page when no title, subtitle and content are present

<div id="block-9" class="block block-type-qtblock block-fluid-height">
    <div class="block-content">
    </div><!-- .block-content -->
</div>

Which is not a lot but still is something that will get space and style on the page.

Empty blocks still take place and style
Empty blocks still take place and style

Hooking

Without going into much detail an hook will be called before any HTML is printed to the page

headway_before_block

and another after the block has been printed to the page

headway_after_block

I will hook into both to

  • activate output buffering before any HTML is printed to the page
  • print the block to the page if there is any actual content in it

And that’s the modified block code shown by run-time

<?php
namespace qtblock;

class Block extends \HeadwayBlockAPI
{
    public $id = 'qtblock';
    public $name = 'qTranslate language chooser';
    public $options_class = '\qtblock\BlockOptions';
    public $description = 'An Headway block to display qTranslate plugin language chooser on the page.';
    protected $content = true;

I hook into both actions when the block is initialized

    public function init()
    {
        add_action('headway_before_block', array($this, 'bufferOutput'));
        add_action('headway_after_block', array($this, 'maybePrintBlock'));
    }

the first one will activate the output buffer to prevent any actual printing to the page

    public function bufferOutput($block)
    {
        // do nothing if it's not this block type
        if ($block['type'] != $this->id) {
            return;
        }
        ob_start();
    }

the content function will either echo some content or set the $content variable to false

    public function content($block)
    {
        if (function_exists('qtrans_generateLanguageSelectCode')) {
            $displayMode = \HeadwayBlocksData::get_block_setting($block, 'display-mode', 'dropdown');
            $out = qtrans_generateLanguageSelectCode($displayMode);
            echo $out;
            return;
        }
        $this->content = false;
    }

this one will eventually print the output to the page

    public function maybePrintBlock($block)
    {
        // do nothing if it's not this block type
        if ($block['type'] != $this->id) {
            return;
        }
        $out = ob_get_contents();
        ob_end_clean();
        if (!$this->content) {
            return;
        }
        echo $out;
    }
    ...
}

A more specific hooking

More specific hooks exist in code in the form

headway_before_block_<blockId>

headway_after_block_<blockId>

The id seen here is not something like qtblock (the block $id property) but a numbered selector like 9 (an int value).
Sadly no hook can be attached there due to the fact that when Block::init() function is called the block id (the int value) is not one of the available informations.

Caveats

In a grid system preventing a block from being printed to the page might issue some layout concerns and the non option-able behaviour seen here might not be the best solution in any layout. I had no problems in a vertically stacked grid but the solution might not cut it for side by side blocks.

I appreciate your input