Как добавить условие доступа к пункту меню?

17

Как я могу добавить новые условия поверх существующих условий, которые определяют, виден ли пункт меню? Эти условия не должны ограничиваться настройками разрешений.

В качестве примера использования (не обязательно причина этого вопроса): Предположим, у меня есть тип контента, пользователям которого разрешено создавать только один узел. У меня есть пункт меню для добавления этого типа контента. Но если пользователь уже создал узел этого типа контента, я хочу скрыть пункт меню. Моя первая мысль - запустить запрос, чтобы проверить существование узла, созданного текущим пользователем, который имеет определенный тип контента. Если он существует, скрыть пункт меню.

Я думаю, что этот тип функциональности следует hook_menu_alter()добавить и добавить необходимую логику. Но я не уверен, как это сделать, не обходя существующие проверки, например, проверяя, есть ли у пользователя права на создание контента такого типа. Должен ли я включить эту логику в свое собственное состояние? Или я могу добавить к существующей логике доступа, не перезаписывая ее?


Изменить: Некоторые люди, кажется, сосредоточены на том, чтобы ответить «как я могу ограничить пользователя созданием одного узла типа контента». Это не вопрос здесь. Вопрос в том, как добавить пользовательские условия доступа к пункту меню.

Chaulky
источник

Ответы:

11

Что вам нужно сделать, это добавить свой обратный вызов через hook_menu_alter (), а затем внутри обратного вызова вы просто выполняете свою логику, а затем возвращаете данные через исходный обратный вызов.

Чтобы быть уверенным, что вы не перезаписываете какие-либо другие изменения hook_menu_alter (), вы должны передать предыдущий обратный вызов вашему обратному вызову через аргумент доступа.

Это все теоретически, но код должен выглядеть примерно так:

MYMODULE_menu_alter(&$items) {
  $items['menu']['access arguments'] = array_merge(array($items['menu']['access callback']), $item['menu']['access arguments']);
  $items['menu']['access callback'] = 'MYMODULE_access_callback';
}

MYMODULE_access_callback() {
  $args = func_get_args();

  // Do Stuff.
  if ($something == FALSE) {
    return FALSE;
  }

  $function = array_shift($args);
  return call_user_func_array($function, $args);
}
Расшифруйте
источник
так что, если я назначу новую функцию обратного вызова доступа, это определенно перезапишет оригинальный обратный вызов?
Чолки
Да, вы можете иметь только один обратный вызов доступа для каждого элемента меню, поэтому убедитесь, что вы вернетесь к исходному обратному вызову. Я видел модуль, который делает что-то подобное, один из тяжелых модулей с правами доступа, но не могу вспомнить, какой именно.
Расшифровка
что делает array_shift для $ args?
Чолки
Он вытягивает первый аргумент из «аргументов доступа», который мы сделали старым «обратным вызовом доступа». Так что, если старый обратный вызов был «MYMODULE2_access_callback», это то, что возвращает array_shift. Он также удаляет его из массива, чтобы мы передавали только те аргументы, которые ожидает обратный вызов.
Расшифровка
1

В ответ на комментарии выше, решение в D7 будет использовать:

/**
 * Implements hook_node_access().
 */
function mymodule_node_access($node, $op, $account) {
  $type = is_string($node) ? $node : $node->type;

  if ($op == 'create' && $type == 'mynodetype' && db_query("SELECT 1 FROM {node} WHERE type = :type AND uid = :uid", array(':type' => $type, ':uid' => $account->uid))->fetchField()) {
    // If the user has already created a node of a specific type, they cannot
    // create any more.
    return NODE_ACCESS_DENY;
  }

  // Otherwise do not affect any node access.
  return NODE_ACCESS_IGNORE;
}
Дейв Рид
источник
1
Похоже, это не имеет никакого отношения к пунктам меню. Я еще не слишком знаком с D7, но похоже, что это специфично для создания узла. Вопрос сосредоточен на пунктах меню в целом.
Чолки
О, я понимаю ... это в ответ на мой комментарий, требующий более подробной информации о вашем решении D7, предложенном в вашем ответе, который указывает на модуль Node Limit. Все еще немного не по теме, но ценится.
Чолки
Потому что видимость ссылок create mynodetype контролируется функцией node_access (), которая будет вызывать этот хук в Drupal 7.
Дейв Рейд
1

Вы ищете модуль API доступа к Chain Menu .

API доступа к цепочечному меню позволяет вашему модулю связывать свои собственные функции обратного вызова при доступе к меню в записи маршрутизатора меню других модулей.

На Drupal Stack Exchange есть как минимум один пример того, как его использовать.

crantok
источник
-1

Одним из вариантов будет создание новой роли, которая имеет разрешение на создание контента для вашего типа контента. После того, как пользователь создаст узел этого типа, уберите эту роль, и он больше не сможет его создать.

cjworden
источник
-1

Возможно, вам стоит попробовать модуль Node Limit .

Со страницы проекта:

Модуль Node Limit позволяет администраторам ограничивать количество узлов определенного типа, которые могут создавать роли или пользователи. Например, если у сайта есть роль «Рекламодатель», которая может создавать «рекламные» узлы, то администратор ограничения узлов может ограничить всех пользователей в этой роли определенным числом узлов. Он также может ограничивать пользователей для каждого пользователя.

Дейв Рид
источник
Ограничение одним узлом является лишь примером использования для добавления метода обратного вызова настраиваемого доступа. Кроме того, Node Limit не удаляет пункт меню, он просто запрещает пользователю добавлять еще один узел этого типа контента.
Чолки
Это правда, теперь, когда я снова просматриваю описание модуля. Если бы это было на Drupal 7, это было бы на самом деле легко, поскольку вы можете использовать hook_node_access ($ node, 'create', $ account), который мог бы повлиять на видимость самой ссылки на создание типа узла.
Дэйв Рейд
Это интересно. Я планирую перейти на D7 в ближайшее время. Вы не могли бы написать более подробно и опубликовать ответ?
Чалки
D7 версия ответа размещена.
Дейв Рейд