kai-jacobsen/kontentblocks

View on GitHub
core/Areas/AreaRegistry.php

Summary

Maintainability
F
3 days
Test Coverage
<?php

namespace Kontentblocks\Areas;

use Kontentblocks\Backend\Environment\PostEnvironment;
use Kontentblocks\Backend\EditScreens\ScreenManager;
use Kontentblocks\Backend\Storage\ModuleStorage;
use Kontentblocks\Kontentblocks;
use Kontentblocks\Language\I18n;
use Kontentblocks\Utils\_K;
use Kontentblocks\Utils\Utilities;

/**
 * Area Registry
 * -----------------------------
 * Collects all registered areas
 * Collects all registered area templates
 * Handles the connection Module > Area
 */
class AreaRegistry
{

    /**
     * All register areas
     * @var array
     * @since 0.1.0
     */
    protected $areas = array();

    /**
     * Global areas with context 'side' filtered
     * @var array
     * @since 0.1.0
     */
    protected $globalSidebars = array();

    /**
     * all global areas filtered
     * @var array
     * @since 0.1.0
     */
    protected $globalAreas = array();

    /**
     * area templates
     * @var array
     * @since 0.1.0
     */
    protected $templates = array();

    /**
     * @var AreaDynamicManager
     */
    protected $areaDynamicManager;


    protected $reserved = array();


    /**
     * Constructer
     */
    public function __construct()
    {
        $this->areaDynamicManager = new AreaDynamicManager();
        // action is triggerd by AreaDynamicManager setup
        // to make sure external areas are properly setup
        add_action('kb.areas.dynamic.setup', array($this, 'init'));
        add_action('wp_footer', array($this, 'setupJSON'), 8);
        $this->addInternalArea();
    }

    /**
     * Even if an area is not an actual requirement for the
     * consitency of a module, most frontend related code expects
     * at least an area model for internal control purposes.
     * If a module is not attached to a valid area, it's internally added
     * to a dummy area, which is created here.
     * This dummy area is ignored from any UI and does not exist publicly.
     */
    private function addInternalArea()
    {
        $this->addArea(
            array(
                'id' => '_internal',
                'internal' => true,
                'context' => 'normal'
            )
        );
    }

    /**
     * Adds an area to the registry
     * Merges default params with provided params
     * $Manual indicates if the area has been registered by code (true) or
     * was added through the admin interface (false)
     *
     * @param array $args
     * @param bool $manual
     * @return AreaProperties
     * @since 0.1.0
     */
    public function addArea($args, $manual = true)
    {
        if (!empty($args['id'])) {
            $args['id'] = sanitize_title($args['id']);
        }
        if (isset($args['dynamic']) && $args['dynamic']) {
            if (!isset($args['context'])) {
                $args['context'] = 'dynamic';
            }
        }

        $area = new AreaProperties($args);
        // merge defaults with provided args
        if ($area->dynamic === true && $manual) {
            $this->areaDynamicManager->add($area);
        }

        $this->areas[$area->id] = $area;
        $this->preFilterAreas($this->areas[$area->id]);
        return $area;
    }

    /**
     * Sort areas to common used collections
     *
     * @param $area
     *
     * @since 0.1.0
     */
    public function preFilterAreas($area)
    {
        if ($area->dynamic === true
        ) {
            if ($area->context === 'side') {
                $this->globalSidebars[$area->id] = $area;
            }

            $this->globalAreas[$area->id] = $area;

        }
    }

    /**
     * Init Function
     * Gets areas which were added by the user through the admin interface
     * Adds those areas to the directory
     *
     * @return void
     * @since 0.1.0
     */
    public function init()
    {
        $areas = wp_cache_get('dynamicareas', Utilities::getCacheGroup());

        if ($areas === false) {
            $areas = get_posts(
                array(
                    'post_type' => 'kb-dyar',
                    'posts_per_page' => -1,
                    'update_post_meta_cache' => false,
                    'suppress_filters' => false // let multilanguage plugins set the right context
                )
            );
            wp_cache_add('dynamicareas', $areas, Utilities::getCacheGroup(), 60 * 60 * 6);
        }


        if (is_array($areas) && !empty($areas)) {
            foreach ($areas as $areapost) {
                $storage = new ModuleStorage($areapost->ID);
                $area = $storage->getDataProvider()->get('_area');
                $area['parent_id'] = $areapost->ID; //deprecated old key
                $area['parentObjectId'] = $areapost->ID;
                $dynamicAreas[] = $area;
            }
        }
        if (!empty($dynamicAreas)) {
            foreach ($dynamicAreas as $area) {
                $this->addArea($area, false);
            }
        }
        _K::info('Dynamic areas queried and setup');
        do_action('kb.areas.setup');
    }


