e107inc/e107

View on GitHub
e107_handlers/menu_class.php

Summary

Maintainability
A
0 mins
Test Coverage
F
31%
<?php
/*
 * e107 website system
 *
 * Copyright (C) 2008-2013 e107 Inc (e107.org)
 * Released under the terms and conditions of the
 * GNU General Public License (http://www.gnu.org/licenses/gpl.txt)
 *
 * e107 Menu Class
 *
*/

if(!defined('e107_INIT'))
{
    exit();
}

/**
 * Retrieve and render site menus
 *
 * @package e107
 * @category e107_handlers
 * @version 1.0
 * @author Cameron
 * @copyright Copyright (c) 2009, e107 Inc.
 *
 */
class e_menu
{
    /**
     * Runtime cached menu data
     *
     * @var array
     */
    public $eMenuActive = array();

    /**
     * Visibility check cache
     *
     * @var array
     */
    protected $_visibility_cache = array();

    /**
     * @var null
     */
    protected $_current_menu = null;

    /**
     * @var array
     */
    protected $_current_parms = array();

    /**
     * Params of all active menus.
     * @var array
     */
    protected $_menu_parms = array();

    /**
     * Constructor
     *
     */
    public function __construct()
    {
    }

    /**
     * Retrieve menus, check visibility against
     * current user classes and current page url
     *
     */
    public function init()
    {
        global $_E107;

        if(!empty($_E107['cli']))
        {
            return;
        }
        
        //    print_a($eMenuArea);
        if(varset($_SERVER['E_DEV_MENU']) === 'true') // New in v2.x Experimental
        {
            $layouts = e107::getPref('menu_layouts');
            if(!is_array($layouts))
            {
                $converted = $this->convertMenuTable();
                e107::getConfig()->set('menu_layouts', $converted)->save();
            }
            
            $eMenuArea = $this->getData(THEME_LAYOUT);
            //print_a($eMenuArea);
        }
        else // standard DB 'table' method.
        {
            $eMenuArea = $this->getDataLegacy();
        }

        
        
        $total = array();
        foreach($eMenuArea as $area => $val)
        {
            foreach($val as $row)
            {
                if($this->isVisible($row))
                {
                    $path = str_replace("/", "", $row['menu_path']);
                    if(!isset($total[$area]))
                    {
                        $total[$area] = 0;
                    }
                    $this->eMenuActive[$area][] = $row;

                    if(!empty($row['menu_parms']))
                    {
                        $key = $row['menu_name'];
                        $this->_menu_parms[$area][$key][] = $row['menu_parms'];
                    }

                    $total[$area]++;
                }
            }
        }

    
        


        e107::getRender()->eMenuTotal = $total;
    }

    /** 
     * Convert from v1.x e107_menu table to v2.x $pref format. 
     */
    function convertMenuTable()
    {
        $sql = e107::getDb();
        
        $sql->select('menus','*','menu_location !=0 ORDER BY menu_location,menu_order');
        $data = array();

        while($row = $sql->fetch())
        {
            $layout     = vartrue($row['menu_layout'],'default');    
            $location     = $row['menu_location'];
            $data[$layout][$location][] = array('name'=>$row['menu_name'],'class'=> (int)$row['menu_class'],'path'=>$row['menu_path'],'pages'=>$row['menu_pages'],'parms'=>$row['menu_parms']);
        }
        
        return $data;        
    }


    /**
     * Return the preferences/parms for the current menu.
     * @return array
     */
    public function pref()
    {
        return (empty($this->_current_parms)) ?  array() : $this->_current_parms;
    }


    /**
     * Return the parameters of an active Menu.
     * @param string $menuName
     * @param int $area
     * @example $parms = $tmp->getParams('news_months_menu',1);
     * @return array|bool
     */
    public function getParams($menuName, $area)
    {

        if(empty($area) || empty($menuName))
        {
            return false;
        }

        if(!empty($this->_menu_parms[$area][$menuName]))
        {
            $arr = array();
            foreach($this->_menu_parms[$area][$menuName] as $val)
            {
                $arr[] = e107::unserialize($val);
            }

            return $arr;
        }

        return false;
    }


    /**
     * Experimental V2 Menu Re-Write - retrieve Menu data from $pref['menu_layouts']
     */
    protected function getData($layout)
    {
        $mpref = e107::getPref('menu_layouts');
        
        if(!varset($mpref[$layout]))
        {
            return array();    
        }

        $ret = array();
        
        foreach($mpref[$layout] as $area=>$v)
        {
            $c = 1;
                    
            foreach($v as $val)
            {
                $class = (int)$val['class'];
                
                if(!check_class($class))
                {
                    continue;    
                }
                
                $ret[$area][] = array(
                    'menu_id'        => $c,
                    'menu_name'        => $val['name'],
                    'menu_location'    => $area,
                    'menu_class'    => $class,
                    'menu_order'    => $c,
                    'menu_pages'    => $val['pages'],
                    'menu_path'        => $val['path'],
                    'menu_layout'    =>  '',
                    'menu_parms'    => $val['parms']

                );

                $c++;
            }
                
            
        }
        
        
            // print_a($ret);
                
        return $ret;    
        
    }


    /**
     * Set Parms for a specific menu.
     * @param string $plugin ie. plugin folder name.
     * @param string $menu menu name. including the _menu but not the .php
     * @param array $parms
     * @param string|int $location default 'all' or  a menu area number..
     * @return int number of records updated or false.
     */
    public function setParms($plugin, $menu, $parms=array(), $location = 'all')
    {
        $qry = 'menu_parms="'.e107::serialize($parms).'" WHERE menu_parms="" AND menu_path="'.$plugin.'/" AND menu_name="'.$menu.'" ';
        $qry .= ($location !== 'all') ? 'menu_location='. (int)$location : '';

        return  e107::getDb()->update('menus', $qry);
    }


    /**
     * Saving of parms from e_menu.php configuration.
     * @param int $id menu_id
     * @param array $parms posted values. 
     * @return mixed
     */
    public function updateParms($id, $parms)
    {
        $model = e107::getModel();
        $model->setModelTable("menus");
        $model->setFieldIdName("menu_id");
        $model->setDataFields(array('menu_parms'=>'json'));

        $model->load($id, true);

        $menu_path = $model->get('menu_path');
        $menu_path = rtrim($menu_path, '/');

        $menu_name = $model->get('menu_name');
        $menu_name = substr($menu_name,0,-5);

        if(!$obj = e107::getAddon($menu_path,'e_menu'))
        {
            return false;
        }

        if(!$fields = e107::callMethod($obj,'config',$menu_name))
        {
            return false;
        }

        if($d = $model->get('menu_parms'))
        {
            $prev = e107::unserialize($d);
        }
        else
        {
            $prev = [];
        }

        foreach($fields as $fld => $var)
        {
            if(isset($parms[$fld]))
            {
                if(!empty($var['multilan']))
                {
                    if(!empty($prev[$fld])) // load previous language values.
                    {
                        $model->setPostedData('menu_parms/'.$fld, $prev[$fld]);
                    }

                    foreach($parms[$fld] as $lang => $val) // add posted language values.
                    {
                        $model->setPostedData('menu_parms/'.$fld.'/'.$lang, $val);
                    }
                }
                else
                {
                    $model->setPostedData('menu_parms/'.$fld, $parms[$fld]);
                }

            }

        }

    //    file_put_contents(e_LOG."menuParmsAjax.log", print_r($parms,true));

        return $model->save();

    }




    /**
     * Add a Menu to the Menu Table.
     * @param string $plugin folder name
     * @param string $menufile name without the .php
     * @return bool|int
     */
    public function add($plugin, $menufile)
    {
        $sql = e107::getDb();

        if(empty($plugin) || empty($menufile))
        {
            return false;
        }

        if($sql->select('menus', 'menu_id' , 'menu_path="'.$plugin.'/" AND menu_name="'.$menufile.'" LIMIT 1'))
        {
            return false;
        }

        $insert = array(
            'menu_id'       => 0,
            'menu_name'     => $menufile,
            'menu_location' => 0,
            'menu_order'    => 0,
            'menu_class'    => 0,
            'menu_pages'    => 0,
            'menu_path'     => $plugin."/",
            'menu_layout'   => '',
            'menu_parms'    => ''
        );

        return  $sql->insert('menus', $insert);


    }


    /**
     * Remove a menu from the Menu table.
     * @param string $plugin folder name
     * @param string $menufile
     * @return int
     */
    public function remove($plugin, $menufile=null)
    {
        $qry = 'menu_path="'.$plugin.'/" ';

        if(!empty($menufile))
        {
            $qry .= ' AND menu_name="'.$menufile.'" ';
        }

        return e107::getDb()->delete('menus', $qry);
    }


