piotrpolak/pepiscms

View on GitHub
pepiscms/application/libraries/MenuRendor.php

Summary

Maintainability
D
2 days
Test Coverage
<?php

/**
 * PepisCMS
 *
 * Simple content management system
 *
 * @package             PepisCMS
 * @author              Piotr Polak
 * @copyright           Copyright (c) 2007-2018, Piotr Polak
 * @license             See license.txt
 * @link                http://www.polak.ro/
 */

defined('BASEPATH') or exit('No direct script access allowed');

/**
 * Menu Rendor used in admin panel
 *
 * @since 0.1.0
 */
class MenuRendor extends ContainerAware
{
    const HAS_SUBMENU_CSS_CLASS = 'hasSubmenu';
    const ACTIVE_CSS_CLASS = 'active';

    const PROP_IS_ACTIVE = 'is_active';
    const PROP_EXTRA_CSS_CLASSES = 'extra_css_classes';
    const PROP_SUBMENU = 'submenu';
    const PROP_ICON_URL = 'icon_url';
    const PROP_CONTROLLER = 'controller';
    const PROP_MODULE = 'module';
    const PROP_METHOD = 'method';
    const PROP_PARAMS = 'params';
    const PROP_APPEND_LANGUAGE_CODE = 'append_language_code';
    const PROP_TITLE = 'title';
    const PROP_LABEL = 'label';
    const PROP_URL = 'url';
    const PROP_MAINMENU = 'mainmenu';
    const DEFAULT_METHOD = 'index';
    const PROP_ICON_PATH = 'icon_path';
    const PROP_SHOW_LABEL = 'show_label';
    const PROP_DESCRIPTION = 'description';
    private $use_cache = true;

    /**
     * Default constructor, empty
     */
    public function __construct()
    {
        $this->load->config('menu');
        $this->load->model('Module_model');
        $this->load->library('ModuleRunner');
    }

    /**
     * Returns whether to use cache
     *
     * @return bool
     */
    public function isUseCache()
    {
        return $this->use_cache;
    }

    /**
     * Sets whether to use cache
     *
     * @param bool $use_cache
     */
    public function setUseCache($use_cache)
    {
        $this->use_cache = $use_cache;
    }

