Backstretch Headway block – 04

Further scenarios

I’ve previously dealt with the option for a theme user not to set any image as the site background and the settings a theme developer is offered by the plugin to deal with that situation. I will deal now with the scenario where a theme user decides to set one or more images to be used as the site background.

Wrappers and utilities

My life, in coding terms, is made a little easier by some wrapper and utility classes. All of what I use below is part of my libraries for WordPress and I’m not delving into them in the post to avoid excessive length; the code is pretty self-explanatory where not commented inline.

Fundamental recipes

I will be using Backstretch jQuery plugin to set the body background when one or more images are involved and the BFI_Thumb class to apply effects to the images.
About that class: I’ve run into an error concerning the missing wp_get_current_user function and have added a require statement on top of the file to solve it. I’ve forked the official repository here and will test it a little more before requesting for a pull.

Forking the code

The htbackstretch\Main class is once again the place where all the things happen and I will modify its super-constructor method, something that smells very badly to me, once again to take further usage scenarios into account

<?php
namespace htbackstretch;

use \tad\wrappers\ThemeCustomizeSection as Section;
use \tad\wrappers\headway\GlobalSettings as Settings;
use \tad\wrappers\headway\VEPanel;
use \tad\wrappers\Option;
use \tad\utils\Script;

class Main
{
    protected $themeSection;
    protected $settings;
    protected $panel;
    protected $showColorPicker;
    protected $imageSources;