    /**
     * Getter for global sidebars
     * @return array
     * @since 0.1.0
     */
    public function getGlobalSidebars()
    {
        return $this->globalSidebars;
    }

    /**
     * Returns all registered area templates
     * @return array of template definitions
     * @since 0.1.0
     */
    public function getTemplates()
    {
        return $this->templates;

    }

    /**
     * Registers an area template and adds it to the area templates array
     *
     * @param array $args
     *
     * @since 0.1.0
     */
    public function addTemplate($args)
    {

        $defaults = array(
            'templateClass' => '',
            'layout' => array(),
            'cycle' => false,
            'last-item' => false,
            'wrap' => false
        );

        if (!empty($args['id'])) {
            $this->templates[$args['id']] = wp_parse_args($args, $defaults);
        }

    }

    /**
     * Get a template by id
     *
     * @param string $tplId
     *
     * @return null | array of params
     * @since 0.1.0
     */
    public function getTemplate($tplId)
    {
        if (isset($this->templates[$tplId])) {
            return $this->templates[$tplId];
        }
        return null;

    }

    /**
     * Check if a template exists by id
     * @param $templateID
     * @return bool
     * @since 0.1.0
     */
    public function templateExists($templateID)
    {
        if (isset($this->templates[$templateID])) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * Modules can connect themselves to an area by specifying the connect parameter
     * This method handles the connection by adding the modules classname to the
     * assigned modules array of the area
     *
     * A Module can be added to all registered areas by setting connect to 'any'
     * A Module can be added to all registered areas of an specific context by setting connect
     * to one or more of the following words : 'top', 'normal', 'side', 'bottom'
     *
     * @param string $classname
     * @param array $args module args
     * @return bool
     */
    public function connect($classname, $args)
    {
        $setting = $args['settings']['connect'];
        $postTypes = get_post_types_by_support('kontentblocks');
        if (empty($setting)) {
            return false;
        }
        if ($setting === 'any') {
            /** @var \Kontentblocks\Areas\AreaProperties $area */
            foreach ($this->areas as $area) {
                $area->connect($classname);
            }
        } else if (is_array($setting)) {
            foreach ($setting as $target) {
                // check for context
                if (in_array($target, array_keys(ScreenManager::getDefaultContextLayout()))) {
                    foreach ($this->getAreasByContext($target) as $connection) {
                        $args['settings']['connect'] = array($connection->id);
                        $this->connect($classname, $args);
                    }
                    // check for page template
                } else if (is_string($target) && (strpos($target, '.php') !== false || $target === 'default')) {
                    foreach ($this->getAreasByPageTemplate($target) as $tplcon) {
                        $args['settings']['connect'] = array($tplcon->id);
                        $this->connect($classname, $args);
                    }
                } else if ($target === 'global') {
                    foreach ($this->getGlobalAreas() as $connection) {
                        $connection->connect($classname);
                    }
                } else if (in_array($target, $postTypes)) {
                    foreach ($this->getAreasByPostType($target) as $area) {
                        $args['settings']['connect'] = array($area->id);
                        $this->connect($classname, $args);
                    }
                } else {
                    // its not a context, not a page template
                    // area id not existent
                    if (empty($this->areas[$target])) {
                        continue;
                    }
                    //area id exists, connect
                    /** @var \Kontentblocks\Areas\AreaProperties $area */
                    $area = $this->areas[$target];
                    $area->connect($classname);
                }
            }
        }
    }

    /**
     * Retrieve all areas in given context
     *
     * @param string $context
     *
     * @return array
     * @since 0.1.0
     */
    public function getAreasByContext($context)
    {
        return array_filter(
            $this->areas,
            function ($area) use ($context) {
                return ($area->context === $context);
            }
        );
    }

    /**
     * Get area which were assigned to a page template
     *
     * @param $tpl
     *
     * @return array
     */
    public function getAreasByPageTemplate($tpl)
    {
        return array_filter(
            $this->areas,
            function ($area) use ($tpl) {
                return Utilities::strposa($area->pageTemplates, $tpl);
            }
        );
    }

    /**
     * getter for global areas
     * @return array
     * @since 0.1.0
     */
    public function getGlobalAreas()
    {
        return $this->globalAreas;
    }

    /**
     * @param $pt
     * @return array
     */
    public function getAreasByPostType($pt)
    {
        return array_filter(
            $this->areas,
            function ($area) use ($pt) {
                return Utilities::strposa($area->postTypes, $pt);
            }
        );
    }

    /**
     * Filters registered areas by post settings
     * This needs an instance of the Environment Class to provide
     * all necessary informations for the filter
     * Areas can be limited to post types and/or page templates
     *
     * @param \Kontentblocks\Backend\Environment\PostEnvironment $environment
     *
     * @return array
     */
    public function filterForPost(PostEnvironment $environment)
    {

        $pageTemplate = $environment->getPageTemplate();
        $postType = $environment->getPostType();
        $areas = array();
        if ($postType === 'kb-gmd') {
            return array('global-module' => $this->getArea('global-module'));
        }
        if ($postType === 'kb-dyar') {
            return $this->getGlobalAreas();
        }

        // loop through areas and find all which are attached to this post type and/or page template
        /** @var \Kontentblocks\Areas\AreaProperties $area */
        foreach ($this->areas as $area) {

            $validArea = null;

            if (empty($area->context)) {
                $area->context = 'side';
            }
            if ((!empty($area->pageTemplates)) && (!empty($area->postTypes))) {
                if (Utilities::strposa($pageTemplate, $area->pageTemplates) && in_array(
                        $postType,
                        $area->postTypes
                    )
                ) {
                    $validArea = $area;
                }
            } elseif (!empty($area->pageTemplates)) {
                if (Utilities::strposa($pageTemplate, $area->pageTemplates)) {
                    $validArea = $area;
                }
            } elseif (!empty($area->postTypes)) {
                if (in_array($postType, $area->postTypes)) {
                    $validArea = $area;
                }
            }

            if (is_callable($area->showCallback)) {
                $res = call_user_func_array($area->showCallback, array($environment, $area));
                if ($res === true) {
                    $validArea = $area;
                }
            }


            if (!is_null($validArea)) {
                $areas[$area->id] = $validArea;
            }
        }
        $sareas = self::orderBy($areas, 'order');
        return $sareas;

    }

    /**
     * Returns an area from the registry by id
     *
     * @param string $areaId
     *
     * @return mixed null if area is not set | area array args if area is set
     * @since 0.1.0
     */
    public function getArea($areaId)
    {
        if (isset($this->areas[$areaId])) {
            array_push($this->reserved, $areaId);
            return $this->areas[$areaId];
        } else {
            return null;
        }

    }

    /**
     * Private helper method to order the areas array by a specified field
     * i.e. order
     * @param array $areas
     * @param string $field
     * @return array
     */
    private function orderBy($areas, $field)
    {


        uasort($areas, function ($a, $b) use ($field) {
            return strnatcmp($a->$field, $b->$field);
        });
        return $areas;
    }

    /**
     * Check if an area id already exists
     *
     * @param $areaId
     * @return bool
     */
    public function areaExists($areaId)
    {
        return isset($this->areas[$areaId]);
    }

    /**
     * Check if area is dynamic
     * @param $areaId
     * @return mixed
     */
    public function isDynamic($areaId)
    {
        $area = $this->getArea($areaId);

        if (is_object($area)) {
            return $area->dynamic;
        }

        return false;

    }

    /**
     * register area templates to frontend access
     * register dummy area for frontend access
     */
    public function setupJSON()
    {
        Utilities::setupCats();
        Kontentblocks::getService('utility.jsontransport')->registerData('AreaTemplates', null, $this->templates);
        Kontentblocks::getService('utility.jsontransport')->registerArea($this->getArea('_internal'));
    }

}