Создание нового типа материала.

Главные вкладки

Аватар пользователя player player 15 января 2008 в 16:13

Модернизированная версия перевода http://drupal.org/node/132845

После прочтения вами "Создание собственного модуля" вы знаете базовые хуки, блоки и возможные формы. Сейчас мы используем больше различных API Drupal и создадим новый тип материала (node type). Перед созданием нового типа материала, сначала решите для себя, действительно ли вам нужно создавать для этого новый модуль. Если вы можете использовать CCK и Views для сбора и манипуляции с данных, то Вам возможно не нужен этот урок. Тем не менее если Вы нуждаетесь в манипулировании вашими данными необычным способом, или имеете составные взаимосвязанные данные, что не может быть сделано CCK, или если же вы желаете знать больше о внутренностях Drupal, тогда у вас есть основания чтобы пройти этот урок.

Введение

Простой тип материала (тип контента), такой как "page" или "story", это наиболее элементарная сторона сайта на Drupal. Это ничего больше чем Название(Title), Тело(Body), и необязательный тизёр(Teaser). Вы сможете легко построить свой тип материала шаг за шагом программируя для Drupal. Научитесь этому и вы поймете как может быть гибок Drupal с его контентом.

Почему вам нужно знать это?

Drupal 5.x теперь легко позволяет вам создавать простые типы материала используя интерфейс администратора, не так ли? Ответ на это - "Да", и все абсолютно довольны этим. Спасибо, Drupal разработчикам!
Когда появился модуль CCK он сделал нашу жизнь еще легче!

Тут приведен пример создания своего собственного типа материала в API - node_example.
Здесь не вносится изменений в базу данных или изменения чужого кода, результат будет новый модуль, который вам нужно загрузить в свой собственный каталог модулей.

Что Вам нужно

  • файловый менеджер(чтобы загрузить и/или скачать файл)
  • текстовый редактор
  • небольшие знания php (необязательны даже)
  • некоторая информация о вашем типе материала.
    • TECHNICAL-NAME - имя для вашего типа материала - лимит 32 символа - не используйте имя соществующего модуля - без пробелов - цыфры и знаки препинания, если вы не знаете что делаете
    • USER-FRIENDLY-NAME - имя для вашего типа материала (и множественных версий с этим именем) (так как ваш модуль будет называться в большинстве мест на сайте) - пробелы и цифры можно использовать, но знаки пунктуации если вы знаете что делаете, Замечание: Вы можете использовать тоже имя что и TECHNICAL-NAME
    • MODULE-DESCRIPTION - короткое описание вашего типа материала которое будет показываться на странице настроек модулей admin->modules page - не используйте кавычки и апострофы , если вы не знаете что делаете
    • CREATE-CONTENT-DESCRIPTION - короткое описание вашего типа материала которое будет показываться на странице создания материалов - не используйте кавычки и апострофы , если вы не знаете что делаете
    • HELP-DESCRIPTION - короткое описание вашего типа материала которое будет показываться на странице помощи - не используйте кавычки и апострофы , если вы не знаете что делаете.

Как пример простой тип - "пресс релиз" как тип материала.

* TECHNICAL-NAME = "press_release"
* USER-FRIENDLY-NAME = "Пресс Релиз"
* USER-FRIENDLY-PLURAL = "Пресс Релиз"
* MODULE-DESCRIPTION = "Включение типа материала пресс релиз."
* CREATE-CONTENT-DESCRIPTION = "Создание типа материала пресс релиз."
* ADMIN-HELP-TEXT = "Этот модуль написал ..."

Я специально использовал этот пример и два слова "press release". TECHNICAL-NAME: "press_release" (обратите внимание на подчеркивание) и USER-FRIENDLY-NAME: "Пресс Релиз" для того чтобы при написании кода и внесении в него изменений было проще и понятнее. Вы можете использовать любые другие значения.
Далее жирным текстом будут показаны места где внесены изменения. Апострофы и кавычки в коде очень важны, пожалуйста будте внимательны. Где вы увидете ----- это показывает начало и конец кода и в Вашем коде это не нужно.

