Advanced Block Example

This is an advanced tutorial on how to create a block in SCHLIX CMS with more complex options. We assume that you've already familiarized yourself with basic example of a block in SCHLIX CMS and know how to use Plugin Creator. Please read that page first if you're still unfamiliar.

Let's say you're given a task to replace the gallery slideshow on the frontpage (under category front_slideshow) with a slideshow that displays images with data source from a particular category in a blog (\App\Blog) instead of gallery (\App\Gallery). You also want to replace the basic Bootstrap 4 slideshow provided by default with a different Javascript slider, like Slick Slider.

Blog and Gallery Slideshow

Let's break down the tasks:

  1. Create a new block using Plugin Creator.
  2. Start coding
    1. Download Slick Slider
    2. Configure config.template.php
    3. Write code to retrieve various display options from config and to retrieve articles from a blog category
    4. Display the articles in view.block.template.php
    5. Test it
  3. Publish it to Extensions Directory (registration required)

1. Create a new block using Plugin Creator

The first step is to create a block with the name Blog Slider.

Plugin Creator - Blog Slider

2. Start Coding

a. Download Slick Slider

Download the ZIP package from https://github.com/kenwheeler/slick and place it inside the blogslider folder that was just generated by the Plugin Creator.

Read the various configuration options on that Github repo and then check out the demo at http://kenwheeler.github.io/slick/.

b. Configure config.template.php

Once you've familiarized yourself with the configuration option as specified on the Settings section of https://github.com/kenwheeler/slick, you can now create a configuration page that can be used by website owners.

Slick Slider configuration

Settings

Option Type Default Description
accessibility boolean true Enables tabbing and arrow key navigation. Unless autoplay: true, sets browser focus to current slide (or first of current slide set, if multiple slidesToShow) after slide change. For full a11y compliance enable focusOnChange in addition to this.
adaptiveHeight boolean false Adapts slider height to the current slide
arrows boolean true Enable Next/Prev arrows
autoplay boolean false Enables auto play of slides
autoplaySpeed int 3000 Auto play change interval
dots boolean false Current slide indicator dots
dotsClass string 'slick-dots' Class for slide indicator dots container
draggable boolean true Enables desktop dragging
... more ... ... ....etc

In addition to the Slick Slider options, we'd like to add additional option such as limiting the display of article to a certain category and the maximum length of article summary to be displayed on each slideshow.

<?php
/**
 * Blog Slider - Config
 * 
 * Slideshow/carousel for Blog using Slick Slider from https://github.com/kenwheeler/slick
 *
 * @copyright 2020 SCHLIX Web Inc
 *
 * @license MIT
 *
 * @package blogslider
 * @version 1.0
 * @author  SCHLIX Web Inc 
 * @link    https://www.schlix.com
 */
if (!defined('SCHLIX_VERSION'))
    die('No Access');

    
/**
 * This function is to create a category tree for the dropdown box int_only_category
 * If you'd like, you can add your function. 
 * @param array $category_array
 * @param int $parent
 * @param int $depth
 * @return string
 */
function generateTree($category_array, $parent = 0, $depth = 0) {
    if ($depth > 100)
        return ''; // Make sure not to have an endless recursion
    $ul_css_class = 'nested-category-list';
    if ($depth > 0)
        $ul_css_class =  '';

    $subtree = array();
    $menu_count = ___c($category_array);
    for ($i = 0; $i < $menu_count; $i++) {
        if ($category_array[$i]['parent_id'] == $parent) {
            $childnodes = generateTree($category_array, $category_array[$i]['cid'], $depth + 1);
            $title = str_repeat('--', $depth).$category_array[$i]['title'];
            $subtree[]= array('cid' => $category_array[$i]['cid'], 'title' =>$title); 

            if (!empty($childnodes))
                $subtree = array_merge($subtree, $childnodes);
        }
    } 
    return $subtree;
}

// main code
$tmp_blog = new \App\blog();
$allcategories = $tmp_blog->getAllCategories("*",'status > 0',0,0,'title','ASC');    

