You are here

menu_link_node_menu.module in Menu Link (Field) 7

Use a menu link field for core.

File

menu_link_node_menu/menu_link_node_menu.module
View source
<?php

/**
 * @file
 * Use a menu link field for core.
 */

/**
 * Implements hook_menu().
 */
function menu_link_node_menu_menu() {
  $items['admin/reports/status/rebuild/node-menu-link'] = array(
    'title' => 'Populate the default menu link field of nodes with existing menu links.',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'menu_link_node_menu_admin_populate_confirm',
    ),
    'access arguments' => array(
      'access administration pages',
    ),
    'type' => MENU_CALLBACK,
    'file' => 'menu_link_node_menu.admin.inc',
  );
  return $items;
}

/**
 * Implements hook_module_implements_alter().
 *
 * Prevent Menu module from adding a menu link form to node forms.
 */
function menu_link_node_menu_module_implements_alter(&$implementations, $hook) {

  // Disable certain hook implementations of the Menu module.
  if (in_array($hook, array(
    'node_insert',
    'node_update',
    'node_delete',
    'node_prepare',
    'form_node_form_alter',
    'node_submit',
  ))) {
    unset($implementations['menu']);
  }
}
class MenuLinkNodeMenu implements ArrayAccess {
  private $node = array();
  private $menu;
  public function __construct($node) {
    $this->node = $node;
  }
  private function loadMenu() {
    $item = array();

    // Prepare the node for the edit form so that $node->menu always exists.
    $menu_name = strtok(variable_get('menu_parent_' . $this->node->type, 'main-menu:0'), ':');
    if (!empty($this->node->{MENU_LINK_DEFAULT_FIELD}[LANGUAGE_NONE][0]['mlid'])) {
      $mlid = $this->node->{MENU_LINK_DEFAULT_FIELD}[LANGUAGE_NONE][0]['mlid'];
      $item = menu_link_load($mlid);
    }

    // Set default values.
    $this->menu = $item + array(
      'link_title' => '',
      'mlid' => 0,
      'plid' => 0,
      'menu_name' => $menu_name,
      'weight' => 0,
      'options' => array(),
      'module' => 'menu',
      'expanded' => 0,
      'hidden' => 0,
      'has_children' => 0,
      'customized' => 0,
    );
  }
  public function offsetSet($offset, $value) {
    if (empty($this->menu)) {
      $this
        ->loadMenu();
    }
    if ($offset === NULL) {
      $this->menu[] = $value;
    }
    else {
      $this->menu[$offset] = $value;
    }
  }
  public function offsetExists($offset) {
    if (empty($this->menu)) {
      $this
        ->loadMenu();
    }
    return isset($this->menu[$offset]);
  }
  public function offsetUnset($offset) {
    if (empty($this->menu)) {
      $this
        ->loadMenu();
    }
    unset($this->menu[$offset]);
  }
  public function offsetGet($offset) {
    if (empty($this->menu)) {
      $this
        ->loadMenu();
    }
    return $this->menu[$offset];
  }

}

/**
 * Implements hook_node_load().
 *
 * @see menu_node_prepare()
 *   Loads node menu only if not set yet.
 */
function menu_link_node_menu_node_load($nodes, $types) {
  foreach ($nodes as $node) {
    $node->menu = new MenuLinkNodeMenu($node);
  }
}

/**
 * Implements hook_node_prepare().
 */
function menu_link_node_menu_node_prepare($node) {
  $node->menu = new MenuLinkNodeMenu($node);
}

/**
 * Implements hook_field_create_instance().
 */
function menu_link_node_menu_field_create_instance($instance) {
  _menu_link_node_menu_update_type($instance);
}

/**
 * Implements hook_field_update_instance().
 */
function menu_link_node_menu_field_update_instance($instance, $prior_instance) {
  _menu_link_node_menu_update_type($instance);
}

/**
 * Implements hook_node_type_insert().
 */
function menu_link_node_menu_node_type_insert($info) {
  _menu_link_node_menu_update_field($info);
}

/**
 * Implements hook_node_type_update().
 */
function menu_link_node_menu_node_type_update($info) {
  _menu_link_node_menu_update_field($info);
}

/**
 * Builds an instance of the default menu link field based on a content type's menu settings.
 *
 * @param $info object
 *   The content type object.
 *
 * @return array
 *   An associative array representing an instance structure.
 */