Существует огромные изменения, которые произошли с drupal 5.x - для работы модуля вам необходимо 2 файла. В предыдущих версиях нужен был только один файл.
Первый файл это .info файл. Это требование drupal 5.x. Про его создание уже писалось в "Создание собственного модуля"
Это не php файл так что <?php писать не надо Общий формат таков.

; $Id$
name = Имя модуля
description = "Описание модуля."

Замечание: Без этого файла модуля не будет в списке на странице администрирования.
Замечание для РОССИИ. Ели не хотите проблем с кодировкой пишите все в UTF-8

Итак:
-----
; $Id$
name = USER-FRIENDLY-NAME
description = "MODULE-DESCRIPTION"
-----
В нашем примере:
-----
; $Id$
name = Пресс Релиз
description = "Включение типа материала пресс релиз."
-----

Да, Вам нужны эти 3-и строки минимум.
Сохраните файл как "TECHNICAL-NAME.info" (в нашем случае: "press_release.info")
Второй нужный нам файл .module уже более тяжел на подъем.

В node_example приведены примеры нескольких хуков. node_example тип материала имеющий свои собственные добавочные поля. Этот урок делался всеголишь как пример. Если вам нужны дополнительные поля то этот урок будет полезен.
.module это файл где собраны все нашит хуки. Хуки очень просты для понимания. Итак, из предыдущего урока повторим, повторим что такое хук.
"Все функции которые Drupal будет использовать в нашем модуле будут иметь следующий вид {modulename}_{hook}, где hook это предопределенное имя суффикс функции. Drupal вызывает эти функции получая специальные данные владея которыми Drupal знает куда ему смотреть."

Рассмотрим примененные в примере хуки

-Эти хуки могут быть применены в каждом модуле типа материала. Например мы используем эти хуки.

  • hook_node_info()
  • hook_perm()
  • hook_access()
  • hook_form()

-Эти хуки необходимы если вы решили добавить поля при создании нового типа материала. Но в нашем примере мы не будем использовать их.

  • hook_insert()
  • hook_update()
  • hook_delete()
  • hook_validate()
  • hook_nodeapi()
  • hook_view()
  • hook_load()

-Этот хук тоже не появится в примере, однако для примера его покажем.

  • hook_help()

Замечание: Считается хорошей практикой писать следующие комментарии перед каждым хуком

<?php
/**
* Выполнение hook_{здесь_имя_хука}().
*/
?>

Итак давайте начнем. Это PHP файл, так что самая первая строка должна быть <?php

Выполнение hook_node_info(). Эта функция заменяет hook_node_name() и hook_node_types() в 4.6. Drupal 5 значительно расширил её.
Это обязательный хук и может определять многие вещи о типе материала. Минимальны следующие определения.

Для node_example:

<?php
function node_example_node_info() {
return array(
'node_example' => array(
'name' => t('Пример материала'),
'module' => 'node_example',
'description' => t("Это пример нового типа материала с новыми полями."),
)
);
}
?>

Рассмотрим поподробнее:
-----
<?php
function TECHNICAL-NAME_node_info() {
return array(
'TECHNICAL-NAME' => array(
'name' => t('USER-FRIENDLY-NAME'),
'module' => 'TECHNICAL-NAME',
'description' => t("CREATE-CONTENT-DESCRIPTION"),
)
);
}
?>
-----

В нашем примере:
-----
<?php
function press_release_node_info() {
return array(
'press_release' => array(
'name' => t('Пресс Релиз'),
'module' => 'press_release',
'description' => t("Создать пресс релиз"),
)
);
}
?>
-----

Выполнение hook_perm(). Этим хуком мы ограничим создание нового типа материала определенными пользователями, мы должны определить их права в нем мы запишем обычные строки в массив, а вот сами права будут определены в следующем хуке (hook_access). Это делается так же как и в версии 4.7.

Для node_example:
<?php
function node_example_perm() {
return array('create example node', 'edit own example nodes');
}
?>

node_example использует USER-FRIENDLY-PLURAL для определения прав, но этот пример последует тому совету. (хотя использование USER-FRIENDLY-PLURAL правельнее)

Рассмотрим этот хук ниже:
-----
<?php
function TECHNICAL-NAME_perm() {
return array('create TECHNICAL-NAME', 'edit own TECHNICAL-NAME');
}
?>
-----

В нашем примере это будет выглядеть так:
-----
<?php
function press_release_perm() {
return array('create press_release', 'edit own press_release');
}
?>
-----

Выполнение hook_access(). Модуль материала может используя node_access() определять доступ пользователей к различным операциям с материалом. Этот пример использует лишь простой шаблон.
Для node_example:
<?php
function node_example_access($op, $node) {
global $user;
// $op - операция происходящая с материалом $node
if ($op == 'create') {
// Только пользователи с правами могут создать этот тип материала.
return user_access('create nameofnodetype');
}
// Пользователи создавшие модуль могут его изменить или удалить.
if ($op == 'update' || $op == 'delete') {
if (user_access('edit own nameofnodetype') && ($user->uid == $node->uid)) {
return TRUE;
}
}
}
?>

Последуем совету снова используя TECHNICAL-NAME, а не USER-FRIENDLY-PLURAL

Рассмотрим это ниже
-----
<?php
function TECHNICAL-NAME_access($op, $node) {
global $user;

if ($op == 'create') {
// Только пользователи с правами могут создать этот тип материала.
return user_access('create TECHNICAL-NAME');
}

// Пользователи создавшие модуль могут его изменить или удалить.
if ($op == 'update' || $op == 'delete') {
if (user_access('edit own TECHNICAL-NAME') && ($user->uid == $node->uid)) {
return TRUE;
}
}
}
?>
-----

В нашем примере:
-----
<?php
function press_release_access($op, $node) {
global $user;

if ($op == 'create') {
// Только пользователи с правами могут создать этот тип материала.
return user_access('create press_release');
}

// Пользователи создавшие модуль могут его изменить или удалить.
if ($op == 'update' || $op == 'delete') {
if (user_access('edit own press_release') && ($user->uid == $node->uid)) {
return TRUE;
}
}
}
?>
-----

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

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

Для node_example:
<?php
function node_example_form(&$node) {
$type = node_get_types('type', $node);
// Нам нужно определить элементы форм для материала.
//Для Названия и тела материала.
$form['title'] = array(
'#type' => 'textfield',
'#title' => check_plain($type->title_label),
'#required' => TRUE,
'#default_value' => $node->title,
'#weight' => -5
);
/**
* Нам нужно чтобы тело и элементы фильтра были рядом.
* (We want the body and filter elements to be adjacent. )
* Мы могли бы попытатьься сделать это, установив им веса,
* но другой модуль может добавить элементы между ними.
* ( We could try doing this by setting their weights, but another
* module might add elements to the form with the same weights
* and end up between ours.)
* Записав их в подмассив, мы сможем таскать их вместе.
*/
$form['body_filter']['body'] = array(
'#type' => 'textarea',
'#title' => check_plain($type->body_label),
'#default_value' => $node->body,
'#required' => FALSE
);
$form['body_filter']['filter'] = filter_form($node->format);
/**
* Замечание: в node_example there ненужен дополнительный код
* так как это всеголишь пример.
*/
return $form;
}
?>

Изменения понадобились лишь в названии функции, весь остальной код остлся прежним.
Рассмотрим подробнее:
-----
<?php
function TECHNICAL-NAME_form(&$node) {
(весь остальной код не меняется)
}
?>
-----

В нашем примере стало:
-----
<?php
function press_release_form(&$node) {
(весь остальной код не меняется)
}
?>
-----

Выполнение hook_help().

Это делается также как и в 4.7 , хотя теперь некоторые функции обьеденены с hook_node_info()

hook_form() пример api
<?php
function hook_help($section) {
switch ($section) {
case 'admin/help#block':
return t('Текст помощи');
break;
}
}
?>

Рассмотрим подробнее:
-----
<?php
function TECHNICAL-NAME_help($section) {
switch ($section) {
case 'admin/help#TECHNICAL-NAME':
return t('ADMIN-HELP-TEXT');
break;
}
}
?>
-----

У нас будет так:
-----
<?php
function press_release_help($section) {
switch ($section) {
case 'admin/help#press_release':
return t('Этот модуль создан ....');
break;
}
}
?>
-----

Сохраните файл как "TECHNICAL-NAME.module", (в нашем примере это: "press_release.module") и его уже можно загружать.

Создайте директорию в вашей директории модулей с именем "TECHNICAL-NAME" в нашем случае press_releas и загрузите эти 2- файла (.module и .info) в вашу директорию.

Перейдите на страницу управления модулями аdminister->modules, (admin/build/modules) и включите ваш модуль.
Замечание: Если вы после включения модудя увидели белый экран, то это значит что имеются ошибки в вашем коде. Удалите файлы из вашей новой дирректории и все станет на свои места. Затем вам нужно исправить ошибки и попробывать включить его снова.

Ваш новый модуль будет работать также как и модуль страници, вам только нужно разрешить к нему доступ (administer-> users -> access control) Настоить его (administer->content->content types) и настроить использование в категориях (administer->content->categories) и т.д.
Вот код который нужно изменить.

Для .info

; $Id$
name = USER-FRIENDLY-NAME
description = "MODULE-DESCRIPTION"

Для .module

<?php
/**
* Выполнение hook_node_info().
*/
function TECHNICAL-NAME_node_info() {
return array(
'TECHNICAL-NAME' => array('name' => t('USER-FRIENDLY-NAME'),
'module' => 'TECHNICAL-NAME',
'description' => t("CREATE-CONTENT-DESCRIPTION"), )
);
}
/**
* Выполнение hook_perm().
*/
function TECHNICAL-NAME_perm() {
return array('create TECHNICAL-NAME', 'edit own TECHNICAL-NAME');
}
/**
* Выполнение hook_access().
*/
function TECHNICAL-NAME_access($op, $node) {
global $user;
if ($op == 'create') {
// Только пользователи с правами могут создать этот тип материала.
return user_access('create TECHNICAL-NAME');
}
// Пользователи создавшие модуль могут его изменить или удалить.
if ($op == 'update' || $op == 'delete') {
if (user_access('edit own TECHNICAL-NAME') && ($user->uid == $node->uid)) {
return TRUE;
}
}
}
/**
* Выполнение hook_form().
*/
function TECHNICAL-NAME_form(&$node) {
$type = node_get_types('type', $node);
// Нам нужно определить элементы форм для материала.
// Для Названия и тела материала
$form['title'] = array(
'#type' => 'textfield',
'#title' => check_plain($type->title_label),
'#required' => TRUE,
'#default_value' => $node->title,
'#weight' => -5
);
/**
* Нам нужно чтобы тело и элементы фильтра были рядом.
* (We want the body and filter elements to be adjacent. )
* Мы могли бы попытатьься сделать это, установив им веса,
* но другой модуль может добавить элементы между ними.
* ( We could try doing this by setting their weights, but another
* module might add elements to the form with the same weights
* and end up between ours.)
* Записав их в подмассив, мы сможем таскать их вместе.
*/
$form['body_filter']['body'] = array(
'#type' => 'textarea',
'#title' => check_plain($type->body_label),
'#default_value' => $node->body,
'#required' => FALSE
);
$form['body_filter']['filter'] = filter_form($node->format);
/**
* Замечание: в node_example there ненужен дополнительный код
* так как это всеголишь пример.
*/
return $form;
}
/**
* Выполнение hook_help().
*/
function TECHNICAL-NAME_help($section) {
switch ($section) {
case 'admin/help#TECHNICAL-NAME':
return t('ADMIN-HELP-TEXT');
break;
}
}
?>

Добавляем права на просмотр и удаление.

Для этого нужно внести изменения в 2-е функции (2-а хука), это hook_perm и hook_access. А именно туда нужно добавить права доступа для материала. Еще нам нужно использовать hook_db_rewrite_sql() чтобы иметь возможность управлять показом материала. Но с hook_db_rewrite_sql() есть одна проблемма, некоторые модули с ней конфликтуют и работают неправильно. К счастью это происходит не так часто.
Итак изменения для hook_perm(). В ней все просто. Просто добавляем строки с описанием операции.
<?php
/**
* Implementation of hook_perm().
*/
function TECHNICAL-NAME_perm() {
return array('view TECHNICAL-NAME', 'create TECHNICAL-NAME', 'edit TECHNICAL-NAME', 'edit own TECHNICAL-NAME', 'delete TECHNICAL-NAME');
}
?>

