You are here

content_menu.module in Content Menu 8

Same filename and directory in other branches
  1. 7 content_menu.module

File

content_menu.module
View source
<?php

/**
 * @file
 * Main code for the content_menu.module.
 *
 * ToDo: Use view query alter hook to mandatorily filter existing-node-selection
 *       by only those node types that can have a menu item in the resp. menu.
 *
 * ToDo: Mark menu items in item listing that are linked to unpublished content.
 *       NOTE: menu links to unpublished nodes are suppressed by Drupal.
 *       => Option 1: Alter Drupal's behavior to show unpublished links, too,
 *                    and then mark them as unpublished (new permission).
 *       => Options 2: Leave everything as is.
 *
 * ToDo: Use menu.module's variable 'menu_override_parent_selector' to provide
 *       custom more scalable menu parent selector.
 *
 * ToDo: Pre-placement of new menu items is sometimes slightly misplaced.
 *
 */
DEFINE('CONTENT_MENU_ADD_ITEM_WEIGHT', 50);

/**
 * Implements hook_permission().
 */
function content_menu_permission() {
  return array(
    // Introduce new permission for editing default system menus.
    'administer system menus' => array(
      'title' => t('Administer system menus'),
      'description' => t('Administer system menus'),
    ),
  );
}

/**
 * Implements hook_menu().
 */
function content_menu_menu() {

  // Add menu router item for use as target with dummy menu items.
  $items['menu-dummy'] = array(
    'title' => 'Placeholder for menu item dummies.',
    'page callback' => 'content_menu_menu_callback_menu_dummy',
    'access arguments' => array(
      'access content',
    ),
    'type' => MENU_CALLBACK,
  );
  $items['admin/structure/menu/manage/add_existing_view'] = array(
    'title' => 'Add existing view.',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'content_menu_add_exiting_view',
    ),
    'access arguments' => array(
      'access content',
    ),
    'file' => 'content_menu.menu_admin.inc',
  );
  return $items;
}

/**
 * Menu router callback for url "menu-dummy".
 * For use as target with dummy menu items.
 */
function content_menu_menu_callback_menu_dummy() {
  return t('This is an empty placeholder page for a menu item.<br /><br />You can replace it with a valid target page URL in the <a href="@url">menu administration</a>.', array(
    '@url' => url('admin/structure/menu'),
  ));
}

/**
 * Implements hook_entity_info_alter().
 */
function content_menu_entity_info_alter(&$entity_info) {
  $entity_info['menu']['list_controller_class'] = 'Drupal\\content_menu\\ContentMenuListController';
}

/**
 * Implements hook_theme().
 */
function content_menu_theme() {
  return array(
    // Theming function for extended menu overview form rendering.
    'menu_overview_form_extended' => array(
      'file' => 'content_menu.menu_admin.inc',
      'render element' => 'form',
    ),
  );
}

/**
 * Implements hook_page_alter().
 *
 * Set consistent menu admin breadcrumbs on pages other than menu admin forms.
 */
function content_menu_page_alter(&$page) {
  $menu_paths = array(
    'admin/structure/menu/',
    'admin/structure/menu-position/',
    content_menu_variable_get_add_existing_content_url(),
  );
  $path = current_path();
  foreach ($menu_paths as $menu_path) {
    if (strpos($path, $menu_path) === 0) {
      content_menu_set_menu_admin_breadcrumb();
      break;
    }
  }
  if (current_path() == content_menu_variable_get_add_existing_content_url()) {
    $item = content_menu_get_menu_item_from_querystring();
    if (isset($item['mlid']) && $item['mlid'] != 0) {
      drupal_set_message(t('You`re about to select a new content target for menu item "%title".', array(
        '%title' => $item['title'],
      )));
      drupal_set_message(t('Now you can select an existing content item for menu item "%title".', array(
        '%title' => $item['title'],
      )));
    }
  }
}

/**
 * Set consistent menu admin breadcrumb.
 */