    /**
     * Renders admin menu
     *
     * @param string $controller
     * @param string $method
     * @param string $language_code
     * @param bool $pull_submenu_from_controller
     * @param bool $current_module
     * @return string
     */
    public function render($controller, $method, $language_code = '', $pull_submenu_from_controller = false, $current_module = false)
    {
        $menu = $this->config->item('menu');

        $pull_submenu_from_controller = $this->ensurePulledSumbenuFromControllerIsSet($controller, $pull_submenu_from_controller);

        $current_module = $this->ensureCurrentModuleIsSet($current_module);

        $cache_var_name = $this->computeCacheVariableName($controller, $method, $language_code, $pull_submenu_from_controller, $current_module);
        if ($cached_menu = $this->getMenuFromCache($cache_var_name)) {
            return $cached_menu;
        }


        // Template for each of the menu elements
        $menu_map_item_template = array(
            self::PROP_URL => '',
            self::PROP_LABEL => '',
            self::PROP_TITLE => '',
            self::PROP_ICON_URL => '',
            self::PROP_IS_ACTIVE => '',
            self::PROP_EXTRA_CSS_CLASSES => array(),
            self::PROP_SUBMENU => array(),
        );


        // For each one of the main menu items
        $menu_map = array();
        foreach ($menu[self::PROP_MAINMENU] as $item) {
            if ($this->shouldSkipMenuItem($item)) {
                continue;
            }

            $menu_map[] = $this->buildMenuItem($language_code, $pull_submenu_from_controller, $current_module, $menu_map_item_template, $item);
        }

        // Here we are going with the modules

        // Checking if one can run modules
        if (SecurityManager::hasAccess('module', 'run')) {
            // Building array of modules to which the user has granted access
            $modules = $this->auth->getSessionVariable('access_modules');
            if (!$modules) {
                $all_modules = ModuleRunner::getInstalledModulesDisplayedInMenuCached();
                $modules = array(); // Resetting type

                // Checking access to each of the modules
                foreach ($all_modules as $module) {
                    if (!SecurityManager::hasAccess($module->name, self::DEFAULT_METHOD, $module->name)) {
                        continue;
                    }
                    $modules[] = $module;
                }
                // Saving some memory
                unset($all_modules);

                // Persisting variable
                $this->auth->setSessionVariable('access_modules', $modules);
            }

            // Building main menu and submenu modules arrays
            $main_menu_modules = array();
            $sub_menu_modules = array();
            foreach ($modules as $module) {
                if ($module->parent_module_id !== null) {
                    if (!isset($sub_menu_modules[$module->parent_module_id])) {
                        $sub_menu_modules[$module->parent_module_id] = array();
                    }
                    $sub_menu_modules[$module->parent_module_id][] = $module;
                } else {
                    $main_menu_modules[] = $module;
                }
            }

            // For each one of main menu modules
            foreach ($main_menu_modules as $module) {
                // Initialize item out of the template
                $menu_map_item = $menu_map_item_template; // RESET

                // Reading module label
                $module_label = $this->Module_model->getModuleLabel($module->name, $this->lang->getCurrentLanguage());
                if (!$module_label) {
                    $module_label = $module->label;
                }

                // Reading module description
                $module_description = $this->Module_model->getModuleDescription($module->name, $this->lang->getCurrentLanguage());
                if ($module_description) {
                    $menu_map_item[self::PROP_TITLE] = $module_description;
                }

                // Assigning remaining menu item attributes
                $menu_map_item[self::PROP_ICON_URL] = module_icon_small_url($module->name);
                $menu_map_item[self::PROP_IS_ACTIVE] = $current_module == $module->name;
                $menu_map_item[self::PROP_LABEL] = $module_label;
                $menu_map_item[self::PROP_URL] = module_url($module->name);

                if (!$module->name) {
                    $items = &$menu[$pull_submenu_from_controller];
                } else {
                    $items = $this->Module_model->getModuleAdminSubmenuElements($module->name, $this->lang->getCurrentLanguage());
                }

                // For modules that are under the current modules if case
                if (isset($sub_menu_modules[$module->module_id])) {
                    foreach ($sub_menu_modules[$module->module_id] as $element) {
                        $items[] = array(
                            self::PROP_MODULE => $element->name,
                            self::PROP_CONTROLLER => $element->name,
                            self::PROP_METHOD => self::DEFAULT_METHOD,
                            self::PROP_LABEL => $this->Module_model->getModuleLabel($element->name, $this->lang->getCurrentLanguage()),
                            self::PROP_ICON_URL => module_icon_small_url($element->name),
                        );
                    }
                }

                // For every items
                if ($items && count($items) > 0) {
                    foreach ($items as $item) {
                        if (!SecurityManager::hasAccess($item[self::PROP_CONTROLLER], isset($item[self::PROP_METHOD]) ? $item[self::PROP_METHOD] : self::DEFAULT_METHOD, $module->name)) {
                            continue;
                        }

                        $menu_map_item[self::PROP_SUBMENU][] = $this->computeSubmenuItem($controller, $method, $language_code, $pull_submenu_from_controller, $menu_map_item_template, $item, $module, $menu_map_item);
                    }
                }

                $menu_map[] = $menu_map_item;
            }
        }

        $output = $this->renderMenu($menu_map);

        $this->setMenuCache($cache_var_name, $output);
        return $output;
    }


