Создание модуля под Drupal 7

от автора

Предисловие

Совсем недавно получил задание научиться писать модули под 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 callbackdrupal_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/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *