Finishing the Installation Profile

We're down to the last section of the philosopherbios_profile_tasks() section. We have looked at how the submission handler set the default theme as a result of the call to drupal_get_form() below. Let's continue looking at the philosopherbios_profile_tasks() function to see what happens after the form data has been processed if ( task 'philosopherbios_pick_theme') form url) See if the form was processed if (variable_get('philosopherbios_theme', FALSE)) variable_del('philosopherbios_theme')...

Filters Actions and Hooks

In this chapter, we will build a module that will automate the process of sending a newsletter email to all of the users on our system. In Chapter 6, we looked at the email features. In this chapter, we will focus on three other important topics building content filters, creating an action and assigning a trigger to the action, and creating a hook that other modules can implement. Along the way, we will revisit some of the other topics we have already explored, but we will focus our attention...

Preparing the Node for Display with hookview

The hook_view hook is called once a node object has been loaded, but before the node has been displayed. It gives us a chance to prepare that node for display before passing it on to the theme engine. Why do we need a function like this Because Drupal doesn't know what to do with the extra fields of our content type. For example, our data has not been checked and escaped for display in the browser. In addition, the theme system does not have any information regarding how it should display our...

Citing Dependencies in the info File

While the .info files we've been using differ only in name and description, here we will make use of a new field. description Send an email message of site news to all users. core 6.x php 5.1 dependencies trigger The sitenews.info file should look largely familiar. The last line is the only one with a new directive. This last line is used to indicate what dependencies a modules has. That is, it tells Drupal which other modules must be installed before this module can function appropriately....

Delaying Java Script Execution with jQuery

There were two conditions we identified that would prevent our original HTML modification from working. The first, the issue of the browser's JavaScript support, was solved above. The second problem was that the query might execute before the document was completely loaded. A browser can start executing JavaScript as soon as the script is loaded even if the entire page has not yet loaded. We will solve that problem now. Execution of the jQuery code must be delayed until the HTML is completely...

Mapping a Function to a URL

The second PHP function to add to our module will make it possible for our client-side AJAX function to connect to a specific URL and retrieve the JSON data. Adding a new page with a distinct URL is not an uncommon task for Drupal module developers. As is to be expected in such a case, there is an existing hook designed to handle just such a case. This hook is called hook_menu . The basic purpose of hook_menu is to allow module developers the ability to register a particular URI or, more...

Invoking a Custom Hook

In the last section, we took a look at this line of code called in sitenews_send_ action content module_invoke_all 'sitenews' The Drupal module_invoke_all is one of the main hook functions. Its job is to call all the hook implementations for a specific hook. It searches all of the enabled modules looking for functions that match the correct pattern. In our case, we pass the function the string sitenews. This tells module_ invoke_all to look for hooks of the form lt modulename gt _sitenews ,...

Adding a Stylesheet

Earlier, the theme_philquotes_quote function wrapped our quote's text and origin information inside of two lt div gt tags. Each tag has a unique id attribute Using those IDs, we can create a stylesheet that styles those two elements. By convention, a module's main stylesheet should be named lt module_name gt . css where lt module_name gt is the name of the module . As with all other module files, this file belongs inside the module directory drupal sites all modules philquotes, for this example...

Updating and Deleting Database Records

With a general idea of how database queries work, we can move along faster through the remaining database functions. We already have a function to perform inserts. Next, we need to create one to update function biography_update node if node- gt revision biography_insert node db_query UPDATE biography .SET dates ' s', life ' s', works ' s' .WHERE vid d, node- gt dates, node- gt life, node- gt works, node- gt vid Actually, it does just a little more than that. This function might be called for a...

Using PHP to Override Theme Behavior

Sometimes there are cases where we want to perform some additional manipulation on data before passing it on to the templates. Some such manipulation could be done inside the PHPTemplate files. After all, they are really just PHP files. However, that violates the principle of separation we have adopted templates should simply display content, not do any processing. Additionally, some data comes into the theme in a somewhat unpredictable way. The content of the right variable in the page.tpl.php...

Loading a Node with hookload

The hook_load hook provides a chance for extended content types, like the one we are creating, to load additional data. This extra information is combined with the existing node data to return a custom node object. While this sounds confusing, in fact it is fairly straightforward. Any content type is going to have an entry in Drupal's node table. So any time Drupal loads a node, it will first fetch data from the node table. Let's refer to our node object as node. When Drupal first loads node,...

Page Template for Descartes

We have already created a theme, Descartes, and added some custom CSS. Now, we are going to modify just one of the templates provided by the Bluemarine theme. We will change the page template to allow the right sidebar the right region to float. The first thing to do is to copy the Bluemarine page template into our descartes theme directory. cp drupal themes bluemarine page.tpl.php drupal sites all themes descartes Now we have a copy of the template that we can tailor to our needs. According to...

Creating Our First Module

In the last chapter, we looked at the basics of Drupal module development. Now we will dive in and create our first module. Our first module will make use of an existing web service to pull in some XML data, format it, and display it as a block in the site's layout. We will cover the following topics in this chapter Creating the .info and .module files Installing and configuring the module Using important Drupal functions Our first module is going to fetch XML data from Goodreads http...

Building a Content Type

In Chapter 5, we created a simple content type using the Drupal administration interface. In this chapter, we will write a module that defines a more complex content type. Doing so will allow us to explore some new APIs including the database and schema APIs. We'll also revisit some of the hooks and APIs we used in the previous chapters. The module we will create in this chapter will define a biography content type. As we create it, we will perform the following Create a module installation...

Passing PHP Settings to Java Script

All we have left to do in philquotes.module is to provide a configuration parameter to the client-side JavaScript that tells it where to find the newly-registered philquotes.json page. This is done in theme_philquotes_quote function theme_philquotes_quote text, origin full_path drupal_get_path 'module', 'philquotes' .' philquotes.css' 'philquotes' .' philquotes.js' opts array 'absolute' gt TRUE json_url url 'philquotes.json', opts drupal_add_js array json_url gt json_url , 'setting' output ' lt...

Java Script Function to Get JSON Content

Beginning where we left off, we will stub out our new function var Philquotes amp raquo lt a gt A function to fetch quotes from the server, and display in the Philquotes.randQuote function Code will go here. The highlighted lines above show our new additions. On the first line, we define the new namespace. Recall that in JavaScript, a namespace is just an object. Creating a new namespace entails nothing more than creating a new empty object var Several lines later, we define our first function...

An Installation Profile

In this last chapter, we will build an installation profile. We will take the base Drupal distribution, add the modules and theme we created here, and build a custom distribution of Drupal. In this chapter, we will focus on the following topics Setting up a custom distribution Creating an installation profile Selecting the modules to be installed Adding our custom content type from Chapter 4 Configuring a trigger in the installer Adding additional steps to the installer Using the Forms API in...

Defining the Callback Function

Our callback function will have a simple task Verify that the incoming parameter is a User ID, and then hand off control to the form generator. This creates the form necessary to compose an email message. function emailusers_compose userid userid intval userid if userid 0 return t 'User ID must be an integer.' account user_load userid if empty account to account- gt mail sb ' lt p gt ' .t 'Send a message to email.', array ' email' gt to ' lt p gt ' The first five lines check the User ID and...

Modifying the User Profile with hookuser

By default, every registered user can visit the My account page for his or her own account. The URL to access this page is usually something like this http example. com drupal q user 3. The page looks like the following screenshot An administrator with the appropriate permissions can also visit this page. In fact, an administrator can visit the account page for any user on the system. This is the main interface for user administration. Much of the content for the page above is generated by the...

The emailusers Module

The module we will create will provide an administration interface. It will make it easy for administrators to send a user an email message directly from the user's profile. This sort of module might be helpful in cases where administrators occasionally need to communicate with specific users through Drupal. There are already a host of mail-related modules available for Drupal. In fact, the Contact module bundled with Drupal provides similar functionality for user-to-user communication. Ours is...