$html_category_choice = generateTree($allcategories)
?>
<schlix-config:integerbox config-key="int_max_entries" min="0" max="100" label="<?= ___('Maximum number of slideshow to be displayed (descending order)') ?>"   config-default-value="10" />
<schlix-config:integerbox config-key="int_max_summary_length" min="0" max="10000" label="<?= ___('Maximum length of summary text to be displayed') ?>"   config-default-value="100" />
<schlix-config:dropdownlist   config-key="int_only_category" label="<?=  ___('Only display the following category') ?>">
    <schlix-config:option value=""><?= ___('(None)') ?> </schlix-config:option>
    <?php foreach ($html_category_choice as $choice): ?>
        <schlix-config:option value="<?= $choice['cid'] ?>"><?= ___h($choice['title']) ?> </schlix-config:option>
    <?php endforeach ?>
</schlix-config:dropdownlist>
<schlix-config:integerbox config-key="int_cache_result_seconds" label="<?= ___("Cache result for (in seconds)") ?>"   config-default-value="120" />
<p class="small"><?= ___('Caching the result set will help with the performance of your website') ?></p>

<hr />
<!-- Options specific to slick slider -->
<h3><?= ___('Slick Slider options') ?></h3>

<x-ui:row>
    <!-- column -->
    <x-ui:column md="6">
        <!-- config -->
        <schlix-config:checkbox config-key="bool_accessibility" label="<?= ___('Enables tabbing and arrow key navigation') ?>" />
        <schlix-config:checkbox config-key="bool_adaptive_height" label="<?= ___('Adapts slider height to the current slide') ?>" />
        <schlix-config:checkbox config-key="bool_arrows" label="<?= ___('Enable Next/Prev arrows') ?>" />
        <schlix-config:checkbox config-key="bool_arrows_inside" label="<?= ___('Display arrows on the inside (for full width slideshow)') ?>" />
        <schlix-config:checkbox config-key="bool_draggable" label="<?= ___('Enables desktop dragging') ?>" />
        
        <schlix-config:checkbox config-key="bool_infinite" label="<?= ___('Infinite slides') ?>" />
        <schlix-config:checkbox config-key="bool_center_mode" label="<?= ___('Enables centered view with partial prev/next slides. Use with odd numbered slides to show counts.') ?>" />
        <schlix-config:integerbox config-key="int_slides_to_show" min="1" max="10" label="<?= ___('Number of slides to show') ?>"   config-default-value="1" />
        <schlix-config:integerbox config-key="int_slides_to_scroll" min="1" max="10" label="<?= ___('Number of slides to scroll') ?>"   config-default-value="1" />
        <!-- end config -->
    </x-ui:column>
    <!-- column -->
    <x-ui:column md="6">
        <!-- config -->
<schlix-config:radiogroup config-key="int_fade" label="<?= ___('Animation') ?>">
    <schlix-config:option value="0"><?= ___S('Slide') ?></schlix-config:option>
    <schlix-config:option value="1"><?= ___S('Fade') ?></schlix-config:option>
</schlix-config:radiogroup>
<fieldset>
    <legend><?= ___('Dots Indicator') ?></legend>
    <schlix-config:checkbox config-key="bool_dots" label="<?= ___('Show current slide indicator dots') ?>" />
    <schlix-config:textbox config-key="str_dots_class" label="<?= ___('Custom Dots CSS class') ?>" config-default-value="slick-dots" />
</fieldset>
<fieldset>
    <legend><?= ___('Autoplay') ?></legend>
    <schlix-config:checkbox config-key="bool_autoplay" label="<?= ___('Enables auto play of slides') ?>" />
    <schlix-config:integerbox config-key="int_autoplay_speed_second" min="1" max="20" label="<?= ___('Auto play change interval (second)') ?>"   config-default-value="3" />
</fieldset>
        
        <!-- end config -->
    </x-ui:column>    
</x-ui:row>

As you can see from above, we use a mix of PHP code, HTML tags, X-UI tags, as well as generic schlix-config tags. What we need to do is simply to incorporate any configurable options and settings. Please note that since there are many options, we only included a few for the purpose of this tutorial.

c. Write code to retrieve various display options from config and to retrieve articles from a blog category

We've embedded some inline comments in the code, hopefully it's clear enough. If you're new to SCHLIX CMS, don't overwhelm yourself by using an editor that can't do inline autocomplete. You can use a capable IDE like NetBeans so you don't need to remember all the functions.

Netbeans autocomplete

<?php
namespace Block;

/**
 * Blog Slider - Main Class
 * 
 * Slideshow/carousel for Blog using Slick Slider from https://github.com/kenwheeler/slick
 * Note - only partial configuration options are implemented for the tutorial
 * at https://www.schlix.com/documentation/v2/application-development/block/advanced-block-example.html
 * 
 * @copyright 2020 SCHLIX Web Inc
 *
 * @license MIT
 *
 * @package blogslider
 * @version 1.0
 * @author  SCHLIX Web Inc 
 * @link    https://www.schlix.com
 */

/**
 * BlogSlider is inherited from https://www.schlix.com/api/2.x/namespace/namespace_s_c_h_l_i_x/class_s_c_h_l_i_x_1_1cms_block.html
 */
class BlogSlider extends \SCHLIX\cmsBlock {

