Статьи

Несколько редакторов на узел в Drupal 7

Одна из вещей, которая делает Drupal великолепным, — это его гибкая система разрешений для пользователей. Стандартная сетка разрешений, с которой мы все знакомы, охватывает большинство случаев использования того, что пользователи могут и не могут делать. Разработчикам модулей также очень легко создавать новые разрешения и роли, которые ограничивают логику, которую они реализуют.

Drupal logo

Тем не менее, я столкнулся с практическим случаем использования, когда параметров конфигурации по умолчанию недостаточно. А именно, если вам нужно иметь нескольких пользователей с правами на редактирование определенного узла данного типа, но без них обязательно иметь доступ для редактирования других таких же типов. Другими словами, следующая замечательная статья должна редактироваться Лорой и Гленном, но не их коллегами. Однако из коробки пользователи определенной роли могут быть хозяевами либо своего собственного контента, либо всего контента определенного типа. Так что это не сразу возможно.

В этой статье я собираюсь показать вам свое решение этой проблемы в виде простого пользовательского модуля с именем editor_list . Узлы статьи будут иметь поле, в котором вы можете выбрать пользователей, и только эти пользователи (или те, у кого есть полный доступ) смогут редактировать этот конкретный узел. Вы можете найти модуль уже в этом git-репозитории и установить его на свой сайт для быстрого запуска. Имейте в виду, что он зависит от модуля Entity Reference, как мы увидим через минуту.

Я буду сводить комментарии кода к минимуму, чтобы сэкономить место, но вы можете найти их в хранилище, если хотите. Базовые знания по Drupal 7 предполагаются в оставшейся части этого урока.

подмости

Сначала нам нужен файл editor_list.info для нашего модуля:

 name = Editor List description = Module illustrating a custom solution for having multiple editors on a node. core = 7.x dependencies[] = entityreference 

Далее нам нужен файл editor_list.module котором будет находиться большая часть нашей бизнес-логики. Так что продолжайте и создайте это, и мы будем заполнять это, поскольку мы продолжаем.

Наконец, хотя здесь это и не рассматривается, у нас может быть файл editor_list.install котором мы можем реализовать hook_install() и hook_update для создания полей и / или развертывания конфигурации. В репозитории вы обнаружите, что я предоставил хук установки, который уже создает поле ссылки на сущность с именем field_editors и присоединяет его к типу контента Article. Если вы следуете, но не используете код в репозитории, вы должны продолжить и создать поле вручную через пользовательский интерфейс. Это простое поле, которое ссылается на сущности пользователя и допускает неограниченный выбор. Ничего особенного.

Доступ к узлу

Возвращаясь к нашему файлу .module , пришло время реализовать нашу логику доступа. Однако сначала, чтобы сделать вещи максимально гибкими и многократно используемыми, давайте создадим простую функцию, которая возвращает массив типов узлов, к которым мы применяем нашу логику доступа:

 function editor_list_node_types() { return array('article'); } 

Так как мы ориентируемся только на статьи, этого будет достаточно. Но мы будем использовать эту функцию в нескольких местах, поэтому в случае, если нам нужно ориентироваться и на другие типы, нам просто нужно обновить этот массив.

Далее, давайте напишем еще одну полезную функцию, которая возвращает все идентификаторы пользователей, заданные в поле редакторов данного узла. Мы также будем использовать это в нескольких местах:

 function editor_list_uids_from_list($node) { $users = field_get_items('node', $node, 'field_editors'); $allowed_uids = array(); if ($users) { $allowed_uids = array_map(function($user) { return $user['target_id']; }, $users); } return $allowed_uids; } 

Я считаю, что эта функция не требует пояснений, поэтому я не буду вдаваться в подробности. Вместо этого мы можем обратиться к нашей реализации hook_node_access() которая вызывается Drupal всякий раз, когда пользователь пытается что- то сделать с узлом (просмотреть, изменить или удалить):

 /** * Implements hook_node_access(). */ function editor_list_node_access($node, $op, $account) { $node_types = editor_list_node_types(); if ( ! is_object($node) || ! in_array($node->type, $node_types) || $op !== 'update') { return NODE_ACCESS_IGNORE; } $allowed_uids = editor_list_uids_from_list($node); if (empty($allowed_uids)) { return NODE_ACCESS_IGNORE; } if (in_array($account->uid, $allowed_uids)) { return NODE_ACCESS_ALLOW; } } 

Так что здесь происходит?

Во-первых, мы используем нашу ранее объявленную вспомогательную функцию, чтобы получить список типов узлов, на которые мы хотим нацелить, и мы в основном игнорируем ситуацию и возвращаем, если тип узла в данный момент доступного узла отсутствует в нашем списке, или если операция является пользователем попытка не относится к типу «обновление». Затем мы используем нашу другую вспомогательную функцию, чтобы проверить, есть ли пользователи в списке редакторов для этого узла, и снова проигнорируем ситуацию, если ее нет. Однако, если они есть, и наш доступный пользователь среди них, мы возвращаем константу NODE_ACCESS_ALLOW которая в основном дает пользователю доступ для выполнения предпринятой операции. Вот и все.

Вы можете проверить документацию для получения дополнительной информации о том, как этот хук работает.

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

Доступ к полю