function _menu_link_node_menu_instance($info) {
  $menu_options = variable_get('menu_options_' . $info->type, array(
    'main-menu',
  ));
  $default = variable_get('menu_parent_' . $info->type, 'main-menu:0');
  list($default_menu_name, $default_plid) = explode(':', $default);
  return array(
    'field_name' => MENU_LINK_DEFAULT_FIELD,
    'entity_type' => 'node',
    'bundle' => $info->type,
    'label' => t('Menu'),
    'settings' => array(
      'menu_options' => $menu_options,
    ),
    'default_value' => array(
      0 => array(
        'mlid' => NULL,
        'menu_name' => $default_menu_name,
        'plid' => $default_plid,
      ),
    ),
  );
}

/**
 * Adds, updates or deletes the default menu link field from a content type based on its menu settings.
 *
 * @param $info object
 *   The content type object.
 */
function _menu_link_node_menu_update_field($info) {
  $prior_instance = field_read_instance('node', MENU_LINK_DEFAULT_FIELD, $info->type, array(
    'include_inactive' => TRUE,
  ));
  $menu_options = variable_get('menu_options_' . $info->type, array(
    'main-menu',
  ));
  if (!empty($menu_options)) {
    $instance = _menu_link_node_menu_instance($info);
    $t_args = array(
      '%label' => $instance['label'],
      '%name' => $info->name,
    );
    if (empty($prior_instance)) {
      try {
        field_create_instance($instance);
        drupal_set_message(t('The %label field has been added to the %name content type.', $t_args));
      } catch (Exception $e) {
        drupal_set_message(t('There was a problem creating field %label: @message.', array(
          '%label' => $instance['label'],
          '@message' => $e
            ->getMessage(),
        )));
      }
    }
    else {

      // TODO recursive?
      // TODO only update field if actually changed.
      // TODO reactivate inactivate field for new content types using a machine name that has been used before?
      $instance = $prior_instance + $instance;
      try {
        field_update_instance($instance);

        //drupal_set_message(t('The %label field has updated.', $t_args));
      } catch (Exception $e) {
        drupal_set_message(t('There was a problem creating field %label: @message.', array(
          '%label' => $instance['label'],
          '@message' => $e
            ->getMessage(),
        )));
      }
    }
  }
  elseif (!empty($prior_instance)) {
    $t_args = array(
      '%label' => $prior_instance['label'],
      '%name' => $info->name,
    );
    field_delete_instance($prior_instance);
    drupal_set_message(t('The %label field has been removed from the %name content type.', $t_args));
  }
}

/**
 * Synchronizes content type's menu settings with those of the default menu link field.
 *
 * @param $instance array
 *   A field instance definition array.
 */
function _menu_link_node_menu_update_type($instance) {
  if ($instance['entity_type'] == 'node' && $instance['field_name'] == MENU_LINK_DEFAULT_FIELD) {
    variable_set('menu_options_' . $instance['bundle'], $instance['settings']['menu_options']);
    if (isset($instance['default_value'][0])) {
      $default = $instance['default_value'][0]['menu_name'] . ':' . $instance['default_value'][0]['plid'];
    }
    elseif (isset($instance['settings']['menu_options']['main-menu'])) {
      $default = 'main-menu:0';
    }
    else {
      $default = reset($instance['settings']['menu_options']) . ':0';
    }
    variable_set('menu_parent_' . $instance['bundle'], $default);
  }
}

Functions

Namesort descending Description
menu_link_node_menu_field_create_instance Implements hook_field_create_instance().
menu_link_node_menu_field_update_instance Implements hook_field_update_instance().
menu_link_node_menu_menu Implements hook_menu().
menu_link_node_menu_module_implements_alter Implements hook_module_implements_alter().
menu_link_node_menu_node_load Implements hook_node_load().
menu_link_node_menu_node_prepare Implements hook_node_prepare().
menu_link_node_menu_node_type_insert Implements hook_node_type_insert().
menu_link_node_menu_node_type_update Implements hook_node_type_update().
_menu_link_node_menu_instance Builds an instance of the default menu link field based on a content type's menu settings.
_menu_link_node_menu_update_field Adds, updates or deletes the default menu link field from a content type based on its menu settings.
_menu_link_node_menu_update_type Synchronizes content type's menu settings with those of the default menu link field.

Classes

Namesort descending Description
MenuLinkNodeMenu