    /**
     * Run the block
     * @global \App\Users $CurrentUser
     * @global \SCHLIX\cmsHTMLPageHeader $HTMLHeader
     */
    public function Run() {
        global $CurrentUser, $HTMLHeader;
        
        // Get values from config
        $config_max_entries = $this->config['int_max_entries'];        
        $config_max_summary_length = $this->config['int_max_summary_length'];        
        $config_category_id = (int) $this->config['int_only_category'];
        // Instantiate a new blog class
        $blog = new \App\Blog();
        // For more info: https://www.schlix.com/api/2.x/namespace/namespace_app/class_app_1_1_blog.html
        // which is inherited from https://www.schlix.com/api/2.x/namespace/namespace_s_c_h_l_i_x/class_s_c_h_l_i_x_1_1cms_application___many_to_many.html
        // which is inherited from https://www.schlix.com/api/2.x/namespace/namespace_s_c_h_l_i_x/class_s_c_h_l_i_x_1_1cms_application___hierarchical_tree___list.html
        // which is inherited from https://www.schlix.com/api/2.x/namespace/namespace_s_c_h_l_i_x/class_s_c_h_l_i_x_1_1cms_application___categorized_list.html
        // which is inherited from https://www.schlix.com/api/2.x/namespace/namespace_s_c_h_l_i_x/class_s_c_h_l_i_x_1_1cms_application___list.html
        // which is inherited from https://www.schlix.com/api/2.x/namespace/namespace_s_c_h_l_i_x/class_s_c_h_l_i_x_1_1cms_application___basic.html
        // there you go!
        $config_max_entries = ($config_max_entries == 0) ? 15 : $config_max_entries;
        
        // initialize variables
        $items = null;
        // Was category specified by the user? If so, then just get the blog
        // articles for the specific category
        if ($config_category_id > 0)
        {
            $items = $blog->getItemsByCategoryID($config_category_id, '*', ' status = 1 ', 0, $config_max_entries, 'date_created', 'DESC', true);
        } else
        // otherwise, display slideshow for the latest blog articles
        {
            $items = $blog->getAllItems('*', ' status = 1 ', 0, $config_max_entries, 'date_created', 'DESC', true);
        }
        $total_item_count = ___c($items);
        if ($total_item_count == 0)
        {
            echo '<!-- No blog article found -->';
            return; // don't do any additional processing
        }
        // these are the config values from https://github.com/kenwheeler/slick
        // We're going to be very verbose so you can easily read it
        // Note: we only implement partial option since this is just for a tutorial demo
        // for https://www.schlix.com/documentation/v2/application-development/block/advanced-block-example.html
        $config_adaptive_height = is_value_true($this->config['bool_infinite']);
        $config_acessibility = is_value_true($this->config['bool_accessibility']);
        $config_adaptive_height = is_value_true($this->config['bool_adaptive_height']);
        $config_arrows = is_value_true($this->config['bool_arrows']);
        $config_bool_arrows_inside = is_value_true($this->config['bool_arrows_inside']); 
        $config_autoplay =  is_value_true($this->config['bool_autoplay']);
        $config_infinite = is_value_true($this->config['bool_infinite']);
        $config_slides_to_show = max (min($this->config['int_slides_to_show'], 10), 1);
        $config_slides_to_scroll = max (min($this->config['int_slides_to_scroll'], 10), 1);
        $config_autoplay_speed_milli_seconds = 1000 * (max (min($this->config['int_autoplay_speed_second'], 20), 1));
        $config_dots = is_value_true($this->config['bool_dots']);
        $config_dots_class = is_value_true($this->config['str_dots_class']);
        $config_fade = is_value_true((int) $this->config['int_fade']);        
        $config_center_mode = is_value_true($this->config['bool_center_mode']);
        // shows you how to set the default to TRUE if the value hasn't been set (e.g. first install)
        $config_draggable = array_key_exists ('bool_draggable', $this->config ) ? is_value_true($this->config['bool_draggable']) : true;
        
        // now put it in a variable where it will be encoded as JSON so the options
        // can be passed before blogslider.js is loaded
        $slick_slider_options = [
            'accessibility' => $config_acessibility, 
            'adaptiveHeight' => $config_adaptive_height,
            'arrows' => $config_arrows,
            'dots' => $config_dots,
            'draggable' => $config_draggable,
            'fade' => $config_fade,
            'autoplay' => $config_autoplay,
            'autoplaySpeed' => $config_autoplay_speed_milli_seconds,
            'centerMode' => $config_center_mode,
            'infinite' => $config_infinite,
            'slidesToShow' => $config_slides_to_show,
            'slidesToScroll' => $config_slides_to_scroll
            
                ];
        if (!empty($config_dots_class))
            $slick_slider_options['dotsClass'] = $config_dots_class;
        // ---------------- end of config settings -------------------------//
        $arr_css_class = ['blogslider'];
        if ($config_bool_arrows_inside)
            $arr_css_class[] = 'blogslider-arrow-insider'; // for full width images, otherwise arrow won't show up
        $css_class_blog_slider = implode(' ', $arr_css_class);
        // You can actually add these 4 lines to the view.block.template.php instead if you prefer
        $this->CSS('slick/slick.css');
        $this->CSS('slick/slick-theme.css');
        $this->CSS('blogslider.css');
        $this->JAVASCRIPT('slick/slick.min.js');        
        // you can also use $HTMLHeader instead
        // For more info: https://www.schlix.com/api/2.x/namespace/namespace_s_c_h_l_i_x/class_s_c_h_l_i_x_1_1cms_h_t_m_l_page_header.html
        // We'll have to use a unique block name like blogslider_{$this->block_name}
        // because there can be more than 1 slideshow on a single page
        // since a block can be called more than once (for different instance)
        // and hence we reference it by the CSS ID instead of CSS class
        $HTMLHeader->JAVASCRIPT_TEXT(
                "var slick_slider_options_{$this->block_name} = ".json_encode($slick_slider_options).";".
                "$(document).ready(function(){
  $('#blogslider_{$this->block_name}').slick(slick_slider_options_{$this->block_name});
});");
        // Now slick_slider_options has been specified, load view.block.template.php
        $this->loadTemplateFile('view.block', compact(array_keys(get_defined_vars())));
    }

}

d. Display the articles in view.block.template.php

This is what the view.block.template.php should look like.

<?php
/**
 * Blog Slider - Main page view template. Lists both categories and items with parent_id = 0 and category_id = 0 
 * 
 * Slideshow/carousel for Blog using Slick Slider from https://github.com/kenwheeler/slick
 * 
 * @copyright 2020 SCHLIX Web Inc
 *
 * @license MIT
 *
 * @package blogslider
 * @version 1.0
 * @author  SCHLIX Web Inc 
 * @link    https://www.schlix.com
 */