Теперь у нас есть рабочий модуль, который делает то, что я изначально намеревался сделать. Но допустим, что ваши администраторы — единственные, кто отвечает за добавление пользователей в списки редакторов. Другими словами, вы боитесь, что если ваши редакторы смогут редактировать свои узлы и удалять себя из списка, они будут заблокированы из узла, с которым они должны работать.

Чтобы учесть эту ситуацию, нам нужно реализовать проверку доступа к полю и устранить возможность того, что редакторы вмешиваются в это поле. Реализация hook_field_access должна хорошо сработать . И если вам интересно, этот хук похож на hook_node_access() но отвечает за отдельные поля, а не за весь узел (+ пара других небольших различий).

 /** * Implements hook_field_access(). */ function editor_list_field_access($op, $field, $entity_type, $entity, $account) { $node_types = editor_list_node_types(); if ($entity_type === 'node' && is_object($entity) && in_array($entity->type, $node_types)) { return editor_list_control_field_access($op, $field, $entity_type, $entity, $account); } } 

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

 function editor_list_control_field_access($op, $field, $entity_type, $entity, $account) { if ($op !== 'edit') { return; } $uids = editor_list_uids_from_list($entity); if (!in_array($account->uid, $uids)) { return; } $deny = array('field_editors'); if (in_array($field['field_name'], $deny)) { return false; } }} 

Поскольку нам важно только, если пользователь пытается обновить определенное поле, мы ничего не возвращаем, если это не так. Имейте в виду, что строка операции здесь edit а не update как это было в другом хуке. Это всего лишь одна из тех причуд непоследовательности в Drupal, которую мы так полюбили . И, как и прежде, мы игнорируем ситуацию, если текущий пользователь не является частью списка редактора.

Затем мы определяем массив имен полей, к которым мы хотим запретить доступ (в нашем случае только одно, но мы можем добавить к нему в зависимости от варианта использования). Наконец, мы возвращаем false, если текущее доступное поле является частью нашего массива $deny . Еще одно отличие здесь в том, что мы должны возвращать логическое значение вместо константы, как мы делали раньше.

Теперь редакторы в списке данного узла не могут удалить себя или добавить кого-либо еще в список. Но опять же, в некоторых случаях вы можете захотеть эту функциональность, а в других нет. Тебе решать.

Убирать

Последнее, что я собираюсь показать вам здесь, относится к организации и, возможно, немного опыта пользователя. В нашей текущей реализации поле списка редакторов на узлах Article присутствует где-то на форме (куда бы вы ни перетаскивали его при редактировании настроек поля). Однако разве не было бы неплохо, если бы она автоматически входила в Authoring information группу Authoring information внизу страницы? Что-то вроде этого:

Drupal 7 multiple editors per node

Я думаю так. Посмотрим, как мы можем это сделать.

Во-первых, нам нужно реализовать hook_form_alter или один из его вариантов. Я предпочитаю самый целевой, чтобы избежать ненужных вызовов и кучу условных проверок:

 /** * Implements hook_form_BASE_FORM_ID_alter(). */ function editor_list_form_article_node_form_alter(&$form, &$form_state, $form_id) { $form['#after_build'][] = 'editor_list_node_form_after_build'; } 

Здесь мы использовали BASE_FORM_ID узлов статьи, поэтому, если мы расширим наше приложение для других типов, мы сделаем то же самое для них. Внутри мы просто определяем функцию #after_build которая будет запускаться, когда форма завершит #after_build . Это делается для того, чтобы все внесенные изменения в форму были уже внесены добавленными модулями. Осталось только написать функцию, отвечающую за внесение изменений в форму:

 function editor_list_node_form_after_build($form, &$form_state) { $field = field_info_field('field_editors'); if ( ! field_access('edit', $field, 'node', $form['#entity'])) { return $form; } if ($form['author']['#access'] === 0) { return $form; } $field_editors = $form['field_editors']; $field_editors['#weight'] = 0; $form['author']['additional_authors'] = $field_editors; $form['field_editors'] = array(); return $form; } 

Это выглядит сложно, но на самом деле это не так. Мы начинаем с загрузки определения поля нашего списка редактора. Это field_access того, чтобы мы могли запустить проверку field_access и просто вернуть массив формы без изменений, если текущий пользователь не имеет доступа к полю. Далее мы делаем то же самое, если текущий пользователь не имеет доступа к группе author в форме (это Authoring information группа « Authoring information », в которую мы хотим поместить поле). И, наконец, мы делаем копию определения поля, меняем его вес и помещаем его в группу, а затем сбрасываем исходное определение, чтобы избежать дублирования.

И это в значительной степени это. Теперь поле списка редакторов должно быть заправлено остальной информацией, связанной с авторством.

Вывод

В этой статье мы создали решение проблемы редактирования контента, которую Drupal 7 не смог исправить из коробки. Тем не менее, он предоставил нам инструменты разработки, необходимые для того, чтобы сделать это легкой задачей внутри пользовательского модуля.

Теперь у нас есть поле списка редактора в форме узла статьи, с помощью которого мы можем точно указать, какие пользователи имеют доступ к этому конкретному узлу. Хотя имейте в виду, что для того, чтобы это было полезным, пользователи, которых вы добавляете в эти списки, не должны иметь роли, позволяющей им редактировать все узлы статьи. В противном случае вы не увидите большой разницы.