Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Apress.Pro.Drupal.7.Development.3rd.Edition.Dec.2010.pdf
Скачиваний:
73
Добавлен:
14.03.2016
Размер:
12.64 Mб
Скачать

CHAPTER 2 WRITING A MODULE

You should see now why we call it hook_menu() or the menu hook.

Tip Drupal’s hooks allow modification of almost any aspect of the software. A complete list of supported hooks and their uses can be found at the Drupal API documentation site (http://api.drupal.org/api/group/ hooks/7).

Adding Module-Specific Settings

Drupal has various node types (called content types in the user interface), such as articles and basic pages. We will want to restrict the use of annotations to only some node types. To do that, I need to create a page where we can tell our module which content types we want to annotate. On that page, we will show a set of check boxes, one for each content type that exists. This will let the end user decide which content types get annotations by checking or unchecking the check boxes (see Figure 2-1). Such a page is an administrative page, and the code that composes it need only be loaded and parsed when needed. Therefore, we will put the code into a separate file, not in our annotate.module file, which will be loaded and run with each web request. Since we told Drupal to look for our settings form in the annotate.admin.inc file, I’ll create sites/all/modules /annotate/annotate.admin.inc, and add the following code to it:

<?php

/**

*@file

*Administration page callbacks for the annotate module.

*/

/**

*Form builder. Configure annotations.

*@ingroup forms

*@see system_settings_form().

*/

function annotate_admin_settings() {

//Get an array of node types with internal names as keys and

//"friendly names" as values. E.g.,

//array('page' => ’Basic Page, 'article' => 'Articles')

$types = node_type_get_types(); foreach($types as $node_type) {

$options[$node_type->type] = $node_type->name;

}

17

CHAPTER 2 WRITING A MODULE

$form['annotate_node_types'] = array( '#type' => 'checkboxes',

'#title' => t('Users may annotate these content types'), '#options' => $options,

'#default_value' => variable_get('annotate_node_types', array('page')), '#description' => t('A text field will be available on these content types to

make user-specific notes.'),

);

$form['#submit'][] = 'annotate_admin_settings_submit'; return system_settings_form($form);

}

Forms in Drupal are represented as a nested tree structure—that is, an array of arrays. This structure describes to Drupal’s form rendering engine how the form is to be represented. For readability, we place each element of the array on its own line. Each form property is denoted with a pound sign (#) and acts as an array key. We start by declaring the type of form element to be checkboxes, which means that multiple check boxes will be built using a keyed array. We’ve already got that keyed array in the $options variable.

We set the options to the output of the function node_type_get_types(), which returns an array of objects. The output would look something like this:

[article] => stdClass Object ( [type] => article [name] => Article [base] => node_content

[description] => Use articles for time-sensitive content like news, press releases or blog posts.

[help] => [has_title] => 1

[title_label] => Title [has_body] => 1 [body_label] => Body [custom] => 1 [modified] => 1 [locked] => 0 [orig_type] => article

)

The keys of the object array are Drupal’s internal names for the node types, with the friendly names (those that will be shown to the user) contained in the name attribute of the object.

Drupal’s form API requires that #options be set as a key => value paired array so the foreach loop uses the type attribute to create the key and the name attribute to create the value portions of a new array I named $options. Using the values in the $options array in our web form, Drupal will generate check boxes for the Basic page and article node types, as well as any other content types you have on your site.

We give the form element a title by defining the value of the #title property.

18

CHAPTER 2 WRITING A MODULE

Note Any returned text that will be displayed to the user (such as the #title and #description properties of our form field) is inside a t() function, a function provided by Drupal to facilitate string translation. By running all text through a string translation function, localization of your module for a different language will be much easier. We did not do this for our menu item because menu items are translated automatically.

The next directive, #default_value, will be the default value for this form element. Because checkboxes is a multiple form element (i.e., there is more than one check box) the value for

#default_value will be an array.

The value of #default_value is worth discussing:

variable_get('annotate_node_types', array('page'))

Drupal allows programmers to store and retrieve any value using a special pair of functions: variable_get() and variable_set(). The values are stored to the variables database table and are available anytime while processing a request. Because these variables are retrieved from the database during every request, it’s not a good idea to store huge amounts of data this way. But it’s a very convenient system for storing values like module configuration settings. Note that what we pass to variable_get() is a key describing our value (so we can get it back) and a default value. In this case, the default value is an array of which node types should allow annotation. We’re going to allow annotation of Basic page content types by default.

Tip When using system_settings_form(), the name of the form element (in this case, annotate_node_types) must match the name of the key used in variable_get().

We provide a description to tell the site administrator a bit about the information that should go into the field. I’ll cover forms in detail in Chapter 11.

Next I’ll add code to handle adding and removing the annotation field to content types. If a site administrator checks a content type, I’ll add the annotation field to that content type. If a site administrator decides to remove the annotation field from a content type, I’ll remove the field. I’ll use Drupal’s Field API to define the field and associate the field with a content type. The Field API handles all of the activities associated with setting up a field, including creating a table in the Drupal database to store the values submitted by content authors, creating the form element that will be used to collect the information entered by the author, and associating a field with a content type and having that field displayed on the node edit form and when the node is displayed on a page. I will cover the Field API in detail in Chapter 8.

The first thing that I will do is to create a form submission routine that will be called when the site administrator submits the form. In this routine, the module will check to see whether the check box for a content type is checked or unchecked. If it is unchecked, I’ll verify that the content type does not have the annotation field associated with it. If it does, that indicates that the site administrator wants the field removed from that content type, and removes the existing annotations that are stored in the database. If the check box is checked, the module checks to see whether the field exists on that content type, and if not, the module adds the annotation field to that content type.

19

Download from Wow! eBook <www.wowebook.com>

CHAPTER 2 WRITING A MODULE

/**

* Process annotation settings submission. */

function annotate_admin_settings_submit($form, $form_state) {

// Loop through each of the content type checkboxes shown on the form. foreach ($form_state['values']['annotate_node_types'] as $key => $value) {

//If the check box for a content type is unchecked, look to see whether

//this content type has the annotation field attached to it using the

//field_info_instance function. If it does then we need to remove the

//annotation field as the administrator has unchecked the box.

if (!$value) {

$instance = field_info_instance('node', 'annotation', $key); if (!empty($instance)) {

field_delete_instance($instance);

watchdog("Annotation", 'Deleted annotation field from content type: %key', array('%key' => $key));

}

}else {

//If the check box for a content type is checked, look to see whether

//the field is associated with that content type. If not then add the

//annotation field to the content type.

$instance = field_info_instance('node', 'annotation', $key); if (empty($instance)) {

$instance = array( 'field_name' => 'annotation', 'entity_type' => 'node', 'bundle' => $key,

'label' => t('Annotation'),

'widget_type' => 'text_textarea_with_summary', 'settings' => array('display_summary' => TRUE), 'display' => array(

'default' => array(

'type' => 'text_default',

),

'teaser' => array(

'type' => 'text_summary_or_trimmed',

),

),

);

$instance = field_create_instance($instance);

watchdog('Annotation', 'Added annotation field to content type: %key', array('%key' => $key));

}

}

} // End foreach loop.

}

20

CHAPTER 2 WRITING A MODULE

The next step is to create the .install file for our module. The install file contains one or more functions that are called when the module is installed or uninstalled. In the case of our module, if it is being installed, we want to create the annotation field so it can be assigned to content types by site administrators. If the module is being uninstalled, we want to remove the annotation field from all the content types and delete the field and its contents from the Drupal database. To do this, create a new file in your annotate module directory named annotate.install.

The first function we will call is hook_install(). We’ll name the function annotate_install()— following the standard Drupal convention of naming hook functions by replacing the word “hook” with the name of the module. In the hook_install function, I’ll check to see if the field exists using the Field API, and if it doesn’t, I’ll create the annotation field.

<?php

/**

* Implements hook_install() */

function annotate_install() {

//Check to see if annotation field exists. $field = field_info_field('annotation');

//if the annotation field does not exist then create it if (empty($field)) {

$field = array(

'field_name' => 'annotation', 'type' => 'text_with_summary', 'entity_types' => array('node'), 'translatable' => TRUE,

);

$field = field_create_field($field);

}

}

The next step is to create the uninstall function using hook_uninstall. I’ll create a function named annotate_uninstall and will use the watchdog function to log a message that tells the site administrator that the module was uninstalled. I will then use the node_get_types() API function to gather a list of all content types that exist on the site and will loop through the list of types, looking to see whether the annotation field exists on that content type. If so, I’ll remove it. Finally I’ll delete the annotation field itself.

/**

* Implements hook_uninstall() */

function annotate_uninstall() {

watchdog("Annotate Module", "Uninstalling module and deleting fields");

$types = node_type_get_types();

21

CHAPTER 2 WRITING A MODULE

foreach($types as $type) { annotate_delete_annotation($type);

}

$field = field_info_field('annotation');

if ($field) { field_delete_field('annotation');

}

}

function annotate_delete_annotation($type) {

$instance = field_info_instance('node', 'annotation', $type->type);

if ($instance) { field_delete_instance($instance);

}

}

The last step in the process is to update the .module file to include a check to see whether the person viewing a node is the author of that node. If the person is not the author, then we want to hide the annotation from that user. I’ll take a simple approach of using hook_node_load(), the hook that is called when a node is being loaded. In the hook_node_load() function, I’ll check to see whether the person viewing the node is the author. If the user is not the author, I’ll hide the annotation by unsetting it.

/**

* Implements hook_node_load() */

function annotate_node_load($nodes, $types) {

global $user;

//Check to see if the person viewing the node is the author. If not then

//hide the annotation.

foreach ($nodes as $node) {

if ($user->uid != $node->uid) { unset($node->annotation);

}

}

}

Save the files you have created (.info, .install, .admin.inc, .module), and click the Modules link in the administrators menu at the top of the page. Your module should be listed in a group titled Pro Drupal Development (if not, double-check the syntax in your annotate.info and annotate.module files; make sure they are in the sites/all/modules/custom directory). Go ahead and enable your new module.

Now that the annotate module is enabled, navigating to admin/config/annotate/settings should show us the configuration form for annotate.module (see Figure 2-1).

22

CHAPTER 2 WRITING A MODULE

Figure 2-1. The configuration form for annotate.module is generated for us.

In only a few lines of code, we now have a functional configuration form for our module that will automatically save and remember our settings! This gives you a feeling of the power you can leverage with Drupal.

Let’s test the process by first enabling annotations for all content types. Check all of the boxes on the configuration settings page and click the “Save configuration” button. Next create a new basic page node, and scroll down until you see the Annotation field (see Figure 2-2).

23

CHAPTER 2 WRITING A MODULE

Figure 2-2. The annotation form as it appears on a Drupal web page

Create a new node by entering values in the title, body, and annotation field. When you’re finished, click the save button, and you should see results similar to Figure 2-3.

Figure 2-3. A node that has an annotation

24

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]