Adding an Input Format

We want to create a new input format one especially for the site news that we will be sending via email. Also, we want to configure this new input format to use the two filters we have created. While we could add this all by manual configuration through the web interface, it is more convenient to have the module do this for us. This sort of task ought to be done during module installation. To install this, we need to create a sitenews.install file and implement hook_install inside that file. I...

In the biography Module

In the last chapter, we created a biography content type. Here, we will extend that module to implement the hook_sitenews hook. This implementation will return the three newest biographies from the database, formatted for inclusion in our site news email. Implements hook_sitenews in sitenews module. q SELECT nid, created FROM node .WHERE status 1 AND type 'biography' .ORDER BY created DESC results db_query_range q, 0, 3 new_bios array while row db_fetch_object results node node_load row- gt nid...

Why Use Installation Profiles

Sure, that sounds nice, one might say, but why would someone need these Let's take a quick look at two scenarios that illustrate how a custom installation profile can be used. We'll start with the most obvious case. Philosopher Bios has become a popular website remember, this is hypothetical . But requests have started pouring in for sister sites Government Bios, Superhero Bios, Ancient Mesopotamian Bios the public is clamoring for specialty biography sites. While we don't want to run all of...

Creating a JSON Message

The first function will retrieve a random quote from the database, and then encode it into JSON and return it to the client. The function we need to define will not implement a hook. Instead, we will use it later as a callback function from another hook. When it is called, it will return the JSON content directly to the client. Following the module naming convention, we will create a new philquotes_ function philquotes_item . The function needs to do only a few things Set an HTTP header to let...

Creating Filters and an Input Format

Earlier, we created a new content type for the news brief that will be included at the top of each site news message. However, there are a few things that we want to change about the content of a news brief node 1. When we send the content out in an email message, we want to make sure that the HTML is removed from the news brief. In short, we want plain text. Drupal's check_plain function seems a likely candidate for this task, but it escapes HTML instead of removing it. We want to remove the...

Theming Biography Content

We are at the last step of developing our custom content type. The code presented in the last section handled the process of retrieving the information from the database, preparing it, and then handing it off to the theme layer. Here, we just need to implement the theme layer. We took our first look at coding themes in Chapter 3. Since then, we have interacted with the theme layer in subsequent chapters. Once again we will make use of the theme layer. We will provide a default theme for our...

Form Constructor

In Drupal parlance, the function that is used to create a form is called a form constructor. The term constructor has a narrow meaning here, and is not synonymous with the object-oriented programming term. A form constructor creates an elaborate data structure that is used to construct, validate, and manage forms. In key ways it works like a hook a particular naming convention is followed, and all form constructors are expected to return the same type of data structure. Before we look at the...

The Form Submissions Callback

The responsibility of the _submit callback is to determine what is to be done with the data. For example, a _submit callback might store the form data in the database or turn it into a Drupal node. In our case, we want to format it as an email message and then send it. Form submission handler, which functions like a hook. Note that the params form and amp form_state are new in D6. They replace form_id and form_values. function emailusers_compose_form_submit form, amp form_state form_values...

Mail Alter Ubercart

Ontology recapitulates philology --W, V. Quine This message was sent from Philosopher Bios http localhost drupal J. If you believe this message to be a case of abuse, please contact root localhost. In the preceding screenshot, we can see the title of the News Brief at the very top. Underneath that is the content of the News Brief node. The original document looked like this Please enjoy your latest update from report name . Its title was This Week's Update. But the call to check_markup ran the...

Handling Form Results

Drupal automatically handles submitted form data. It first checks the fields against the definition provided by the form constructor to ensure that the data is valid and in the expected form. It then executes a couple of callback functions. The first, which should be the name of the form constructor with _validate appended to the end e.g. emailusers_compose_form_validate is used to perform additional validation on the data. We will forgo that function in this chapter, though we will make use of...

