Предисловие
Совсем недавно получил задание научиться писать модули под Drupal 7. Начал рыскать в поисках различных статей и мануалов, и понял, что их довольно мало, и информация совсем минимальная. Официальная документация так же не дала мне исчерпывающей информации. С горем пополам собрал некоторую информацию с нескольких источников и решил поделиться ей с Вами.
Не буду рассказывать, что такое Drupal, его структуру. Статья рассчитана на тех, кто минимально знает принцип работы хуков и отображения Drupal. Все это можно прочитать на официальном сайте API Drupal.
Начало разработки модуля
Я покажу как создать модуль, позволяющий добавлять RSS ленты и выводить их контент на отдельной странице.
Для начала необходимо выбрать «краткое имя» модуля. Оно будет использоваться во всех файлах и именах функций модуля. Оно должно начинаться только с буквы и содержать только символы нижнего регистра и знак "_". Я назвал свой модуль «rss_feeds».
Создаем папку: sites/all/modules/rss_feeds (все новые модули, которые Вы хотите добавить, должны находиться в этой папке). В ней создаем файл rss_feeds.info, который содержит META-информацию о нашем модуле:
name = RSS Feeds
description = Makes a compact page to navigate on RSS feeds.
package = «RSS»
core = 7.x
version = «7.x-1.0»
configure = admin/config/content/rss_feeds
files[]= rss_feeds.module
- name — имя модуля, которое будет отображаться в админке;
- description — описание модуля, которое подскажет администратору, что делает этот модуль;
- package — указывает категорию, в которой будет отображен модуль на странице модулей;
- core — версия Drupal, под которую он разрабатывался;
- version — версия нашего модуля;
- configure — путь, по которому будет доступна страница настроек модуля;
- files[] — массив подключаемых модулем файлов;
Есть еще некоторые поля, о которых вы можете прочитать тут.
Наш модуль будет использовать БД, в которой будет хранить данные о RSS (его имя, и URL). Создаем файл rss_feeds.install:
<?php function rss_feeds_uninstall() { cache_clear_all('rss_feeds', 'cache', TRUE); drupal_uninstall_schema('rssfeeds'); menu_rebuild(); } function rss_feeds_schema() { $schema['rssfeeds'] = array( 'fields' => array( 'id' => array('type' => 'serial', 'not null' => TRUE), 'name' => array('type' => 'varchar', 'length' => 255, 'not null' => TRUE), 'url' => array('type' => 'varchar', 'length' => 255, 'not null' => TRUE), 'created_at' => array('type' => 'int', 'not null' => TRUE), 'updated_at' => array('type' => 'int', 'not null' => TRUE), ), 'primary key' => array('id'), ); }
В хуке schema() мы создаем нужную нам таблицу. Хук uninstall() чистит кэш, удаляет таблицу и перестраивает меню при удалении модуля (закрывающих тег "?>" в Drupal принято не ставить).
Создание файла .module
Далее создаем непосредственно файл модуля — rss_feeds.module:
<?php function rss_feeds_block_info() { $blocks['rss_feeds'] = array( 'info' => t('RSS Feeds'), 'cache' => DRUPAL_CACHE_PER_ROLE, // по умолчанию ); return $blocks; }
В этом хуке мы описываем используемые блоки. У нас он будет только один и называться ‘rss_feeds’. ‘info’ — это краткая информация о блоке, а ‘cache’ — правило кеширования. Подробнее см hook_block_info().
Далее описываем hook_menu():
function rss_feeds_menu() { $items = array(); $items['admin/config/content/rss_feeds'] = array( 'title' => 'RSS Feeds', 'description' => 'Configure the RSS feeds list.', 'page callback' => 'rss_list', 'access arguments' => array('administer site configuration'), ); $items['admin/config/content/rss_feeds/list'] = array( 'title' => 'RSS feeds list', 'type' => MENU_DEFAULT_LOCAL_TASK, 'weight' => 1, ); // rss add form $items['admin/config/content/rss_feeds/add'] = array( 'title' => 'Add rss', 'page callback' => 'drupal_get_form', 'page arguments' => array('rss_feeds_form'), 'access arguments' => array('administer site configuration'), 'type' => MENU_LOCAL_TASK, 'weight' => 2, ); // rss edit form $items['admin/config/content/rss_feeds/%rss/edit'] = array( 'title' => 'Edit RSS', 'page callback' => 'drupal_get_form', 'page arguments' => array('rss_feeds_form', 4), 'access arguments' => array('administer site configuration'), 'type' => MENU_CALLBACK, ); // rss delete $items['admin/config/content/rss_feeds/%rss/delete'] = array( 'title' => 'Delete RSS', 'page callback' => 'rss_feeds_delete', 'page arguments' => array(4), 'access arguments' => array('administer site configuration'), 'type' => MENU_CALLBACK, ); $items['rss_feeds'] = array( 'title' => 'RSS feeds', 'page callback' => '_rss_feeds_page', 'access arguments' => array('access content'), ); $items['rss_feeds/%rss/items'] = array( 'title' => 'RSS feed content', 'page callback' => 'rss_content', 'page arguments' => array(1), 'access callback' => TRUE, 'access arguments' => array('access content'), 'type' => MENU_CALLBACK, ); return $items; }
В items мы задаем URL, его заголовок (title), описание (description), функция обработки (page callback), передаваемые аргументы (page arguments), параметры доступа (access arguments), тип (type) и «вес» (weight). Хотелось бы выделить page callback — drupal_get_from. Эта функция принимает в качестве параметра форму и выводит ее. В «page arguments => array(1)», в качестве аргумента мы передаем 1-ый элемент URL (отсчет идет от 0).
Подробнее о hook_menu().
Далее мы опишем форму, через которую мы будем добавлять и редактировать наши RSS-ленты:
function rss_feeds_form($form, &$form_state, $rss = null) { $form['name'] = array( '#title' => t('RSS feed name.'), '#description' => t('Insert RSS shortcut name'), '#type' => 'textfield', '#default_value' => $rss ? $rss['name'] : '', '#required' => true, ); $form['url'] = array( '#title' => t('RSS feed url.'), '#description' => t('Insert RSS url'), '#type' => 'textfield', '#default_value' => $rss ? $rss['url'] : '', '#required' => true, ); $form['submit'] = array( '#type' => 'submit', '#value' => $rss ? t('Save') : t('Add'), ); if ($rss) { $form['id'] = array( '#type' => 'value', '#value' => $rss['id'], ); } return $form; }
Как Вы видите, если мы будем передавать в форму параметр $rss, форма будет понимать, добавляем мы новую ленту, или редактируем существующую. Функция t() (translate), позволяет локализировать модуль (об этом я расскажу чуть позже). Подробнее о hook_form().
Далее необходимо описать hook_form_validate(), который будет обрабатывать данные, введенные в форму:
function rss_feeds_form_validate($form, &$form_state) { $url = $form_state['values']['url']; if (fopen($url, "r")) { libxml_use_internal_errors(true); $rss_feed = simplexml_load_file($url); if (empty($rss_feed)) { form_set_error('url', t('URL is invalid!')); } } else { form_set_error('url', t('URL is invalid!')); } }
Сначала мы получаем данные из $form_state, а потом их обрабатываем. Если что-то не так — выкидывается стандартный form_set_error(), в котором мы указываем имя поля формы и сообщение.
Когда форма проходит валидацию, вызывается hook_form_submit():
function rss_feeds_form_submit($form, &$form_state) { $rss = array( 'name' => $form_state['values']['name'], 'url' => $form_state['values']['url'], 'created_at' => time(), 'updated_at' => time(), ); // save edit data if (isset($form_state['values']['id'])) { $rss['id'] = $form_state['values']['id']; drupal_write_record('rssfeeds', $rss, 'id'); drupal_set_message(t('RSS Feed saved!')); } // add new data else { drupal_write_record('rssfeeds', $rss); drupal_set_message(t('RSS Feed added!')); } drupal_goto('admin/config/content/rss_feeds'); }
Думаю тут все понятно. drupal_write_record() делает запись в БД, drupal_set_message() показывает системное сообщение, drupal_goto() перекидывает на заданный URL.
Для того чтобы форма принимала не просто ID ленты ($rss), а ее данные, нужно определить hool_load():
function rss_load($id) { $rss = db_select('rssfeeds', 'n') ->fields('n', array('id', 'name', 'url', 'created_at', 'updated_at')) ->condition('n.id', $id) ->execute()->fetchAssoc(); return $rss; }
Теперь в качестве $rss в форму будет передаваться не число, а объект с данными.
Далее следует реализовать функцию вывода страницы, на которой мы сможем проводить редактирование нашей таблицы RSS лент — rss_list():
function rss_list() { $header = array( array('data' => t('Name')), array('data' => t('URL')), array('data' => t('Actions')) ); $rss = db_select('rssfeeds', 'n') ->fields('n', array('id', 'name', 'url')) ->execute()->fetchAll(); $row = array(); if ($rss) { foreach ($rss as $rss_feed) { $actions = array( l(t('edit'), 'admin/config/content/rss_feeds/' . $rss_feed->id . '/edit'), l(t('delete'), 'admin/config/content/rss_feeds/' . $rss_feed->id . '/delete'), ); $row [] = array( array('data' => $rss_feed->name), array('data' => $rss_feed->url), array('data' => implode(' | ', $actions)), ); } } return theme('table', array( 'header' => $header, 'rows' => $row, )); }
Функция l() (link) — создает ссылку. Но главная функция — theme(). С ней Вы познакомитесь отдельно, т.к. она очень обширная и имеет множество полезных параметров.
Ниже создадим функцию удаления записей rss_feeds_delete():
function rss_feeds_delete($rss) { $rss_deleted = db_delete('rssfeeds') ->condition('id', $rss['id']) ->execute(); drupal_set_message(t('RSS Feed deleted!')); drupal_goto('admin/config/content/rss_feeds'); }
Без комментариев.
Для удобства в дальнейшем я добавил функцию, которая будет возвращать данные, в зависимости от параметра (блок или страница). Вот ее содержимое:
function rss_contents($display) { $query = db_select('rssfeeds', 'n') ->fields('n', array('id', 'name', 'url')) ->orderBy('name', 'DESC'); if ($display == 'block') { $query->range(0, 5); } return $query->execute(); }
Если в качестве параметра мы укажем ‘block’, то нам выведет лишь 5 записей.
Далее реализуем хук вывода блока — hook_block_view():
function rss_feeds_block_view($delta = '') { $blocks = array(); switch ($delta) { case 'rss_feeds': $select = db_select('rssfeeds', 'tc'); $select->addField('tc', 'name'); $select->addField('tc', 'url'); $entries = $select->execute()->fetchAll(); $blocks['subject'] = t('List of URLs'); $blocks['content'] = theme('rssfeeds_block', array('urls' => $entries)); } return $blocks; }
Данный блок будет доступен в панели администрирования.
Теперь напишем функцию отображения страницы, на которой будет список лент, при нажатии на которые, будет выводиться контент (в новой вкладке):
function _rss_feeds_page() { drupal_set_title(t('RSS Feeds')); $result = rss_contents('page')->fetchAll(); if (!$result) { $page_array['rss_feeds_arguments'] = array( '#title' => t('RSS Feeds page'), '#markup' => t('No RSS feeds available'), ); return $page_array; } else { $page_array = theme('rssfeeds_page', array('urls' => $result)); return $page_array; } }
… и страницу отображения контента:
function rss_content($rss) { $url = $rss['url']; libxml_use_internal_errors(true); $rss_feed = simplexml_load_file($url); if (!empty($rss_feed)) { drupal_set_title($rss_feed->channel->title); $page_array = theme('rssfeeds_content', array('items' => $rss_feed)); return $page_array; } else { $page_array['rss_feeds_arguments'] = array( '#title' => t('All posts from the last week'), '#markup' => t('No posts available.'), ); return $page_array; } }
Возможно Вы заметили, но страницы не будут выводиться. Я указал в функции theme() параметр ‘rssfeeds_block’ и ‘rssfeeds_content’. Это созданные мной шаблоны. Сделаем hook_theme() для их инициализации:
function rss_feeds_theme() { return array( 'rssfeeds_block' => array( 'variables' => array( 'urls' => NULL ), 'template' => 'rssfeeds-block', ), 'rssfeeds_page' => array( 'variables' => array( 'urls' => NULL ), 'template' => 'rssfeeds-page', ), 'rssfeeds_content' => array( 'variables' => array( 'items' => NULL ), 'template' => 'rssfeeds-content', ) ); }
В поле ‘template’ указываем название шаблона.
Теперь создаем сами файлы шаблонов. А именно: rssfeeds-block.tpl.php, rssfeeds-page.tpl.php и rssfeeds-content.tpl.php.
Содержимое первого файла:
<div id="rssfeeds-pager"> <?php foreach ($urls as $url): ?> <span> <a target="_blank" href="http://<?php echo $url->url; ?>"><?php echo $url->url; ?></a><br> </span> <?php endforeach; ?> </div>
… второго:
<div id="rssfeeds-pager"> <?php foreach ($urls as $url): ?> <span> <a target="_blank" href="rss_feeds/<?php echo $url->id; ?>/items"><?php echo $url->name; ?></a><br> </span> <?php endforeach; ?> </div>
… и третьего соответственно:
<?php foreach ($items->channel->item as $item): ?> <span class="title"><a href="<?php echo $item->link; ?>"><?php echo $item->title; ?></a></span><br><br> <?php echo $item->description; ?><br><br><?php echo $item->pubDate; ?> <hr><br><br> <?php endforeach; ?>
Теперь наш модуль будет функционировать. Однако, Вы наверняка заметили — к элементам определены классы. Теперь прикрепим к нашему модулю файл CSS стилей. Для этого добавим в файл .info данную строку:
stylesheets[all][] = main.css
И создаем файл main.css:
.title { font-size: 16px; text-shadow: 0px 0px 1px black; font-weight: bold; } .title a { text-decoration: none; color: #B81007; } .title a:hover { text-decoration: underline; }
Заключение
Вот собственно и получился готовый модуль. Если Вам понравилась данная статья, я напишу как добавлять js-файлы к модулям и делать файлы-локализации. Надеюсь кому-нибудь она поможет.
Спасибо за внимание!
ссылка на оригинал статьи http://habrahabr.ru/post/200340/
Добавить комментарий