if (!defined('SCHLIX_VERSION')) die('No Access');

// NOTE - always add id = $this->block_name, which is unique, so frontend developer
// can style it
?>

<div class="<?= $css_class_blog_slider ?>" id="blogslider_<?= $this->block_name ?>">
    <?php for ($i = 0; ($i < $config_max_entries) && ($i < $total_item_count); $i++): ?>
    <?php 
        // the follow fields from gk_blog_item will be used:
        // title, summary, summary_intro_image, summary_secondary_headline (if available),
        // the code below will be executed in a loop
        $item = $items[$i];
        $link = $blog->createFriendlyURL("action=viewitem&id={$item['id']}");
        $thumb_filename = $blog->getBlogImage('image_large', $item['summary_intro_image']); 
        
        // We'd like to add a paragraph
        $secondary_headline = $item['summary_secondary_headline'] ? $item['summary_secondary_headline'] : mb_substr(strip_tags($item['summary']), 0, 255,'UTF-8') . '...';
        if ($item['summary_secondary_headline']) {
            $secondary_headline = $item['summary_secondary_headline'];
        } else {
            $article_summary = strip_tags($item['summary']);
            $article_summary_space_position = mb_strpos($article_summary, ' ', $config_max_summary_length-5);
            if ($article_summary_space_position == 0 || $article_summary_space_position > $config_max_summary_length)
                $article_summary_space_position = $config_max_summary_length;
            $secondary_headline = mb_substr($article_summary, 0, $article_summary_space_position,'UTF-8');
        }
        // begin looped slideshow
        // if you want to check read permission before displaying it
        if ($CurrentUser->hasReadPermission($item['permission_read'])):
    ?>
        <div class="blogslider-image" style="background-image:url(<?= $thumb_filename ?>)">
            <div class="container d-flex h-100">
                <div class="row align-self-center w-100">
                    <div class="col-8 mx-auto">
                        <div class="text-center blogslider-text">
                            <h1><a href="<?= $link ?>"><?= ___h($item['title']) ?></a></h1>
                            <p>
                            <?= $secondary_headline ?>                                 
                            </p>
                        </div>
                    </div>
                </div>                
            </div>
            
        </div>
    <?php endif; ?>
    <?php endfor; ?>
</div>

We also added an extra CSS file:

/**
 * Blog Slider 
 * NOTE: This CSS file that can be overriden by copying it to:
 * /web/{site_name}/themes/{theme_name}/blocks/blogslider/blogslider.css}
 * 
 * Slideshow/carousel for Blog using Slick Slider from https://github.com/kenwheeler/slick
 * 
 * @copyright 2020 SCHLIX Web Inc
 *
 * @license MIT
 *
 * @package blogslider
 * @version 1.0
 * @author  SCHLIX Web Inc 
 * @link    https://www.schlix.com
 */

.blogslider-image
{

  max-width: 100vw;
  position: relative;
  
  width: 100vw ;
  height:700px;
  background-repeat: no-repeat;
  background-position: center center;
  background-size: cover;
  
}

.blogslider-text, .blogslider-text a
{
    color:white;text-shadow:0 0 3px gray;
}

.blogslider-text a:hover
{
    text-decoration: underline;
}


.blogslider-arrow-insider .slick-prev {
  left: 100px;
  z-index: 1;
}
.blogslider-arrow-insider .slick-next {
  right: 100px;
}

e. Test it

By now, your block has been coded properly. Test and check with Chrome/Firefox developer tools to see if there's any Javascript error, etc. The following screenshot is what it looks like when you use it with the companyprofile theme.

Slick Slider plugin

3. Publish it to Extensions Directory (registration required)

Once you're done, go back to Plugin Creator and click Export ZIP.

[Download the full source code of this tutorial (75 Kb)].