Source for file item.php
Documentation is available at item.php
* @package Joomla.Administrator
* @copyright Copyright (C) 2005 - 2013 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE.txt
require_once JPATH_COMPONENT .
'/helpers/menus.php';
* Menu Item Model for Menus.
* @package Joomla.Administrator
* @var string The prefix to use with controller messages.
* @var string The help screen key for the menu item.
protected $helpKey =
'JHELP_MENUS_MENU_ITEM_MANAGER_EDIT';
* @var string The help screen base URL for the menu item.
* @var boolean True to use local lookup for the help screen.
* Method to test whether a record can be deleted.
* @param object A record object.
* @return boolean True if allowed to delete the record. Defaults to the permission set in the component.
if ($record->published != -
2)
return $user->authorise('core.delete', 'com_menus.item.' . (int)
$record->id);
* Method to test whether a record can have its state edited.
* @param object A record object.
* @return boolean True if allowed to change the state of the record. Defaults to the permission set in the component.
return $user->authorise('core.edit.state', 'com_menus.item.' . (int)
$record->id);
// Default to component settings if menu item not known.
return parent::canEditState($record);
* Method to perform batch operations on an item or a set of items.
* @param array $commands An array of commands to perform.
* @param array $pks An array of item ids.
* @param array $contexts An array of item contexts.
* @return boolean Returns true on success, false on failure.
public function batch($commands, $pks, $contexts)
// Remove any values of zero.
if (!empty($commands['menu_id']))
$result =
$this->batchCopy($commands['menu_id'], $pks, $contexts);
elseif ($cmd ==
'm' &&
!$this->batchMove($commands['menu_id'], $pks, $contexts))
if (!empty($commands['assetgroup_id']))
if (!$this->batchAccess($commands['assetgroup_id'], $pks, $contexts))
if (!empty($commands['language_id']))
if (!$this->batchLanguage($commands['language_id'], $pks, $contexts))
$this->setError(JText::_('JLIB_APPLICATION_ERROR_INSUFFICIENT_BATCH_INFORMATION'));
* Batch copy menu items to a new menu or parent.
* @param integer $value The new menu or sub-item.
* @param array $pks An array of row IDs.
* @param array $contexts An array of item contexts.
* @return mixed An array of new IDs on success, boolean false on failure.
protected function batchCopy($value, $pks, $contexts)
// $value comes as {menutype}.{parent_id}
$query =
$db->getQuery(true);
// Check that the parent exists
if (!$table->load($parentId))
if ($error =
$table->getError())
// If the parent is 0, set it to the ID of the root item in the tree
if (!$parentId =
$table->getRootId())
// Check that user has create permission for menus
if (!$user->authorise('core.create', 'com_menus'))
// We need to log the parent ID
// Calculate the emergency stop count as a precaution against a runaway loop bug
$query->select('COUNT(id)')
->from($db->quoteName('#__menu'));
$count =
$db->loadResult();
catch
(RuntimeException $e)
// Parent exists so we let's proceed
while (!empty($pks) &&
$count >
0)
// Pop the first id off the stack
// Check that the row actually exists
if ($error =
$table->getError())
// Copy is a bit tricky, because we also need to copy the children
->from($db->quoteName('#__menu'))
->where('lft > ' . (int)
$table->lft)
->where('rgt < ' . (int)
$table->rgt);
$childIds =
$db->loadColumn();
// Add child ID's to the array only if they aren't already there.
foreach ($childIds as $childId)
// Make a copy of the old ID and Parent ID
$oldParentId =
$table->parent_id;
// Reset the id because we are making a copy.
// If we a copying children, the Old ID will turn up in the parents list
// otherwise it's a new top level item
$table->parent_id = isset
($parents[$oldParentId]) ?
$parents[$oldParentId] :
$parentId;
$table->menutype =
$menuType;
// Set the new location in the tree for the node.
$table->setLocation($table->parent_id, 'last-child');
// TODO: Deal with ordering?
// Alter the title & alias
list
($title, $alias) =
$this->generateNewTitle($table->parent_id, $table->alias, $table->title);
$newId =
$table->get('id');
// Add the new ID to the array
// Now we log the old 'parent' to the new 'parent'
$parents[$oldId] =
$table->id;
// Rebuild the hierarchy.
// Rebuild the tree path.
if (!$table->rebuildPath($table->id))
* Batch move menu items to a new menu or parent.
* @param integer $value The new menu or sub-item.
* @param array $pks An array of row IDs.
* @param array $contexts An array of item contexts.
* @return boolean True on success.
protected function batchMove($value, $pks, $contexts)
// $value comes as {menutype}.{parent_id}
$query =
$db->getQuery(true);
// Check that the parent exists.
if (!$table->load($parentId))
if ($error =
$table->getError())
// Check that user has create and edit permission for menus
if (!$user->authorise('core.create', 'com_menus'))
if (!$user->authorise('core.edit', 'com_menus'))
// We are going to store all the children and just moved the menutype
// Parent exists so we let's proceed
// Check that the row actually exists
if ($error =
$table->getError())
// Set the new location in the tree for the node.
$table->setLocation($parentId, 'last-child');
$table->parent_id =
$parentId;
// Check if we are moving to a different menu
if ($menuType !=
$table->menutype)
// Add the child node ids to the children array.
->select($db->quoteName('id'))
->from($db->quoteName('#__menu'))
->where($db->quoteName('lft') .
' BETWEEN ' . (int)
$table->lft .
' AND ' . (int)
$table->rgt);
$children =
array_merge($children, (array)
$db->loadColumn());
// Rebuild the tree path.
if (!$table->rebuildPath())
// Process the child rows
// Remove any duplicates and sanitize ids.
// Update the menutype field in all nodes where necessary.
->update($db->quoteName('#__menu'))
->set($db->quoteName('menutype') .
' = ' .
$db->quote($menuType))
->where($db->quoteName('id') .
' IN (' .
implode(',', $children) .
')');
catch
(RuntimeException $e)
* Method to check if you can save a record.
* @param array $data An array of input data.
* @param string $key The name of the key for the primary key.
protected function canSave($data =
array(), $key =
'id')
* Method to get the row form.
* @param array $data Data for the form.
* @param boolean $loadData True if the form is to load its own data (default case), false if not.
* @return mixed A JForm object on success, false on failure
public function getForm($data =
array(), $loadData =
true)
// The folder and element vars are passed when saving the form.
$this->setState('item.link', $item->link);
// The type should already be set.
$form =
$this->loadForm('com_menus.item', 'item', array('control' =>
'jform', 'load_data' =>
$loadData), true);
// Modify the form based on access controls.
// Disable fields for display.
$form->setFieldAttribute('menuordering', 'disabled', 'true');
$form->setFieldAttribute('published', 'disabled', 'true');
// Disable fields while saving.
// The controller has already verified this is an article you can edit.
$form->setFieldAttribute('menuordering', 'filter', 'unset');
$form->setFieldAttribute('published', 'filter', 'unset');
* Method to get the data that should be injected in the form.
* @return mixed The data for the form.
// Check the session for previously entered form data.
* Get the necessary data to load an item help screen.
* @return object An object with key, url, and local properties for loading the item help screen.
* Method to get a menu item.
* @param integer $pk An optional id of the object to get, otherwise the id from the model state is used.
* @return mixed Menu item data object on success, false on failure.
public function getItem($pk =
null)
$pk =
(!empty($pk)) ?
$pk : (int)
$this->getState('item.id');
// Get a level row instance.
// Attempt to load the row.
// Check for a table object error.
if ($error =
$table->getError())
// Prime required properties.
if ($type =
$this->getState('item.type'))
$table->parent_id =
$this->getState('item.parent_id');
$table->menutype =
$this->getState('item.menutype');
// If the link has been set in the state, possibly changing link type.
if ($link =
$this->getState('item.link'))
// Check if we are changing away from the actual link type.
$table->component_id =
0;
$table->component_id =
0;
$table->component_id =
0;
$table->type =
'component';
// Ensure the integrity of the component_id field is maintained, particularly when changing the menu item type.
if (isset
($args['option']))
// Load the language file for the component.
||
$lang->load($args['option'], JPATH_ADMINISTRATOR .
'/components/' .
$args['option'], null, false, true);
// Determine the component id.
if (isset
($component->id))
$table->component_id =
$component->id;
// We have a valid type, inject it into the state for forms to use.
$this->setState('item.type', $table->type);
// Convert to the JObject before adding the params.
$properties =
$table->getProperties(1);
// Convert the params field to an array.
$registry->loadString($table->params);
$result->params =
$registry->toArray();
// Merge the request arguments in to the params for a component.
if ($table->type ==
'component')
// Note that all request arguments become reserved parameter names.
$result->request =
$args;
if ($table->type ==
'alias')
// Note that all request arguments become reserved parameter names.
if ($table->type ==
'url')
// Note that all request arguments become reserved parameter names.
// Load associated menu items
$result->associations =
array();
$result->menuordering =
$pk;
* Get the list of modules not in trash.
* @return mixed An array of module records (id, title, position), or false on error.
$query =
$db->getQuery(true);
// Join on the module-to-menu mapping table.
// We are only interested if the module is displayed on ALL or THIS menu item (or the inverse ID number).
//sqlsrv changes for modulelink to menu manager
$query->select('a.id, a.title, a.position, a.published, map.menuid')
->from('#__modules AS a')
->join('LEFT', sprintf('#__modules_menu AS map ON map.moduleid = a.id AND map.menuid IN (0, %1$d, -%1$d)', $this->getState('item.id')))
->select('(SELECT COUNT(*) FROM #__modules_menu WHERE moduleid = a.id AND menuid < 0) AS ' .
$db->quoteName('except'));
// Join on the asset groups table.
$query->select('ag.title AS access_title')
->join('LEFT', '#__viewlevels AS ag ON ag.id = a.access')
->where('a.published >= 0')
->where('a.client_id = 0')
->order('a.position, a.ordering');
$result =
$db->loadObjectList();
catch
(RuntimeException $e)
* A protected method to get the where clause for the reorder
* This ensures that the row will be moved relative to a row with the same menutype
* @param JTableMenu $table instance
* @return array An array of conditions to add to add to ordering queries.
return 'menutype = ' .
$this->_db->quote($table->menutype);
* Returns a Table object, always creating it
* @param type $type The table type to instantiate
* @param string $prefix A prefix for the table class name. Optional.
* @param array $config Configuration array for model. Optional.
* @return JTable A database object
public function getTable($type =
'Menu', $prefix =
'MenusTable', $config =
array())
* Auto-populate the model state.
* Note. Calling getState in this method will result in recursion.
$pk =
$app->input->getInt('id');
if (!($parentId =
$app->getUserState('com_menus.edit.item.parent_id')))
$parentId =
$app->input->getInt('parent_id');
$this->setState('item.parent_id', $parentId);
$menuType =
$app->getUserState('com_menus.edit.item.menutype');
if ($app->input->getString('menutype', false))
$menuType =
$app->input->getString('menutype', 'mainmenu');
$this->setState('item.menutype', $menuType);
if (!($type =
$app->getUserState('com_menus.edit.item.type')))
$type =
$app->input->get('type');
// Note a new menu item will have no field type.
// The field is required so the user has to change it.
if ($link =
$app->getUserState('com_menus.edit.item.link'))
* @param object $form A form object.
* @param mixed $data The data expected for the form.
* @throws Exception if there is an error in the form event.
protected function preprocessForm(JForm $form, $data, $group =
'content')
// Initialise form with component view params if available.
if ($type ==
'component')
// Parse the link arguments.
// Confirm that the option is defined.
if (isset
($args['option']))
// The option determines the base path to work with.
$option =
$args['option'];
// Confirm a view is defined.
if (isset
($args['view']))
// Determine the layout to search for.
if (isset
($args['layout']))
$layout =
$args['layout'];
// Check for the layout XML file. Use standard xml file if it exists.
$base .
'/views/' .
$view .
'/tmpl',
$base .
'/view/' .
$view .
'/tmpl'
$path =
JPath::find($tplFolders, $layout .
'.xml');
// If custom layout, get the xml file from the template folder
// template folder is first part of file name -- template:folder
if (!$formFile &&
(strpos($layout, ':') >
0))
$templatePath =
JPATH::clean(JPATH_SITE .
'/templates/' .
$temp[0] .
'/html/' .
$option .
'/' .
$view .
'/' .
$temp[1] .
'.xml');
$formFile =
$templatePath;
// Now check for a view manifest file
$metadataFolders =
array(
$base .
'/view/' .
$view,
$base .
'/views/' .
$view
$metaPath =
JPath::find($metadataFolders, 'metadata.xml');
// Now check for a component manifest file
// If an XML file was found in the component, load it first.
// We need to qualify the full path to avoid collisions with component file names.
if ($form->loadFile($formFile, true, '/metadata') ==
false)
throw
new Exception(JText::_('JERROR_LOADFILE_FAILED'));
// Attempt to load the xml file.
throw
new Exception(JText::_('JERROR_LOADFILE_FAILED'));
// Get the help data from the XML file if present.
$help =
$xml->xpath('/metadata/layout/help');
// We don't have a component. Load the form XML to get the help path
$xmlFile =
JPath::find(JPATH_ROOT .
'/administrator/components/com_menus/models/forms', 'item_' .
$type .
'.xml');
// Attempt to load the xml file.
throw
new Exception(JText::_('JERROR_LOADFILE_FAILED'));
// Get the help data from the XML file if present.
$help =
$xml->xpath('/form/help');
$helpKey =
trim((string)
$help[0]['key']);
$helpURL =
trim((string)
$help[0]['url']);
$helpLoc =
trim((string)
$help[0]['local']);
$this->helpLocal =
(($helpLoc ==
'true') ||
($helpLoc ==
'1') ||
($helpLoc ==
'local')) ?
true :
false;
// Now load the component params.
// TODO: Work out why 'fixing' this breaks JForm
// Add the component params last of all to the existing form.
if (!$form->load($path, true, '/config'))
throw
new Exception(JText::_('JERROR_LOADFILE_FAILED'));
// Load the specific type file
if (!$form->loadFile('item_' .
$type, false, false))
throw
new Exception(JText::_('JERROR_LOADFILE_FAILED'));
// Association menu items
$addform =
new SimpleXMLElement('<form />');
$fields =
$addform->addChild('fields');
$fields->addAttribute('name', 'associations');
$fieldset =
$fields->addChild('fieldset');
$fieldset->addAttribute('name', 'item_associations');
$fieldset->addAttribute('description', 'COM_MENUS_ITEM_ASSOCIATIONS_FIELDSET_DESC');
foreach ($languages as $tag =>
$language)
if ($tag !=
$data['language'])
$field =
$fieldset->addChild('field');
$field->addAttribute('name', $tag);
$field->addAttribute('type', 'menuitem');
$field->addAttribute('language', $tag);
$field->addAttribute('label', $language->title);
$field->addAttribute('translate_label', 'false');
$option =
$field->addChild('option', 'COM_MENUS_ITEM_FIELD_ASSOCIATION_NO_VALUE');
$option->addAttribute('value', '');
$form->load($addform, false);
// Trigger the default form events.
parent::preprocessForm($form, $data, $group);
* Method rebuild the entire nested set tree.
* @return boolean False on failure or error, true otherwise.
// Initialiase variables.
$query =
$db->getQuery(true);
$query->select('id, params')
->where('params NOT LIKE ' .
$db->quote('{%'))
->where('params <> ' .
$db->quote(''));
$items =
$db->loadObjectList();
catch
(RuntimeException $e)
foreach ($items as &$item)
$registry->loadString($item->params);
$params = (string)
$registry;
$query->update('#__menu')
->set('params = ' .
$db->quote($params))
->where('id = ' .
$item->id);
$db->setQuery($query)->execute();
catch
(RuntimeException $e)
* Method to save the form data.
* @param array $data The form data.
* @return boolean True on success.
public function save($data)
$pk =
(!empty($data['id'])) ?
$data['id'] : (int)
$this->getState('item.id');
// Load the row if saving an existing item.
if ($table->parent_id ==
$data['parent_id'])
// If first is chosen make the item the first child of the selected parent.
if ($data['menuordering'] == -
1)
$table->setLocation($data['parent_id'], 'first-child');
// If last is chosen make it the last child of the selected parent.
elseif ($data['menuordering'] == -
2)
$table->setLocation($data['parent_id'], 'last-child');
// Don't try to put an item after itself. All other ones put after the selected item.
// $data['id'] is empty means it's a save as copy
elseif ($data['menuordering'] &&
$table->id !=
$data['menuordering'] ||
empty($data['id']))
$table->setLocation($data['menuordering'], 'after');
// Just leave it where it is if no change is made.
elseif ($data['menuordering'] &&
$table->id ==
$data['menuordering'])
unset
($data['menuordering']);
// Set the new parent id if parent id not matched and put in last position
$table->setLocation($data['parent_id'], 'last-child');
// We have a new item, so it is not a change.
$table->setLocation($data['parent_id'], 'last-child');
if (!$table->bind($data))
// Alter the title & alias for save as copy. Also, unset the home record.
if (!$isNew &&
$data['id'] ==
0)
list
($title, $alias) =
$this->generateNewTitle($table->parent_id, $table->alias, $table->title);
// Rebuild the tree path.
if (!$table->rebuildPath($table->id))
$this->setState('item.menutype', $table->menutype);
// Load associated menu items
// Adding self to the association
$associations =
$data['associations'];
foreach ($associations as $tag =>
$id)
unset
($associations[$tag]);
// Detecting all item menus
$all_language =
$table->language ==
'*';
if ($all_language &&
!empty($associations))
$associations[$table->language] =
$table->id;
// Deleting old association for these items
$query =
$db->getQuery(true)
->delete('#__associations')
->where('context=' .
$db->quote('com_menus.item'))
->where('id IN (' .
implode(',', $associations) .
')');
catch
(RuntimeException $e)
if (!$all_language &&
count($associations) >
1)
// Adding new association for these items
->insert('#__associations');
foreach ($associations as $id)
$query->values($id .
',' .
$db->quote('com_menus.item') .
',' .
$db->quote($key));
catch
(RuntimeException $e)
if (isset
($data['link']))
$option =
$juri->getVar('option');
parent::cleanCache($option);
* Method to save the reordered nested set tree.
* First we save the new order values in the lft values of the changed ids.
* Then we invoke the table rebuild to implement the new ordering.
* @param array $idArray id's of rows to be reordered
* @param array $lft_array lft values of rows to be reordered
* @return boolean false on failuer or error, true otherwise
public function saveorder($idArray =
null, $lft_array =
null)
// Get an instance of the table object.
if (!$table->saveorder($idArray, $lft_array))
* Method to change the home state of one or more items.
* @param array $pks A list of the primary keys to change.
* @param integer $value The value of the home state.
* @return boolean True on success.
public function setHome(&$pks, $value =
1)
// Remember that we can set a home page for different languages,
// so we need to loop through the primary key array.
foreach ($pks as $i =>
$pk)
$languages[$table->language] =
true;
if ($table->home ==
$value)
if ($table->language ==
'*')
// Prune items that you can't change.
elseif (!$table->check())
// Prune the items that failed pre-save checks.
elseif (!$table->store())
// Prune the items that could not be stored.
* Method to change the published state of one or more records.
* @param array &$pks A list of the primary keys to change.
* @param integer $value The value of the published state.
* @return boolean True on success.
public function publish(&$pks, $value =
1)
// Default menu item existence checks.
foreach ($pks as $i =>
$pk)
if ($table->load($pk) &&
$table->home &&
$table->language ==
'*')
// Prune items that you can't change.
// Ensure that previous checks doesn't empty the array
return parent::publish($pks, $value);
* Method to change the title & alias.
* @param integer $parent_id The id of the parent.
* @param string $alias The alias.
* @param string $title The title.
* @return array Contains the modified title and alias.
// Alter the title & alias
while ($table->load(array('alias' =>
$alias, 'parent_id' =>
$parent_id)))
if ($title ==
$table->title)
return array($title, $alias);
* Custom clean cache method
protected function cleanCache($group =
null, $client_id =
0)
parent::cleanCache('com_modules');
parent::cleanCache('mod_menu');
Documentation generated on Tue, 19 Nov 2013 15:05:57 +0100 by phpDocumentor 1.4.3