    public function __construct()
    {
        add_action('after_setup_theme', array($this, 'blockRegister'));
        add_action('init', array($this, 'extend_updater'));

        // add the 'Background images' section
        // options will be stored in the 'backstretch' option in an array format
        // the namespace is used as the text domain
        $this->themeSection = new Section('Background images', 'backstretch', 'What to use as site background?', __NAMESPACE__);

        // add the multi-image control to allow the user to select one or more images
        // defaults to no images
        // will be stored in 'backstretch[imageSources]'
        $this->themeSection->addSetting('imageSources', 'Upload or select one or more images.', '', 'multi-image');

        // load Headway site-wide settings from the database passing a prefix to get this block
        // theme-wide settings only
        $this->settings = new Settings('htbackstretch-');
        $dbValue = $this->settings->noImageSelected;
        is_null($dbValue) ? $this->showColorPicker = '0' : $this->showColorPicker = $dbValue;

        // please note: the first option in the select the theme developer
        // uses has the index 0 and that's the one reading
        // 'user can set a background color'
        if ($this->showColorPicker == '0') {
            // if the setting has not been set yet or the setting is
            // true then add the color picker to theme customizer controls
            // the set color will be stored in the 'backstretch[bg-color]' option
            $this->themeSection->addSetting('bg-color', 'Select a background color', '#FFF', 'color');
        }

        // register this block theme-wide settings
        VEPanel::on(__NAMESPACE__ . '\VisualEditorPanel');

        // depending on the theme user and the theme developer settings
        // do something related to the body background image or color
        $this->useOptions();
    }

Aside for some things it already did before the constructor will now use a wrapper in its fluent form and will delegate the decisions about what’s the current scenario to the useOptions method

protected function useOptions()
{
    // get the images sources from the database if the theme user did
    // upload/selected at least one
    $imageSources = Option::on('backstretch')->imageSources;

    // if the user did not select at least one image to use as the body
    // background then maybe use the color
    if (is_null($imageSources) or $imageSources == '') {
        $this->maybePrintBodyStyle();
        return;
    }

    // there is at least one image, use that
    $this->useImages($imageSources);
}

The function took in the check that was previously in the __construct method to stop and count the images the user might have chosen. The whole block plugin is user-oriented allowing the theme developer to use settings to build on the theme user decisions

protected function maybePrintBodyStyle()
{
    // if the theme user is not allowed to set a body background color return
    if ($this->showColorPicker != '0') {
        return;
    }
    // hook into the 'wp_enqueue_scripts' hook to print the style
    $tag = 'wp_enqueue_scripts';
    $function = function () {
        $class = 'htbackstretch-color';
        $color = \tad\wrappers\Option::on('backstretch')->bgColor;
        echo sprintf('<style>body.%s{background-color:%s;}</style>', $class, $color);
    };
    add_action($tag, $function);
    // hook into th body_class filter to add a class to the body
    $tag = 'body_class';
    $function = function ($classes) {
        $classes[] = 'htbackstretch-color';
        return $classes;
    };
    add_filter($tag, $function);
}

This did not change at all save for a bit of fluent in the use of the Option::on method and its details are discussed in the previous post.

protected function useImages($imageSources)
{
    // the multiple images control will store the image sources in a
    // comma separated list
    $this->imageSources = explode(',', $imageSources);

    // will be 1 to many
    $count = count($this->imageSources);

    // did the theme developer chose to show one random image per page load?
    // defaults to false -> show in a slider-like effect
    $useRandom = (bool)($this->settings->moreImagesSelected or '0');
    if ($useRandom) {
        $randomIndex = mt_rand(0, $count - 1);
        $this->imageSources = array($this->imageSources[$randomIndex]);
    }

    $useEffect = false;

    // effects: grayscale, sepia, negative in this order
    $effect = '0';

    if ($count == 1) {
        // the setting for 'do not use an effect' is '0'
        // will default to not using an effect
        $useEffect = (bool)($this->settings->oneImageSelected or '0');
        $effect = $this->settings->oneImageEffect or '0';
    } else {
        // the setting for 'do not use an effect' is '0'
        // will default to not using an effect
        $useEffect = (bool)($this->settings->moreImagesEffectUse or '0');
        $effect = $this->settings->moreImagesEffect or '0';
    }
    if ($useEffect) {
        // require the BFI_Thumb file
        \tadlibs_include('bfi/BFI_Thumb');
        $buffer = array();
        foreach ($this->imageSources as $src) {
            // obtain the url to the modified image generated
            // by bfi_thumb
            $params = array();
            switch ($effect) {
                case '1':
                    // sepia, is it sepia?
                    $params = array('grayscale' => true, 'color' => '#643200');
                    break;
                case '2':
                    // negative
                    $params = array('negate' => true);
                    break;
                default:
                    // grayscale
                    $params = array('grayscale' => true);
                    break;
            }
            // add the source of the modified image to the buffer
            $buffer[] = bfi_thumb($src, $params);
        }
        $this->imageSources = $buffer;
    }
    // hook into wp_enqueue_scripts
    add_action( 'wp_enqueue_scripts', array($this, 'enqueueScripts'));
}

what happens in this method is that the theme user generated scenario, in this method having selected one or more than one images, is taken into account in the count variable and then used along the guidelines set by the theme developer to apply an effect or not.

public function enqueueScripts()
{
    // enqueue the backstretch plugin, requires jQuery
    wp_enqueue_script('backstretch', '//cdnjs.cloudflare.com/ajax/libs/jquery-backstretch/2.0.4/jquery.backstretch.min.js', 'jquery'); 
    // localize the image sources to the page
    wp_localize_script('backstretch', 'backstretchImages', $this->imageSources); 
    // enqueue a script to start backstretch
    $src = Script::suffix(HTBACKSTRETCH_BLOCK_URL . 'assets/js/backstretchStart.js');
    wp_enqueue_script('backstretchStart', $src, array('jquery', 'backstretch'), false, true);
}

The function that queues the script is trivial and nothing strange. The Script class will take care to queue a minified version of the script if WP_DEBUG is not enabled. The script itself adds little to the usage guidelines illustrated on Backstretch plugin page.

/*global document, jQuery, backstretch, $, backstretchImages*/
jQuery(document).ready(function($) {
    // if the expected data is not there return
    // if there are no images in the data return
    // if backstretch has not been loaded return
    if (backstretchImages === 'undefined' || backstretchImages.length === 0 || $.backstretch === 'undefined') {
        return;
    }
    // one image?
    if (backstretchImages.length === 1) {
        $.backstretch(backstretchImages[0]);
        return;
    }
    // 2 or more images?
    $.backstretch(backstretchImages, {
        duration: 3000,
        fade: 750
    });
});

Final result and the code

I’ve cleaned the layout a little to show how the plugin will interact with the page and here’s the video

The code actual to this post is on Github under the 0.0.3 tag.

Next step

Refactoring and thorough testing, after that might add more settings on the theme developer side.