The Content Creation Form

In Chapter 4, we created the Quote content type. Creating new content was easy. The form displayed in Create content Quote was the same form used by Create content Page and Create content Story. Now, however, we have a more complex content type. We need a form that will display all the biography fields Biography of stored as the document's title , Overview stored in the document's body , Dates, Life, and Works. To specify how the form for our new content type ought to look, we will implement...

A CSS Stylesheet

The first step in creating a new stylesheet is answering a question How divergent is this theme's style going to be from the parent style We are basing our Descartes theme on the Bluemarine theme. But how similar will style elements be Will we keep the same fonts The same colors The same paddings, borders, and margins The degree to which the styles differ will determine how we create our stylesheet. Let's take a quick glance at our site with the Bluemarine style Let's take a quick glance at our...

Sending Mail with the Mail API

The Drupal 6 Mail API is used to provide mail-sending services to Drupal modules. In most cases, using the Mail API is a two-step process 1. Implement hook_mail in your module. 2. Elsewhere in your module, use the drupal_mail function to invoke your hook_mail implementation and also do additional formatting and sending. In the previous section, we briefly glanced at the drupal_mail function. Here, we will start by looking at the function in more detail. Inside emailusers_compose_ form_submit ,...

Hooks for Getting Data

We have only three more functions to go. So far, we've created an installer, implemented hooks to create the database tables, implemented access controls, created a form, and developed functions for maintaining our custom tables. In this section, we will look at functions used for accessing our content. And once again, we will be moving back toward the familiar. We will start by implementing hook_load , then we will take a look at hook_view and hook_theme ....

The Second Filter Remove All Tags

The second filter is similar to the first in structure, but it is considerably shorter. This filter will remove all tags from the given text return t 'Removes all tags HTML or XML elements .' case 'prepare' return text case 'process' As we saw with the last filter, description returns a human-friendly message indicating what the module does. Since there is no preprocessing work to be done, prepare returns text unaltered. The processing operation does the filtering. PHP has a built-in function...

Database Hooks

When we created the installer, we used the Schema API to define how our new biography content type table looks. However, the Schema API is abstract enough that we wrote no SQL, and we didn't directly call any database functions. Now we will create four functions that deal with the database at a low level, and we will write SQL statements to perform database operations. We need to define functions to perform the following Insert a new biography entry into the biography table. Remove an unwanted...

Access Controls

In the last chapter, we took a quick look at access controls as a way to allow some users the ability to send email messages, while denying others such capabilities. Here, we will be doing something similar. We will create one hook to register three new permissions, and then we will create a second hook that will test a user to see if she or he can access a biography. First, let's look at hook_perm , which is responsible for registering new permissions function biography_perm return array...

Database Inserts with hookinsert

When a user submits a new biography as defined by biography_form , Drupal will go through a checking and cleaning procedure, and then it will attempt to store the data. Part of the storage procedure involves calling the appropriate hook_insert implementation for the module specified by hook_node_info . Our biography_ node_info set 'module' gt 'biography', so Drupal will look for the biography_ insert function to handle the task of inserting biography data into custom tables. biography_insert is...

The Schema API Defining Database Structures

The next task in our module installation process is the defining of our new table. In previous versions of Drupal, a module author wrote SQL DDL Data Definition Language to define any new database structures. However, this caused portability problems. Since SQL DDL is not particularly transferable from one DBMS DataBase Management System to another, modules that worked on, say, MySQL could not then be installed on PostgreSQL. In the past, Drupal has been tightly integrated with MySQL, with...

Correlating the New Table with Nodes

The install script defined a new table. But how does Drupal know that this table is to be treated as part of a content type In fact, how is Drupal to know that we are defining a content type at all In the first chapter, we discussed nodes. From the developer's point of view, each piece of content is backed by a node and perhaps some extensions to that node. What extensions are used depends on the content type of that piece of content. The table we just created represents extensions to the node...