    /**
     * @param $module
     * @param $item
     * @return string
     */
    private function buildSubmenuUrl($module, $item, $language_code)
    {
        if (!$module->name) {
            $submenu_url = admin_url() . $item[self::PROP_CONTROLLER] . '/';
        } elseif (isset($item[self::PROP_MODULE]) && $item[self::PROP_MODULE]) {
            $submenu_url = module_url($item[self::PROP_MODULE]);
        } else {
            $submenu_url = module_url($module->name);
        }

        if (isset($item[self::PROP_METHOD])) {
            $submenu_url .= $item[self::PROP_METHOD] . '/';
        } else {
            $submenu_url .= self::DEFAULT_METHOD . '/';
        }

        if (isset($item[self::PROP_PARAMS])) {
            $submenu_url .= $item[self::PROP_PARAMS] . '/';
        }

        if (isset($item[self::PROP_APPEND_LANGUAGE_CODE]) && $item[self::PROP_APPEND_LANGUAGE_CODE]) {
            $submenu_url .= 'language_code-' . $language_code;
        }
        return $submenu_url;
    }

    /**
     * @param $controller
     * @param $pull_submenu_from_controller
     * @param $item
     * @param $method
     * @return bool
     */
    private function isSubmenuActive($controller, $pull_submenu_from_controller, $item, $method)
    {
        if ($method == $item[self::PROP_METHOD]) {
            if ($pull_submenu_from_controller && $pull_submenu_from_controller == $item[self::PROP_CONTROLLER]) {
                return true;
            } elseif ($item[self::PROP_CONTROLLER] == $controller) {
                return true;
            }
        }

        return false;
    }

    /**
     * @param $item
     * @param $default
     * @return bool
     */
    private function getMenuIconPath($item, $default)
    {
        return isset($item[self::PROP_ICON_PATH]) && $item[self::PROP_ICON_PATH] ? $item[self::PROP_ICON_PATH] : $default;
    }

    /**
     * @param $language_code
     * @param $item
     * @return string
     */
    private function getMenuUrl($language_code, $item)
    {
        $url = admin_url() . $item[self::PROP_CONTROLLER] . '/';
        if (isset($item[self::PROP_METHOD])) {
            $url .= $item[self::PROP_METHOD] . '/';
        }
        if (isset($item[self::PROP_APPEND_LANGUAGE_CODE]) && $item[self::PROP_APPEND_LANGUAGE_CODE] && $language_code) {
            $url .= 'language_code-' . $language_code . '/';
        }
        return $url;
    }

    /**
     * @param $item
     * @return bool
     */
    private function shouldSkipMenuItem($item)
    {
        if (!SecurityManager::hasAccess($item[self::PROP_CONTROLLER], isset($item[self::PROP_METHOD]) ? $item[self::PROP_METHOD] : self::DEFAULT_METHOD)) {
            return true;
        }
        if ($item[self::PROP_CONTROLLER] == 'utilities' && !$this->config->item('cms_enable_utilities')) {
            return true;
        }
        if ($item[self::PROP_CONTROLLER] == 'ajaxfilemanager' && (!$this->config->item('cms_enable_filemanager') || !$this->config->item('feature_is_enabled_filemanager'))) {
            return true;
        }
        return false;
    }

    /**
     * @param $cache_var_name
     * @return bool|mixed
     */
    private function getMenuFromCache($cache_var_name)
    {
        if ($this->use_cache && $this->auth->getSessionVariable($cache_var_name)) {
            return $this->auth->getSessionVariable($cache_var_name);
        }
        return false;
    }

    /**
     * @param $current_module
     * @return bool|mixed
     */
    private function ensureCurrentModuleIsSet($current_module)
    {
        if (!$current_module) {
            $current_module = is_object($this->modulerunner) ? $this->modulerunner->getRunningModuleName() : false;
        }
        return $current_module;
    }

    /**
     * @param $controller
     * @param $pull_submenu_from_controller
     * @return mixed
     */
    private function ensurePulledSumbenuFromControllerIsSet($controller, $pull_submenu_from_controller)
    {
        if (!$pull_submenu_from_controller) {
            $pull_submenu_from_controller = $controller;
        }
        return $pull_submenu_from_controller;
    }

    /**
     * @param $language_code
     * @param $pull_submenu_from_controller
     * @param $current_module
     * @param array $menu_map_item_template
     * @param $item
     * @return array
     */
    private function buildMenuItem($language_code, $pull_submenu_from_controller, $current_module, array $menu_map_item_template, $item)
    {
        $new_item = $menu_map_item_template; // RESET

        if (isset($item[self::PROP_SHOW_LABEL]) && !$item[self::PROP_SHOW_LABEL]) {
            $new_item[self::PROP_EXTRA_CSS_CLASSES][] = 'no-label';
        }

        $new_item[self::PROP_IS_ACTIVE] = $this->isMenuActive($pull_submenu_from_controller, $current_module, $item);
        $new_item[self::PROP_ICON_URL] = $this->getMenuIconPath($item, false);
        $new_item[self::PROP_URL] = $this->getMenuUrl($language_code, $item);
        $new_item[self::PROP_TITLE] = $this->getMenuTitle($item);
        $new_item[self::PROP_LABEL] = $this->getMenuLabel($item);

        return $new_item;
    }

    /**
     * @param $controller
     * @param $method
     * @param $language_code
     * @param $pull_submenu_from_controller
     * @param $current_module
     * @return string
     */
    private function computeCacheVariableName($controller, $method, $language_code, $pull_submenu_from_controller, $current_module)
    {
        return 'menu_c:' . $controller . '_m:' . $method . '_lc:' . $language_code . '_psfc:' . $pull_submenu_from_controller . '_cm:' . $current_module . '_lng:' . $this->lang->getCurrentLanguage();
    }

    /**
     * @param $cache_var_name
     * @param $output
     */
    private function setMenuCache($cache_var_name, $output)
    {
        if ($this->use_cache) {
            $this->auth->setSessionVariable($cache_var_name, $output);
        }
    }

    /**
     * @param $controller
     * @param $method
     * @param $language_code
     * @param $pull_submenu_from_controller
     * @param array $menu_map_item_template
     * @param $item
     * @param $module
     * @param array $menu_map_item
     * @return array
     */
    private function computeSubmenuItem($controller, $method, $language_code, $pull_submenu_from_controller,
                                        array $menu_map_item_template, $item, $module, array $menu_map_item)
    {
        $new_item = $menu_map_item_template; // RESET

        // The following if prevents 2 elements to be active when $pull_submenu_from_controller is specified
        $new_item[self::PROP_IS_ACTIVE] = $this->isSubmenuActive($controller, $pull_submenu_from_controller, $item, $method);
        $new_item[self::PROP_URL] = $this->buildSubmenuUrl($module, $item, $new_item, $language_code);
        $new_item[self::PROP_TITLE] = $this->getMenuTitle($item);
        $new_item[self::PROP_ICON_URL] = $this->getMenuIconPath($item, $menu_map_item[self::PROP_ICON_URL]);
        $new_item[self::PROP_LABEL] = $this->getMenuLabel($item);

        return $new_item;
    }

    /**
     * @param $item
     * @return string|null
     */
    private function getMenuTitle($item)
    {
        if (isset($item[self::PROP_DESCRIPTION])) {
            return $this->lang->line($item[self::PROP_DESCRIPTION]);
        }
        return '';
    }

    /**
     * @param $item
     * @return string|null
     */
    private function getMenuLabel($item)
    {
        if (isset($item[self::PROP_LABEL]) && $item[self::PROP_LABEL]) {
            return $this->lang->line($item[self::PROP_LABEL]);
        }
        return '';
    }

    /**
     * @param $pull_submenu_from_controller
     * @param $current_module
     * @param $item
     * @return bool
     */
    private function isMenuActive($pull_submenu_from_controller, $current_module, $item)
    {
        return $item[self::PROP_CONTROLLER] == $pull_submenu_from_controller || $item[self::PROP_CONTROLLER] == $current_module;
    }

    /**
     * @param array $menu_items
     * @return string
     */
    private function renderMenu(array $menu_items)
    {
        return get_instance()->load->view('templates/menurendor_menu.php', array('menu_items' => $menu_items), true);
    }
}