function content_menu_set_menu_admin_breadcrumb($menu_name = NULL) {
  $breadcrumb = array();
  $breadcrumb[] = l(t('Home'), '<front>');
  $breadcrumb[] = l(t('Administration'), 'admin');
  $breadcrumb[] = l(t('Structure'), 'admin/structure');
  $breadcrumb[] = l(t('Menus'), 'admin/structure/menu');
  if ($menu_name == NULL) {
    $menu_item = content_menu_get_menu_item_from_querystring();
    if (isset($menu_item['name'])) {
      $menu_name = $menu_item['name'];
    }
  }
  if ($menu_name) {
    $menus = menu_get_menus();
    $menu_title = t($menus[$menu_name]);
    $breadcrumb[] = l(t($menu_title), 'admin/structure/menu/manage/' . $menu_name);
    drupal_set_title(t($menu_title));
  }
  drupal_set_breadcrumb($breadcrumb);
}

/**
 * Implements hook_menu_link_insert().
 */
function content_menu_menu_link_insert($menu_link) {
  content_menu_mark_link_updated($menu_link);
}

/**
 * Implements hook_menu_link_update().
 */
function content_menu_menu_link_update($menu_link) {
  content_menu_mark_link_updated($menu_link);
}

/**
 * Remember newly created menu items to highlight them
 * on the next menu admin page view/request.
 *
 * @param \Drupal\menu_link\Plugin\Core\Entity\MenuLink $menu_link
 *   A menu link entity.
 */
function content_menu_mark_link_updated($menu_link) {

  // Save menu links created on or right before menu admin pages to session var.
  if (isset($_GET['destination']) && strpos($_GET['destination'], 'admin/structure/menu/manage/') === 0 || strpos(current_path(), 'admin/structure/menu/manage/') === 0) {
    $menu_link->created = time();
    $_SESSION['content_menu_inserted_links'][$menu_link
      ->id()] = $menu_link;
  }
}

/**
 * Implements hook_form_FORM_ID_alter().
 *
 * Improve menu_edit_item form for better authoring experience.
 */
function content_menu_form_menu_link_form_alter(&$form, &$form_state) {

  // Pre-populate menu item form elements with data from querystring.
  if (isset($_GET['menu_title'])) {
    $menu_item = content_menu_get_menu_item_from_querystring();

    // If even link_path is given, create item right away and go back.
    if (!empty($menu_item['link_path'])) {
      content_menu_link_save($menu_item);
      drupal_goto(check_plain($_GET['destination']));
    }
    $form['link_title']['#default_value'] = $menu_item['title'];
    $form['link_path']['#default_value'] = $menu_item['link_path'];
    $form['parent']['#default_value'] = $menu_item['name'] . ':' . $menu_item['plid'];
    $form['weight']['#default_value'] = $menu_item['weight'];
    $form['enabled']['#default_value'] = !$menu_item['hidden'];
  }

  // Extend breadcrumb
  if ($form['mlid']['#value']) {
    $menu = explode(':', $form['parent']['#default_value']);
    content_menu_set_menu_admin_breadcrumb($menu[0]);
  }

  // Simplify the form, by putting all "advanced" fields in fieldset.
  $form['advanced'] = array(
    '#type' => 'details',
    '#weight' => 100,
    '#title' => t('Advanced menu item settings'),
  );
  $advanced_elements = array(
    'description',
    'expanded',
    'parent',
    'weight',
  );
  foreach ($advanced_elements as $el_key) {
    $form['advanced'][$el_key] = $form[$el_key];
    unset($form[$el_key]);
  }
}

/**
 * Implements hook_form_FORM_ID_alter().
 *
 * Improve menu_item_delete_form form for better authoring experience.
 */
function content_menu_form_menu_item_delete_form_alter(&$form, &$form_state, $form_id) {
  if ($form['#item']['mlid']['#value']) {

    // Extend breadcrumb
    $menu_name = $form['#item']['menu_name'];
    content_menu_set_menu_admin_breadcrumb($menu_name);

    // Add question text to form (instead of having it as the page title).
    $form['question'] = array(
      '#type' => 'markup',
      '#markup' => '<div class="question">' . t('Are you sure you want to delete the custom menu link %item?', array(
        '%item' => t($form['#item']['link_title']),
      )) . '</div><p></p>',
      '#weight' => -100,
    );

    // If menu item to delete links to a node, offer the choice to
    // delete the associated node as well, if user is permitted.
    if ($form['#item']['router_path'] == 'node/%') {
      $nid = explode('/', $form['#item']['link_path']);
      if (isset($nid[1]) && is_numeric($nid[1])) {
        $node = node_load($nid[1]);
        if (is_object($node) && node_access('delete', $node)) {
          $form['delete_node'] = array(
            '#type' => 'checkbox',
            //'#markup' => '<div class="question">' . t('Are you sure you want to delete the custom menu link %item?', array('%item' => t($form['#item']['link_title']))) . '</div><p></p>',
            '#title' => t('Delete associated %type <a href="@url">%title</a> as well.', array(
              '%type' => node_type_get_name($node),
              '@url' => url('node/' . $node->nid),
              '%title' => $node->title,
            )),
            '#default_value' => FALSE,
            '#weight' => -50,
          );
          $form['delete_node_nid'] = array(
            '#type' => 'value',
            '#value' => $node->nid,
          );
          $form['#submit'] = array(
            'content_menu_item_delete_form_submit',
          );
        }
      }
    }
  }
}

/**
 * Extended submit handler for menu_item_delete_form.
 */
function content_menu_item_delete_form_submit($form, &$form_state) {

  // Call default submit handler to handle deletion of menu item itself.
  menu_item_delete_form_submit($form, $form_state);

  // Delete an associated node as well, if intended and user is permitted.
  if ($form_state['input']['delete_node'] == 1) {
    $nid = explode('/', $form['#item']['link_path']);
    if (isset($nid[1]) && is_numeric($nid[1])) {
      $node = node_load($nid[1]);
      if (is_object($node) && node_access('delete', $node) && $node->nid == $form_state['values']['delete_node_nid']) {
        node_delete($node->nid);
        watchdog('content', '@type: deleted %title.', array(
          '@type' => $node->type,
          '%title' => $node->title,
        ));
        drupal_set_message(t('@type %title has been deleted.', array(
          '@type' => node_type_get_name($node),
          '%title' => $node->title,
        )));
      }
    }
  }
}

/**
 * Implements hook_form_FORM_ID_alter().
 *
 * Pre-populate input elements in menu_position_add_rule_form.
 */
function content_menu_form_menu_position_add_rule_form_alter(&$form, &$form_state, $form_id) {

  // Pre-populate menu item form elements with data from querystring.
  if (isset($_GET['menu_title'])) {
    $menu_item = content_menu_get_menu_item_from_querystring();
    $form['admin_title']['#default_value'] = $menu_item['title'];
    $form['plid']['#default_value'] = $menu_item['name'] . ':' . $menu_item['plid'];
  }
}

/**
 * Implements hook_form_FORM_ID_alter().
 *
 * Overhaul the menu_overview_form to improve menu authoring experience.
 */
function content_menu_form_menu_overview_form_alter(&$form, &$form_state) {
  if (config('content_menu.settings')
    ->get('alter_all_menus') || content_menu_is_menu_considered($form['#menu']['menu_name'])) {
    module_load_include('inc', 'content_menu', 'content_menu.menu_admin');
    _content_menu_form_menu_overview_form_alter($form, $form_state, $form_id);
  }
}

/**
 * Implements template_preprocess_views_view_field().
 *
 * For views to select exiting content, rewrite the nid field ("select" link)
 * to link to the correct target url and pass through the querystring params.
 */
function content_menu_preprocess_views_view_field(&$vars) {
  $view =& $vars['view'];
  $field =& $vars['field'];
  $selection_view = explode(':', config('content_menu.settings')
    ->get('add_existing_content_view'));
  if ($field->field == 'nid' && $view->name == $selection_view[0] && (!isset($selection_view[1]) || $view->current_display == $selection_view[1])) {
    module_load_include('inc', 'content_menu', 'content_menu.menu_admin');
    $item = content_menu_get_menu_item_from_querystring();
    $item['link_path'] = 'node/' . $field->original_value;
    $url = 'admin/structure/menu/manage/' . $item['name'] . '/add';
    $vars['output'] = l(t('select'), $url, array(
      'query' => content_menu_assemble_query_string($item),
    ));
  }
}