    /** 
     * Function to retrieve Menu data from tables.
     */
    private function getDataLegacy()
    {
        $sql = e107::getDb();
        $menu_layout_field = THEME_LAYOUT != e107::getPref('sitetheme_deflayout') ? THEME_LAYOUT : "";
        
    //    e107::getCache()->CachePageMD5 = md5(e_LANGUAGE.$menu_layout_field); // Disabled by line 93 of Cache class. 
        //FIXME add a function to the cache class for this.

        if(!defined('PREVIEWTHEME'))
        {
            $cacheData = e107::getCache()->retrieve_sys("menus_".USERCLASS_LIST."_".md5(e_LANGUAGE.$menu_layout_field));
            $menu_data = e107::unserialize($cacheData);
        }

        $eMenuArea = array();
        // $eMenuList = array();
        //    $eMenuActive    = array();  // DEPRECATED
        
        
        if(empty($menu_data) || !is_array($menu_data))
        {
            $menu_qry = 'SELECT * FROM #menus WHERE menu_location > 0 AND menu_class IN ('.USERCLASS_LIST.')  ';
            $menu_qry .= !defined('PREVIEWTHEME') ? 'AND menu_layout = "'.$menu_layout_field.'" ' : '';
            $menu_qry .= 'ORDER BY menu_location,menu_order';
            
            if($sql->gen($menu_qry))
            {
                while($row = $sql->fetch())
                {
                    $eMenuArea[$row['menu_location']][] = $row;
                }
            }
            
            $menu_data['menu_area'] = $eMenuArea;

            $menuData = e107::serialize($menu_data,'json');

            e107::getCache()->set_sys('menus_'.USERCLASS_LIST.'_'.md5(e_LANGUAGE.$menu_layout_field), $menuData);
            
        }
        else
        {
            $eMenuArea = $menu_data['menu_area'];
        }    
        
        
        
        return $eMenuArea;
    }

    /**
     * Returns true if a menu is currently active. 
     * @param string $menuname (without the '_menu.php' )
     */
    public function isLoaded($menuname)
    {
        if(empty($menuname))
        {
            return false;    
        }
        
        foreach($this->eMenuActive as $area)
        {
            foreach($area as $menu)
            {
                if($menu['menu_name'] == $menuname."_menu")
                {
                    return true;    
                }
                
            }
        
        }    
        
        return false;
    }


    /**
     * @return bool
     */
    protected function isFrontPage()
    {
        return e_REQUEST_SELF == SITEURL;
    }



    /**
     * Check visibility of a menu against URL
     *
     * @param array $row menu data
     * @return boolean
     */
    protected function isVisible($row, $url = '')
    {
        $iD = varset($row['menu_id']);

        if(isset($this->_visibility_cache[$iD]))
        {
            return $this->_visibility_cache[$iD];
        }

        $show_menu = TRUE;
        $tp = e107::getParser();
        if($row['menu_pages'])
        {
            list ($listtype, $listpages) = explode("-", $row['menu_pages'], 2);
            $pagelist = explode("|", $listpages);
            // TODO - check against REQUEST_URI, see what would get broken
            $check_url = $url ? $url : ($_SERVER['REQUEST_URI'] ? SITEURLBASE.$_SERVER['REQUEST_URI'] : e_SELF.(e_QUERY ? "?".e_QUERY : ''));

            switch($listtype)
            {
                case '1': //show menu
                    $show_menu = false;

                    foreach($pagelist as $p)
                    {
                        if($p === 'FRONTPAGE' && $this->isFrontPage())
                        {
                            $show_menu = true;
                            break;
                        }

                        $p = $tp->replaceConstants($p, 'full');
                        if(substr($p, -1)==='!')
                        {
                            $p = substr($p, 0, -1);
                            if(substr($check_url, strlen($p)*-1) == $p)
                            {
                                $show_menu = true;
                                break 2;
                            }
                        }
                        elseif(strpos($check_url, $p) !== false)
                        {
                            $show_menu = true;
                            break 2;
                        }
                    }
                    break;
                case '2': //hide menu
                    $show_menu = true;
                    foreach($pagelist as $p)
                    {
                        if($p === 'FRONTPAGE' && $this->isFrontPage())
                        {
                            $show_menu = false;
                            break;
                        }


                        $p = $tp->replaceConstants($p, 'full');
                        if(substr($p, -1) === '!')
                        {
                            $p = substr($p, 0, -1);
                            if(substr($check_url, strlen($p)*-1) == $p)
                            {
                                $show_menu = false;
                                break 2;
                            }
                        }
                        elseif(strpos($check_url, $p) !== false)
                        {
                            $show_menu = false;
                            break 2;
                        }
                    }
                    break;
            } //end switch
        } //endif menu_pages

        $this->_visibility_cache[$iD] = $show_menu;
        return $show_menu;
    }

    /**
     * Render menu area
     *
     * @param string $parm
     * @return string
     */
    public function renderArea($parm = '')
    {
        global $sql, $ns, $tp, $sc_style;
        global $error_handler;
                
        $e107 = e107::getInstance();

        $tmp = explode(':', $parm);
        
        
        $buffer_output = (E107_DBG_INCLUDES) ? false : true; // Turn off when trouble-shooting includes. Default - return all output.
        

        if(isset($tmp[1])&&$tmp[1] === 'echo')
        {
            $buffer_output = false;
        }
        if(!array_key_exists($tmp[0], $this->eMenuActive))
        {
            return;
        }
        if($buffer_output)
        {
            ob_start();
        }
        
        e107::getRender()->eMenuArea = $tmp[0];
        foreach($this->eMenuActive[$tmp[0]] as $row)
        {
            $this->renderMenu($row['menu_path'], $row['menu_name'], $row['menu_parms']);
        }
        e107::getRender()->eMenuCount = 0;
        e107::getRender()->eMenuArea = null;
        if($buffer_output)
        {
            return ob_get_clean();
        }
    }

    /**
     * Render menu
     *
     * @param string $mpath menu path
     * @param string $mname menu name
     * @param string $parm menu parameters
     * @param boolean $return
     * return string if required
     */
    public function renderMenu($mpath, $mname='', $parm = '', $return = false)
    {
    //    global $sql; // required at the moment.


        global $sc_style;
                

        $sql        = e107::getDb();
        $ns         = e107::getRender();
        $tp         = e107::getParser();
        $e107cache  = e107::getCache(); // Often used by legacy menus.

        if($tmp = e107::unserialize($parm)) // support e_menu.php e107 serialized parm.
        {
            $parm = $tmp;
            unset($tmp);
        }

        $this->_current_parms = $parm;
        $this->_current_menu = $mname;


        if($return)
        {
            ob_start();
        }

        if(e_DEBUG === true)
        {
            echo "\n<!-- Menu Start: ".$mname." -->\n";
        }
        e107::getDebug()->logTime($mname);
        
        if(is_numeric($mpath) || ($mname === false)) // Custom Page/Menu 
        {
            $query = ($mname === false) ? "menu_name = '".$mpath."' " :  "page_id=". (int)$mpath ." "; // load by ID or load by menu-name (menu_name)
            
            $sql->select("page", "*", $query);
            $page = $sql->fetch();
            
            if(!empty($page['menu_class']) && !check_class($page['menu_class']))
            {
                echo "\n<!-- Menu not rendered due to userclass settings -->\n";
                return null;
            }
            
            $caption = (vartrue($page['menu_icon'])) ? $tp->toIcon($page['menu_icon']) : '';
            $caption .= $tp->toHTML($page['menu_title'], true, 'parse_sc, constants');
            
            if(!empty($page['menu_template'])) // New v2.x templates. see core/menu_template.php
            {
                $template = e107::getCoreTemplate('menu',$page['menu_template'],true,true);    // override and merge required. ie. when menu template is not in the theme, but only in the core. 
                $page_shortcodes = e107::getScBatch('page',null,'cpage');  
                $page_shortcodes->setVars($page)->wrapper('menu/'.$page['menu_template'] );
                  
                $head = $tp->parseTemplate($template['start'], true, $page_shortcodes);
                $foot = $tp->parseTemplate($template['end'], true, $page_shortcodes);
                  
            //     print_a($template['body']);           
                $text = $head.$tp->parseTemplate($template['body'], true, $page_shortcodes).$foot;
            //     echo "TEMPLATE= ($mpath)".$page['menu_template'];

                $ns->setUniqueId('cmenu-'.$page['menu_name']);
                $caption .= e107::getForm()->instantEditButton(e_ADMIN_ABS."cpage.php?mode=menu&action=edit&tab=2&id=". (int)$page['page_id'],'J');

                $ns->tablerender($caption, $text, 'cmenu-'.$page['menu_template']);
            }
            else 
            {                
                $text = $tp->toHTML($page['menu_text'], true, 'parse_sc, constants');
                $ns->setUniqueId('cmenu-'.$page['menu_name']);

                $ns->tablerender($caption, $text, 'cmenu');
            }
            
        }
        else
        {
            // not sure what would break this, but it's good idea to go away
            e107::loadLanFiles($mpath);
            
            //include once is not an option anymore
            //e107_include will break many old menus (evil globals), so we'll wait for a while...
            //e107_include(e_PLUGIN.$mpath."/".$mname.".php");
            //if(substr($mpath,-1)!='/')
            //{
            //    $mpath .= '/';
            //}

            $mpath = trim($mpath, '/').'/'; // faster...

            $id = e107::getForm()->name2id($mpath . $mname);
            $ns->setUniqueId($id);


            $pref = e107::getPref(); // possibly used by plugin menu.


            deftrue('e_DEBUG') ? include(e_PLUGIN.$mpath.$mname.'.php') : @include(e_PLUGIN.$mpath.$mname.'.php');
        }
        e107::getDebug()->logTime("(After ".$mname.")");

        if(e_DEBUG === true)
        {
            echo "\n<!-- Menu End: ".$mname." -->\n";
        }

        if($return)
        {
            return ob_get_clean();
        }

        unset($pref);
        return null;
    }
}