<?php
/**
* This file implements the Site Menu class.
*
* This file is part of the b2evolution/evocms project - {@link http://b2evolution.net/}.
* See also {@link https://github.com/b2evolution/b2evolution}.
*
* @license GNU GPL v2 - {@link http://b2evolution.net/about/gnu-gpl-license}
*
* @copyright (c)2003-2020 by Francois Planque - {@link http://fplanque.com/}.
*
* @license http://b2evolution.net/about/license.html GNU General Public License (GPL)
*
* @package evocore
*/
if( !defined('EVO_MAIN_INIT') ) die( 'Please, do not access this page directly.' );
load_class( '_core/model/dataobjects/_dataobject.class.php', 'DataObject' );
/**
* Menu Class
*
* @package evocore
*/
class SiteMenu extends DataObject
{
var $name;
var $locale;
var $translates_menu_ID;
/**
* @var array Site Menu Entries
*/
var $entries = NULL;
/**
* @var integer Translation menu count
*/
var $count_translation_menus = NULL;
/**
* @var array Localized menus
*/
var $localized_menus = NULL;
/**
* Constructor
*
* @param object table Database row
*/
function __construct( $db_row = NULL )
{
// Call parent constructor:
parent::__construct( 'T_menus__menu', 'menu_', 'menu_ID' );
if( $db_row != NULL )
{ // Get menu data from DB:
$this->ID = $db_row->menu_ID;
$this->translates_menu_ID = $db_row->menu_translates_menu_ID;
$this->name = $db_row->menu_name;
$this->locale = $db_row->menu_locale;
}
}
/**
* Get delete cascade settings
*
* @return array
*/
static function get_delete_cascades()
{
return array(
array( 'table' => 'T_menus__menu', 'fk' => 'menu_translates_menu_ID', 'msg' => T_('%d child menus') ),
array( 'table' => 'T_menus__entry', 'fk' => 'ment_menu_ID', 'msg' => T_('%d menu entries') ),
);
}
/**
* Load data from Request form fields.
*
* @return boolean true if loaded data seems valid.
*/
function load_from_Request()
{
// Name:
param( 'menu_name', 'string' );
param_check_not_empty( 'menu_name', T_('Please enter a name for the menu.') );
$this->set_from_Request( 'name' );
// Locale:
param( 'menu_locale', 'string' );
$this->set_from_Request( 'locale' );
// Translation of Menu:
$menu_translates_menu_ID = param( 'menu_translates_menu_ID', 'integer', NULL );
if( isset( $menu_translates_menu_ID ) && $this->has_translations() )
{ // Display error message if we want make the meta category from category with posts
global $Messages;
$Messages->add( sprintf( T_('This menu cannot become a translation of another because it has %d translations itself.'), $this->count_translation_menus ) );
}
$this->set_from_Request( 'translates_menu_ID' );
// Store auto menu entries in temp var, they will be inserted in SiteMenu::dbinsert():
$this->insert_menu_entries = param( 'menu_entries', 'array' );
return ! param_errors_detected();
}
/**
* Insert object into DB based on previously recorded changes.
*
* @return boolean true on success
*/
function dbinsert()
{
global $DB;
$DB->begin();
if( $r = parent::dbinsert() )
{ // If Menu has been inserted successfully:
if( ! empty( $this->insert_menu_entries ) )
{
$entry_sections = array();
$root_order = 10;
$prev_parent = NULL;
foreach( $this->insert_menu_entries as $menu_entry_key => $menu_entry_text )
{
$SiteMenuEntry = new SiteMenuEntry();
$SiteMenuEntry->set( 'menu_ID', $this->ID );
if( $menu_entry_key == '#contact#' )
{ // Special "Contact" entry:
$SiteMenuEntry->set( 'text', T_('Contact') );
$SiteMenuEntry->set( 'type', 'ownercontact' );
$SiteMenuEntry->set( 'order', $root_order );
$root_order += 10;
$SiteMenuEntry->dbinsert();
}
elseif( preg_match( '/^([a-z]+)_(\d+)(_(\d+))?$/', $menu_entry_key, $m ) )
{ // Section or Collection entry:
switch( $m[1] )
{
case 'sec':
// Section entry:
$SiteMenuEntry->set( 'text', $menu_entry_text );
$SiteMenuEntry->set( 'type', 'text' );
$SiteMenuEntry->set( 'order', $root_order );
$root_order += 10;
if( $SiteMenuEntry->dbinsert() )
{
$entry_sections[ $m[2] ] = $SiteMenuEntry->ID;
}
break;
case 'coll':
// Collection entry:
$SiteMenuEntry->set( 'text', $menu_entry_text );
$SiteMenuEntry->set( 'coll_ID', $m[2] );
$SiteMenuEntry->set( 'type', 'home' );
if( isset( $m[4], $entry_sections[ $m[4] ] ) )
{
if( $prev_parent != $entry_sections[ $m[4] ] )
{
$sub_order = 10;
}
$SiteMenuEntry->set( 'parent_ID', $entry_sections[ $m[4] ] );
$prev_parent = $entry_sections[ $m[4] ];
$SiteMenuEntry->set( 'order', $sub_order );
$sub_order += 10;
}
else
{
$SiteMenuEntry->set( 'order', $root_order );
$root_order += 10;
}
$SiteMenuEntry->dbinsert();
break;
}
}
}
}
}
if( $r )
{
$DB->commit();
}
else
{
$DB->rollback();
}
return $r;
}
/**
* Duplicate menu
*
* @return boolean True if duplication was successfull, false otherwise
*/
function duplicate()
{
global $DB;
$DB->begin();
$duplicated_menu_ID = $this->ID;
$this->ID = 0;
// Fields that should not be duplicated must be included in the array below:
$skipped_fields = array( 'ID' );
// Get all fields of the duplicated menu:
$source_fields_SQL = new SQL( 'Get all fields of the duplicated menu #'.$duplicated_menu_ID );
$source_fields_SQL->SELECT( '*' );
$source_fields_SQL->FROM( 'T_menus__menu' );
$source_fields_SQL->WHERE( 'menu_ID = '.$DB->quote( $duplicated_menu_ID ) );
$source_fields = $DB->get_row( $source_fields_SQL, ARRAY_A );
// Use field values of duplicated collection by default:
foreach( $source_fields as $source_field_name => $source_field_value )
{
// Cut prefix "menu_" of each field:
$source_field_name = substr( $source_field_name, 5 );
if( in_array( $source_field_name, $skipped_fields ) )
{ // Do not duplicate skipped fields
continue;
}
if( isset( $this->$source_field_name ) )
{ // Unset current value in order to assign new below, especially to update this in array $this->dbchanges:
unset( $this->$source_field_name );
}
$this->set( $source_field_name, $source_field_value );
}
// Call this firstly to find all possible errors before inserting:
// Also to set new values from submitted form:
if( ! $this->load_from_Request() )
{ // Error on handle new values from form:
$this->ID = $duplicated_menu_ID;
$DB->rollback();
return false;
}
// Try insert new collection in DB:
if( ! $this->dbinsert() )
{ // Error on insert collection in DB:
$this->ID = $duplicated_menu_ID;
$DB->rollback();
return false;
}
// Copy all menu entries linked to the menu:
$menu_entry_fields = array( 'ment_ID', 'ment_menu_ID', 'ment_parent_ID', 'ment_order', 'ment_user_pic_size', 'ment_text', 'ment_type',
'ment_coll_logo_size', 'ment_coll_ID', 'ment_item_ID', 'ment_url', 'ment_visibility', 'ment_access', 'ment_show_badge', 'ment_highlight', 'ment_class' );
$menu_entries_SQL = 'SELECT '.implode( ', ', $menu_entry_fields ).'
FROM T_menus__entry
WHERE ment_menu_ID = '.$DB->quote( $duplicated_menu_ID ).'
ORDER BY ment_parent_ID ASC, ment_order ASC';
$menu_entries = $DB->get_results( $menu_entries_SQL, ARRAY_A );
$entries = array();
foreach( $menu_entries as $menu_entry )
{
$loop_SiteMenuEntry = new SiteMenuEntry();
foreach( $menu_entry_fields as $entry_field )
{
if( $entry_field == 'ment_ID')
{
continue;
}
elseif( $entry_field == 'ment_menu_ID' )
{
$loop_SiteMenuEntry->set( 'menu_ID', $this->ID );
}
elseif( $entry_field == 'ment_parent_ID' && ! empty( $menu_entry['ment_parent_ID'] ) && isset( $entries[$menu_entry['ment_parent_ID']] ) )
{
$loop_SiteMenuEntry->set( 'parent_ID', $entries[$menu_entry['ment_parent_ID']]);
}
else
{
$property = substr( $entry_field, 5 );
$loop_SiteMenuEntry->set( $property, $menu_entry[$entry_field] );
}
}
$loop_SiteMenuEntry->dbinsert();
$entries[$menu_entry['ment_ID']] = $loop_SiteMenuEntry->ID;
}
// Duplication is successful, commit all above changes:
$DB->commit();
// Commit changes in cache:
$SiteMenuCache = & get_SiteMenuCache();
$SiteMenuCache->add( $this );
return true;
}
/**
* Get name of Menu Entry
*
* @return string Menu Entry
*/
function get_name()
{
return $this->get( 'name' );
}
/**
* Get menu entries
*
* @return array Objects of Site Menu Entries
*/
function get_entries()
{
if( $this->entries !== NULL )
{ // Use already loaded menu entries:
return $this->entries;
}
$SiteMenuEntryCache = & get_SiteMenuEntryCache();
$SiteMenuEntryCache->clear();
$entries_SQL = $SiteMenuEntryCache->get_SQL_object();
$entries_SQL->WHERE( 'ment_menu_ID = '.$this->ID );
$entries_SQL->WHERE_and( 'ment_parent_ID IS NULL' );
$entries_SQL->ORDER_BY( 'ment_order, ment_ID' );
$SiteMenuEntryCache->load_by_sql( $entries_SQL );
$this->entries = $SiteMenuEntryCache->cache;
return $this->entries;
}
/**
* Get max order of menu entries
*
* @param integer Parent Menu Entry ID
* @return integer
*/
function get_max_order( $parent_ID = NULL )
{
if( empty( $this->ID ) )
{
return 0;
}
global $DB;
$SQL = new SQL( 'Get max order of entries in Menu #'.$this->ID.' for parent Menu Entry #'.intval( $parent_ID ) );
$SQL->SELECT( 'MAX( ment_order )' );
$SQL->FROM( 'T_menus__entry' );
$SQL->WHERE( 'ment_menu_ID = '.$DB->quote( $this->ID ) );
$SQL->WHERE_and( 'ment_parent_ID '.( empty( $parent_ID ) ? 'IS NULL' : '= '.$DB->quote( $parent_ID ) ) );
return intval( $DB->get_var( $SQL ) );
}
/**
* Get localized menus
*
* @param string Locale
* @return array Array of SiteMenu objects
*/
function get_localized_menus( $locale )
{
global $DB;
if( ! isset( $this->localized_menus[$locale] ) )
{
$SiteMenuCache = & get_SiteMenuCache();
$SiteMenuCache->clear( true );
$where = 'menu_translates_menu_ID = '.$DB->quote( $this->ID ).' AND menu_locale = '.$DB->quote( $locale );
$this->localized_menus[$locale] = $SiteMenuCache->load_where( $where );
}
return $this->localized_menus[$locale];
}
/**
* Check if this menu has at least one post
*
* @return boolean
*/
function has_translations()
{
global $DB;
if( $this->ID == 0 )
{ // New menu has no translation menus:
return false;
}
if( !isset( $this->count_translation_menus ) )
{
$SQL = new SQL( 'Check if menu has translations menus' );
$SQL->SELECT( 'COUNT( menu_translates_menu_ID )' );
$SQL->FROM( 'T_menus__menu' );
$SQL->WHERE( 'menu_translates_menu_ID = '.$DB->quote( $this->ID ) );
$this->count_translation_menus = $DB->get_var( $SQL );
}
return ( $this->count_translation_menus > 0 );
}
}
?>