Изменения для hook_perm(). Здесь у нас добавляются команды которые будут передаватся в $op
<?php
/**
* Implementation of hook_access().
*/
function TECHNICAL-NAME_access($op, $node) {
global $user;
switch ($op) {
case 'view':
// только пользователи с правами могут смотреть.
return user_access('view TECHNICAL-NAME');
break;
case 'create':
// только пользователи с правом создания могут создавать.
return user_access('create TECHNICAL-NAME');
break;
case 'update':
if (user_access('edit TECHNICAL-NAME')) {
// пользователи с правами на редактирования могут редактировать
return TRUE;
} else {
// пользователи с правами на редактирования своего материала могут редактировать
if (user_access('edit own TECHNICAL-NAME') && ($user->uid == $node->uid)) {
return TRUE;
}
}
break;
case 'delete':
// только пользователи с правами на удаление могут удалять
return user_access('delete TECHNICAL-NAME');
break;
}
}
?>

И добавляем хук hook_db_rewrite_sql().

<?php
/**
* Implementation of hook_db_rewrite_sql().
*/
function TECHNICAL-NAME_db_rewrite_sql($query, $primary_table, $primary_field, $args) {
switch ($primary_field) {
case 'nid':
// этот запрос работает с объектами материала
$return = array();
if (!user_access('view TECHNICAL-NAME')) {
if ($primary_table != 'n') {
$return['join'] = "LEFT JOIN {node} n ON $primary_table.nid = n.nid";
}
$return['where'] = "n.type <> 'TECHNICAL-NAME'";
return $return;
}
break;
case 'tid':
// этот запрос работает с объектами таксономии
break;
case 'vid':
// этот запрос работает с объектами словаря
break;
}
}
?>

Попрошу побольше критики и своих трактовок перевода

Комментарии

Аватар пользователя astal astal 5 марта 2010 в 23:35

Замечание: Считается хорошей практикой писать следующие комментарии перед каждым хуком
Так же хорошей практикой является написание комментариев на английском языке. Хотя бы не надо переводить "Выполнение hook_help()"....

Аватар пользователя player player 15 января 2008 в 19:29

Quote:
Так же хорошей практикой является написание комментариев на английском языке. Хотя бы не надо переводить "Выполнение hook_help()"....

Это правда? Все комментарии надо оставить на родном языке? Так же менее понятно!? Просто в первый раз слышу если честно. Если это правда, то это мой позор.

Аватар пользователя astal astal 20 января 2008 в 1:36

player wrote:
Это правда? Все комментарии надо оставить на родном языке? Так же менее понятно!? Просто в первый раз слышу если честно. Если это правда, то это мой позор.

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

Аватар пользователя Nikit Nikit 16 января 2008 в 4:44

2axel: а какие идеи с подсветкой? я хотел пастебин запричиндалит, пока оставил в покое. Тоже охота у себя сделать подсветку любого программного кода Smile

Аватар пользователя Separator@drupal.org Separator@drupal.org 16 января 2008 в 12:58

Нет, это просто подсветка кода введенного между тегами <code> или , с указанием языка подсветки, например: &lt;code lang="php"&gt; или &lt;code lang="basic"&gt;, там вообще очень много языков поддерживается, даже есть какие-то специальные подсветки drupal5 и drupal6, правда между этими и php подсветкой я не нашел разницы :) Также для разных языков можно задавать свои теги, например &lt;php&gt; для php, &lt;sql&gt; для sql и т.д., также обычная &lt;?php ... ?&gt; подсветка работает, да там в принципе мого чего есть и настроек куча :)

Аватар пользователя Nikit Nikit 17 января 2008 в 4:35

ясно, сенкс, значит модуль ноды не нада подключат, чисто филтер, будем пристыковываться к буедитор...

Аватар пользователя player player 20 января 2008 в 3:01

'astal' wrote:
Есть некоторые программы которые занимаются обработкой кода для создания списка функций и тому подобного, которые так же учитывают комментарии(если они заданы корректно).
Спасибо, буду знать.