/**
 * Implements hook_menu_item_target_types_alter().
 *
 * Extend the target types for a new menu item provided by default.
 * See content_menu.api.php for further documentation.
 */
function content_menu_menu_item_target_types_alter(&$target_types, &$context) {
  module_load_include('inc', 'content_menu', 'content_menu.menu_admin');
  _content_menu_menu_item_target_types_alter($target_types, $context);
}

/**
 * Alter a menu item's form element in the menu item administration.
 *
 * @param $el Array The form element of the menu item to alter.
 */
function content_menu_menu_item_element_alter(&$el) {
  module_load_include('inc', 'content_menu', 'content_menu.menu_admin');
  _content_menu_menu_item_element_alter($el);
}

//
//
// Menu management reachability improvements
//
//

/**
 * Implements hook_node_insert().
 *
 * After creating a new node, if it's being created to replace a menu-dummy, perform the replacement.
 */
function content_menu_node_insert($node) {

  // Ensure that this node is replacing a menu dummy.
  if (isset($_GET['menu_link_path']) && $_GET['menu_link_path'] == 'menu-dummy') {

    // Update the dummy that is being replaced by this node.
    $menu_item = content_menu_get_menu_item_from_querystring();
    $menu_item['link_path'] = "node/" . $node->nid;
    content_menu_link_save($menu_item);
  }
}

/**
 * Implements hook_form_alter().
 *
 * Improve menu item management for node edit forms.
 */
function content_menu_form_alter(&$form, &$form_state, $form_id) {
  if (isset($form['#node_edit_form'])) {

    // Add link to menu overview / admin form to menu fieldset.
    if (user_access('administer menu')) {
      if ($form['#node_edit_form'] && isset($form['menu'])) {
        $menu = explode(':', $form['menu']['link']['parent']['#default_value']);
        $form['menu']['link']['menu_admin_link'] = array(
          '#type' => 'markup',
          '#markup' => l(t('Manage menu structure') . ' …', 'admin/structure/menu/manage/' . $menu[0], array(
            'attributes' => array(
              'target' => '_blank',
            ),
          )),
        );
      }
    }
    if (isset($form['menu'])) {
      if (isset($_GET['menu_link_path']) && $_GET['menu_link_path'] == 'menu-dummy') {

        // If this node is being created to replace a menu dummy, substitute the usual menu form for a description of what will happen.
        $form['menu']['enabled']['#access'] = FALSE;
        $form['menu']['link']['#access'] = FALSE;
        $trail = content_menu_get_menu_trail($_GET['menu_mlid']);
        $form['menu']['menu_dummy_replace'] = array(
          '#markup' => t('A link to this node will replace the menu dummy at <br> %trail', array(
            '%trail' => implode(' » ', $trail),
          )),
        );
        $form['title']['#default_value'] = $form['title_field']['und']['0']['value']['#default_value'] = $_GET['menu_title'];
      }
      else {

        // Hide description field from menu item fieldset.
        $form['menu']['link']['description']['#access'] = FALSE;

        // Pre-Populate menu item fields, if given via querystring.
        if (isset($_GET['menu_title'])) {
          $menu_item = content_menu_get_menu_item_from_querystring();

          // Set node form's menu item input field according to query string input.
          $form['menu']['enabled']['#default_value'] = 1;
          $form['menu']['link']['hidden']['#value'] = $menu_item['hidden'];
          $form['menu']['link']['link_title']['#default_value'] = $menu_item['title'];
          $form['menu']['link']['parent']['#default_value'] = $menu_item['name'] . ':' . $menu_item['plid'];
          $form['menu']['link']['weight']['#default_value'] = $menu_item['weight'];
          $form['title']['#default_value'] = $form['title_field']['und']['0']['value']['#default_value'] = $menu_item['title'];
        }
      }
    }
  }
}

