Creating a module for Drupal 7. Part 1

Foreword


Most recently, I was given the task of learning to write modules for Drupal 7. I started scouring in search of various articles and manuals, and I realized that there are quite a few of them, and the information is very minimal. The official documentation also did not give me exhaustive information. With grief in half, I collected some information from several sources and decided to share it with you.

I will not tell what Drupal is, its structure. The article is intended for those who minimally know the principle of hooks and Drupal mapping. All this can be read on the official Drupal API website .

Start of module development


I will show how to create a module that allows you to add RSS feeds and display their content on a separate page.

First you need to select the “short name” of the module. It will be used in all files and function names of the module. It should begin only with a letter and contain only lowercase characters and the sign "_". I called my module “rss_feeds”.

Create a folder: sites / all / modules / rss_feeds (all new modules that you want to add must be in this folder). In it, we create the rss_feeds.info file , which contains META information about our module:

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 - the name of the module that will be displayed in the admin panel;
  • description - a description of the module that tells the administrator what this module does;
  • package - indicates the category in which the module will be displayed on the modules page;
  • core - the version of Drupal for which it was developed;
  • version - version of our module;
  • configure - the path along which the module settings page will be available;
  • files [] - an array of files connected by the module;


There are some more fields that you can read about here .

Our module will use a database in which it will store data about RSS (its name, and URL). Create the rss_feeds.install file :

 array(
			'id'         => array('type' => 'serial',  'size'=>'normal',  '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')
	);
	return $schema;
}


In the schema () hook, we create the table we need. The uninstall () hook clears the cache, deletes the table, and rebuilds the menu when the module is removed (it is customary not to set the "?>" Tags to close in Drupal).

Creating a .module File


Next, we create the module file directly - rss_feeds.module :

 t('RSS Feeds'),
		'cache' => DRUPAL_CACHE_PER_ROLE, // по умолчанию
	);
	return $blocks;
}


In this hook, we describe the blocks used. With us it will be only one and called 'rss_feeds' . 'info' is the brief information about the block, and 'cache' is the cache rule. See hook_block_info () for more details .

Next we describe 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;
}


In items we set the URL, its title (title), description (description), processing function (page callback), passed arguments (page arguments), access parameters (access arguments), type (type) and "weight" (weight). I would like to highlight page callback - drupal_get_from . This function takes a form as a parameter and displays it. In "page arguments => array (1)" , as an argument we pass the 1st element of the URL (the count goes from 0).

More on hook_menu () .

Next, we will describe the form through which we will add and edit our RSS feeds:

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;
}


As you can see, if we pass the parameter $ rss to the form, the form will understand whether we are adding a new feed, or editing an existing one. The t () (translate) function allows you to localize the module (I will talk about this a bit later). More on hook_form () .

Next, you need to describe hook_form_validate () , which will process the data entered into the form:

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!'));
	}
}


First we get the data from $ form_state , and then we process it. If something is wrong, the standard form_set_error () is thrown , in which we specify the form field name and message.

When the form passes validation, hook_form_submit () is called :

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');
}


I think everything is clear here. drupal_write_record () writes to the database, drupal_set_message () shows the system message, drupal_goto () throws to the given URL.

In order for the form to accept not just the tape ID ( $ rss ), but its data, you need to define 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;
}


Now, as $ rss , not a number will be transferred to the form, but an object with data.

Next, we need to implement the page output function, on which we can edit our table of RSS feeds - 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,
	));
}


Function l () (link) - creates a link. But the main function is theme () . You will meet her separately, because it is very extensive and has many useful parameters.

Below we create the function to delete records 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');
}


No comments.

For convenience, in the future I added a function that will return data, depending on the parameter (block or page). Here are its contents:

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();
}


If we specify 'block' as the parameter , then we will only get 5 records.

Next, we implement the hook of the block output - 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;
}


This block will be available in the administration panel.

Now we’ll write a function to display the page on which there will be a list of feeds, when clicked on, the content will be displayed (in a new tab):

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;
	}
}


... and the content display page:

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;
	}
}


You may have noticed, but the pages will not be displayed. I specified the 'rssfeeds_block' and 'rssfeeds_content' parameters in the theme () function . These are the templates I created. Let's make hook_theme () to initialize them:

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',
		)
	);
}


In the field 'template' indicate the name of the template.

Now create the template files themselves. Namely: rssfeeds-block.tpl.php , rssfeeds-page.tpl.php and rssfeeds-content.tpl.php .

The contents of the first file:



... second:



... and the third, respectively:

channel->item as $item): ?>
	title; ?>

description; ?>

pubDate; ?>




Now our module will function. However, you probably noticed - classes are defined for the elements. Now attach the CSS styles file to our module. To do this, add this line to the .info file:

stylesheets [all] [] = main.css

And create the main.css file :

.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;
}


Conclusion


That's actually the finished module. If you liked this article, I will write how to add js files to modules and make localization files. I hope she helps someone.

Thanks for attention!

PS



Wrote the second part .

Also popular now: