menu_link.module in Menu Link (Field) 7
Same filename and directory in other branches
File
menu_link.moduleView source
<?php
/**
* @file
* Defines a menu link field type.
*/
require_once dirname(__FILE__) . '/menu_link.field.inc';
/**
* Name of the fixed Menu link field.
*
* This module provides one Menu link field by default. This field cannot be
* deleted and its storage backend is restricted to field_sql_storage. This way
* other modules can use its database table {field_data_menu_link}, to include
* the {menu_links} table in entity queries.
*/
define('MENU_LINK_DEFAULT_FIELD', 'menu_link');
/**
* Implements hook_help().
*/
function menu_link_help($path, $arg) {
switch ($path) {
case 'admin/help#menu_link':
$output = '';
$output .= '<h3>' . t('About') . '</h3>';
$output .= '<p>' . t("The Menu link module defines a menu link field type for the Field module. A menu link field may be used to place links into a menu that link to it's entity. See the <a href='@field-help'>Field module help page</a> for more information about fields.", array(
'@field-help' => url('admin/help/field'),
'@filter-help' => url('admin/help/filter'),
)) . '</p>';
return $output;
}
}
/**
* Load multiple menu links, access checked and link translated for rendering.
*
* This function should never be called from within node_load() or any other
* function used as a menu object load function since an infinite recursion may
* occur.
*
* @param $mlids array
* An array of menu link IDs.
* @param $conditions array
* An associative array of conditions on the {menu_links}
* table, where the keys are the database fields and the values are the
* values those fields must have.
*
* @return
* An array of menu links indexed by mlid.
*
* @see menu_link_load()
*
* @todo Remove this function when http://drupal.org/node/1034732 lands.
*/
function menu_link_load_multiple(array $mlids, array $conditions = array()) {
$query = db_select('menu_links', 'ml', array(
'fetch' => PDO::FETCH_ASSOC,
));
$query
->leftJoin('menu_router', 'm', 'm.path = ml.router_path');
$query
->fields('ml');
// Weight should be taken from {menu_links}, not {menu_router}.
$query
->addField('ml', 'weight', 'link_weight');
$query
->fields('m');
if (!empty($mlids)) {
$query
->condition('ml.mlid', $mlids, 'IN');
}
if (!empty($conditions)) {
foreach ($conditions as $field => $value) {
$query
->condition('ml.' . $field, $value);
}
}
$items = array();
foreach ($query
->execute() as $item) {
$item['weight'] = $item['link_weight'];
$items[$item['mlid']] = $item;
_menu_link_translate($items[$item['mlid']]);
}
return $items;
}
/**
* Delete multiple menu links.
*
* @param $mlids array
* An array of menu link IDs.
* @param $force boolean
* Forces deletion. Internal use only, setting to TRUE is discouraged.
*
* @see menu_link_delete()
*
* @todo Remove this function when http://drupal.org/node/1034732 lands.
*/
function menu_link_delete_multiple(array $mlids, $force = FALSE) {
if (!empty($mlids)) {
$query = db_select('menu_links')
->fields('menu_links')
->condition('mlid', $mlids, 'IN');
if (!$force) {
// Exclude links belonging to system module except if they are marked
// updated (generated during update from Drupal 5).
$query
->condition(db_or()
->condition('module', 'system', '<>')
->condition('updated', 0, '<>'));
}
$links_to_delete = $query
->execute()
->fetchAllAssoc('mlid', PDO::FETCH_ASSOC);
if (!empty($links_to_delete)) {
$links_with_children = array();
$parent_mlids = array();
$affected_menus = array();
foreach ($links_to_delete as $item) {
if ($item['has_children']) {
$links_with_children[$item['mlid']] = $item['mlid'];
}
$parent_mlids[$item['plid']] = $item['plid'];
$affected_menus[$item['menu_name']] = $item['menu_name'];
}
$parent_mlids = array_diff_key($parent_mlids, array(
0 => 0,
) + array_keys($links_to_delete));
// Re-parent any children to it's closest parent that is not deleted.
if (!empty($links_with_children)) {
$children = menu_link_load_multiple(array(), array(
'plid' => $links_with_children,
));
foreach ($children as $item) {
while (isset($links_to_delete[$item['plid']])) {
$item['plid'] = $links_to_delete[$item['plid']]['plid'];
}
menu_link_save($item);
}
}
db_delete('menu_links')
->condition('mlid', array_keys($links_to_delete), 'IN')
->execute();
foreach ($links_to_delete as $item) {
// Notify modules we have deleted the item.
module_invoke_all('menu_link_delete', $item);
// Update the has_children status of the parent.
_menu_update_parental_status($item);
}
// Update the has_children status of parents of deleted links.
// @todo fix query und use this instead of _menu_update_parental_status($item);
/*if (!empty($parent_mlids)) {
$exists_query = db_select('menu_links', 'child')
->fields('child', array('mlid'))
->condition('child.hidden', 0)
->where('child.plid = parent.mlid')
->where('child.menu_name = parent.menu_name')
->range(0, 1);
db_update('menu_links', 'parent')
->fields(array('has_children' => 0))
->condition('has_children', 1)
->condition('mlid', $parent_mlids, 'IN')
->notExists($exists_query)
->execute();
}*/
// Clear caches.
foreach ($affected_menus as $menu_name) {
menu_cache_clear($menu_name);
}
_menu_clear_page_cache();
}
}
}
/**
* Implements hook_menu_delete().
*/
function menu_link_menu_delete($menu) {
// Menu should not be removed from settings of menu_link field instances; menus
// have a machine name so they can be recreated. Non existant menus won't be
// available in the field widgets.
}
/**
* Implements hook_menu_link_alter().
*
* @see http://drupal.org/node/1087888
* Add $prior_link to hook_menu_link_update().
*/
function menu_link_menu_link_alter($item = NULL) {
static $existing_item;
if (isset($item)) {
$existing_item = FALSE;
if (isset($item['mlid'])) {
if ($existing_item = db_query("SELECT * FROM {menu_links} WHERE mlid = :mlid", array(
':mlid' => $item['mlid'],
))
->fetchAssoc()) {
$existing_item['options'] = unserialize($existing_item['options']);
}
}
if ($existing_item['module'] == 'menu_link') {
}
}
return $existing_item;
}
/**
* Implements hook_menu_link_update().
*
* Synchronize menu_link fields with the updated menu link.
*/
function menu_link_menu_link_update($item) {
$prior_item = menu_link_menu_link_alter();
if ($item['module'] == 'menu_link' && empty($item['menu_link_field_save'])) {
static $entity_paths;
if (empty($entity_paths)) {
$entity_get_info = entity_get_info();
foreach ($entity_get_info as $entity_type => $entity_info) {
if (isset($entity_info['path'])) {
$entity_path = str_replace('%' . $entity_type, '%', $entity_info['path']);
$entity_paths[$entity_path] = $entity_type;
}
}
}
if (isset($entity_paths[$item['router_path']])) {
// Get entity type
$entity_type = $entity_paths[$item['router_path']];
// Get path to entity without wildcard
$router_path = str_replace('%', '', $item['router_path']);
// Get Entity ID
$entity_id = str_replace($router_path, '', $item['link_path']);
// Load entity
$entity = array_shift(entity_load($entity_type, array(
$entity_id,
)));
// Get bundle
list(, , $bundle) = entity_extract_ids($entity_type, $entity);
$save_entity = FALSE;
foreach (field_info_instances($entity_type, $bundle) as $instance) {
$field_name = $instance['field_name'];
$field = field_info_field($field_name);
if ($field['module'] == 'menu_link') {
// Check if field exist on this entity
if (isset($entity->{$field_name}) && !empty($entity->{$field_name})) {
// Check if content is the same as current path for each language
foreach ($entity->{$field_name} as $lang => $items) {
// for each contents of fields
foreach ($items as $i => $field_item) {
// Check if it's current item
if ($field_item['mlid'] == $item['mlid']) {
// So give it updated options
foreach ($entity->{$field_name}[$lang][$i] as $option_name => $option_value) {
$entity->{$field_name}[$lang][$i][$option_name] = $item[$option_name];
}
$save_entity = TRUE;
break;
// We found item in field content so break to next field
}
}
}
}
}
}
// Entity has been edited so save it
if ($save_entity) {
entity_save($entity_type, $entity);
}
}
}
}
/**
* Implements hook_menu_link_delete().
*
* Remove link from all menu_link fields. Note that this module disables the
* possibility to delete menu links through the Admin > Structure > Menus
* interface that are used in a menu_link field (by storing menu links under its
* own module instead of system). So this hook may not be neccessary at all.
*/
function menu_link_menu_link_delete($item) {
if ($item['module'] == 'menu_link' && empty($item['menu_link_field_save'])) {
// TODO
}
}
/**
* Implements hook_field_update_forbid().
*/
function menu_link_field_update_forbid($field, $prior_field, $has_data) {
if ($field['field_name'] == MENU_LINK_DEFAULT_FIELD) {
if (!empty($field['settings']['link_path_field'])) {
throw new FieldUpdateForbiddenException(t('The link path cannot not be exposed for the ":menu_link_field" field.', array(
':menu_link_field' => MENU_LINK_DEFAULT_FIELD,
)));
}
}
}
/**
* Implementation of hook_views_api().
*/
function menu_link_views_api() {
return array(
'api' => 3,
'path' => drupal_get_path('module', 'menu_link'),
);
}
/**
* Implements hook_field_attach_prepare_translation_alter().
*
* For newly created translations, enforces empty menu link fields.
*
* Otherwise menu_link would set the language of the menu item
* back and forth upon editing one and the other translated entity.
*/
function menu_link_field_attach_prepare_translation_alter($entity, $context) {
list(, , $bundle) = entity_extract_ids($context['entity_type'], $entity);
foreach (field_info_instances($context['entity_type'], $bundle) as $instance) {
$field_name = $instance['field_name'];
$field = field_info_field($field_name);
if ($field['module'] == 'menu_link') {
if (isset($entity->{$field_name}) && !empty($entity->{$field_name})) {
$entity->{$field_name} = array();
}
}
}
}
Functions
Name | Description |
---|---|
menu_link_delete_multiple | Delete multiple menu links. |
menu_link_field_attach_prepare_translation_alter | Implements hook_field_attach_prepare_translation_alter(). |
menu_link_field_update_forbid | Implements hook_field_update_forbid(). |
menu_link_help | Implements hook_help(). |
menu_link_load_multiple | Load multiple menu links, access checked and link translated for rendering. |
menu_link_menu_delete | Implements hook_menu_delete(). |
menu_link_menu_link_alter | Implements hook_menu_link_alter(). |
menu_link_menu_link_delete | Implements hook_menu_link_delete(). |
menu_link_menu_link_update | Implements hook_menu_link_update(). |
menu_link_views_api | Implementation of hook_views_api(). |
Constants
Name | Description |
---|---|
MENU_LINK_DEFAULT_FIELD | Name of the fixed Menu link field. |