//
//
// Helper functions
//
//

/**
 * Build the menu trail array that points to the given mlid.
 */
function content_menu_get_menu_trail($leaf_mlid) {
  $menu_link = menu_link_load($leaf_mlid);
  $menu = menu_load($menu_link['menu_name']);
  $trail = array();
  while ($menu_link) {
    array_unshift($trail, $menu_link['link_title']);
    $menu_link = menu_link_load($menu_link['plid']);
  }
  array_unshift($trail, $menu['title']);
  return $trail;
}

/**
 * Build menu item data from querystring.
 *
 */
function content_menu_get_menu_item_from_querystring() {

  // Sanitize querystring input.
  $menu_item = array();
  foreach ($_GET as $get_key => $get_value) {
    if (substr($get_key, 0, 5) == 'menu_') {
      $menu_item[check_plain(substr($get_key, 5))] = strip_tags($get_value);
    }
  }
  return $menu_item;
}

/**
 * Check if a given menu (name) applies for being considered by this module.
 */
function content_menu_is_menu_considered($menu_name) {
  $exclude_menus = config('content_menu.settings')
    ->get('special_menus');
  return !in_array($menu_name, $exclude_menus);
}

/**
 * Wrapper for menu_link_save() to create new menu link item from array.
 */
function content_menu_link_save($menu_item) {
  $menu_item['menu_name'] = $menu_item['name'];
  $menu_item['link_title'] = $menu_item['title'];
  $menu_item['customized'] = 1;
  return menu_link_save($menu_item);
}

/**
 * Get config variable 'content_menu_add_existing_content_url'.
 */
function content_menu_variable_get_add_existing_content_url() {
  $url = config('content_menu.settings')
    ->get('content_menu_add_existing_content_url');

  // If using default view, ensure to only goto view page if views is enabled.
  if ($url == 'admin/structure/menu/manage/add_existing_content' && !module_exists('views')) {
    $url = NULL;
  }
  return $url;
}

Functions

Namesort descending Description
content_menu_entity_info_alter Implements hook_entity_info_alter().
content_menu_form_alter Implements hook_form_alter().
content_menu_form_menu_item_delete_form_alter Implements hook_form_FORM_ID_alter().
content_menu_form_menu_link_form_alter Implements hook_form_FORM_ID_alter().
content_menu_form_menu_overview_form_alter Implements hook_form_FORM_ID_alter().
content_menu_form_menu_position_add_rule_form_alter Implements hook_form_FORM_ID_alter().
content_menu_get_menu_item_from_querystring Build menu item data from querystring.
content_menu_get_menu_trail Build the menu trail array that points to the given mlid.
content_menu_is_menu_considered Check if a given menu (name) applies for being considered by this module.
content_menu_item_delete_form_submit Extended submit handler for menu_item_delete_form.
content_menu_link_save Wrapper for menu_link_save() to create new menu link item from array.
content_menu_mark_link_updated Remember newly created menu items to highlight them on the next menu admin page view/request.
content_menu_menu Implements hook_menu().
content_menu_menu_callback_menu_dummy Menu router callback for url "menu-dummy". For use as target with dummy menu items.
content_menu_menu_item_element_alter Alter a menu item's form element in the menu item administration.
content_menu_menu_item_target_types_alter Implements hook_menu_item_target_types_alter().
content_menu_menu_link_insert Implements hook_menu_link_insert().
content_menu_menu_link_update Implements hook_menu_link_update().
content_menu_node_insert Implements hook_node_insert().
content_menu_page_alter Implements hook_page_alter().
content_menu_permission Implements hook_permission().
content_menu_preprocess_views_view_field Implements template_preprocess_views_view_field().
content_menu_set_menu_admin_breadcrumb Set consistent menu admin breadcrumb.
content_menu_theme Implements hook_theme().
content_menu_variable_get_add_existing_content_url Get config variable 'content_menu_add_existing_content_url'.