Creating a Node Module

Traditionally, when you wanted to create a new content type in Drupal, you would write a node module that takes responsibility for providing the new and interesting things your content type needs. We say traditionally, because recent advents within the Drupal framework allow you to create content types within the administrative interface and extend their functionality with contributed modules rather than writing a node module from scratch. We'll cover both solutions within this chapter. Let's...

Customizing the Node Form for Our Node Type

So far you've got the metadata defined for your new node type, and the menu callback and access permissions defined. Next you need to build the node form so that users can enter jokes. You do that by implementing hook_form() ** (we use it for labeling title and body fields). We defined this in joke_node_info(). type node_get_types('type', node) form 'title' array( ' type' > 'textfield', ' title' > check_plain( type-> title_label), ' required' > TRUE, ' default_value' > node-> title,...

How the Indexer Works

The indexer has a preprocessing mode where text is filtered through a set of rules to assign scores. Such rules include dealing with acronyms, URLs, and numerical data. During the preprocessing phase, other modules have a chance to add logic to this process in order to perform their own data manipulations. This comes in handy during language-specific tweaking, as shown here using the contributed Porter-Stemmer module resume > resume (accent removal) Another such language preprocessing example...

Listing Your Module on the Administration Page

Drupal's administration page presents the various site configuration options to the site administrator. You want your module to have a place on this configuration page, so that the site administrator can adjust the settings for your module. Let's add some more configuration options to the node annotation module we built in the previous chapter. We need to provide a link on the administration page so that the site administrator can get to the screen where our settings can be changed. We put the...

D

D option, checkout command, 326 d option, cvs command, 326 d option, update command, 329 d placeholder, 52 dynamic queries, 312 storing data in database table, 20 data column, cache table, 244 data component, user object, 65 data entry form, adding, 16-21 data parameter, cache_set function, 251 data types handling security of user input, 302-303 HTML text, 303 plain text, 302 rich text, 303 URL, 303 database abstraction layer, 49-51 allowing MySQL PostgreSQL database bottlenecks, 344-348...

Connecting to Multiple Databases Within Drupal

While the database abstraction layer makes remembering function names easier, it also adds built-in security to queries. Sometimes we need to connect to third-party or legacy databases, and it would be great to use Drupal's database API for this need as well and get the security benefits. The good news is, we can In the settings.php file, db_url can be either a string as it usually is or an array composed of multiple database connection strings. Here's the default syntax, specifying a single...

Access Control

In our examples so far, we've simply set the access key of the menu item to TRUE, meaning that anyone can access our menu. Usually menu access is controlled by defining permissions inside the module using hook_perm and testing those permissions using user_access . Let's define a permission called receive greeting, if a user does not have a role that has been granted this permission, the user will receive an Access denied message if he or she tries to go to http example. Implementation of...

Getting pot Files for Drupal

The definitive .pot files for Drupal can be downloaded from http drupal.org project drupal- pot. After downloading and extracting the .tar.gz file for the branch of Drupal you are interested in, you should have a directory full of .pot files corresponding to Drupal files. For example, aggregator-module.pot contains the translatable strings from Drupal's aggregator module. gunzip drupal-pot-5.x-1.x-dev.tar.gz tar -xf drupal-pot-5.x-1.x-dev.tar ls drupal-pot You'll notice a few other files in the...

Building a Block

For this example, you'll create two blocks that make content moderation easier to manage. First, you'll create a block to list comments being held pending approval, then you'll create a block to list unpublished nodes. Both blocks will also provide links to the edit form for each piece of moderated content. Let's create a new module named approval. module to hold our block code. Create a new folder named approval within sites all modules custom you might need to create the modules and custom...

Adding the Data Entry Form

In order for the user to enter notes about a web page, we're going to need to provide a place for the notes to be entered. Let's add a form for notes function annotate_nodeapi amp node, op, teaser, page switch op case 'view' global user If only the node summary is being displayed, or if the user is an anonymous user not logged in , abort. if teaser user- gt uid 0 break types_to_annotate variable_get 'annotate_nodetypes', array 'story' if in_array node- gt type, types_to_annotate break Add our...

JQuery Within Drupal

Using jQuery within Drupal is easy because jQuery is preinstalled with Drupal. Log into your Drupal site as user 1 the administrative account and create a new node of type page. On the node creation form select PHP code under the Input formats section. Enter Testing jQuery as the title and add the following to the body section of the form ' document .ready function lt p id one gt Paragraph one lt p gt lt p gt Paragraph two lt p gt lt p gt Paragraph three lt p gt Hit Submit and then reload the...

Callback Mapping

When a web browser makes a request to Drupal, it gives Drupal a URL. From this information, Drupal must figure out what code to run and how to handle the request. This is commonly known as dispatching. Drupal trims off the base part of the URL and uses the latter part, called the path. For example, if the URL is http example.com q node 3, the Drupal path is node 3. The general approach taken is as follows Drupal asks all its modules to provide an array of menu items that is, a path and some...

The Menu System

D rupal's menu system is one of those dark places where few have the courage to tread. Put on your armor we're going in The term menu system is somewhat of a misnomer. It may be better to think of the menu system as having three primary responsibilities callback mapping, access control, and menu customization. Essential code for the menu system is in includes menu .inc, while optional code that enables such features as customizing menus is in menu.module. In this chapter, we'll explore what...

Replacing Builtin Strings with Custom Strings

Suppose the term blog bothers you, so you want to change it to journal without modifying any code. You can use the locale module to change it. The approach is to create a language containing only the string s we want replaced. First, let's add a custom language to hold our custom strings. The interface for doing that is shown in Figure 18-2. We'll call it English-custom and use en-US for the language code. Tip If you intend to have multiple languages simultaneously enabled on your site and will...

Making Queries Secure with dbquery

A common way of exploiting web sites is called SQL injection. Let's examine a module written by someone not thinking about security. This person just wants a simple way to list titles of all nodes of a certain type function insecure_menu may_cache items array if may_cache items array 'path' gt 'insecure', 'title' gt t 'Insecure Module' , 'description' gt t 'Example of how not to do things.' , 'callback' gt 'insecure_code', 'access' gt user_access 'access content' Menu callback, called when user...

Defining Node Grants

There are three basic permissions for operations on nodes view, update, and delete. When one of these operations is about to take place, the module providing the node type gets first say with its node_access implementation. If that module doesn't take a position on whether the access is allowed, Drupal asks all modules that are interested in node access to respond to the question of whether the operation ought to be allowed. They do this by responding to hook_node_grants with a list of grant...

External Authentication with Server Provided

When a user signs in with a username in the form of joe example.com, we have more information to go by. Drupal core contains drupal. module, which provides an XML-RPC client that contacts another server for authentication. For example, on the site http groups.drupal.org, you can log in with your http drupal.org username and password. Here's what happens when I do that for the first time 1. I log in to groups.drupal.org with the username jvandyk drupal.org and my password. 2. groups. drupal .org...

Manipulating Nodes That Are Not Our Type with hooknodeapi

The preceding hooks are only invoked based on the node type. When Drupal sees a blog node type, blog_load is called. What if you want to add some information to every node, regardless of its type The hooks we've reviewed so far aren't going to cut it for that we need an exceptionally powerful hook hook_nodeapi . Although this hook isn't needed for creating nodes, it's worth mentioning here because it creates an opportunity for modules to react to the different states during the life cycle of...

Translating Strings with t

All strings in Drupal should be run through the t function this is Drupal's translate function, with the function name shortened to t for convenience because of its frequent use. The locale-specific part of the function looks like this function t string, args 0 global locale if function_exists 'locale' amp amp locale 'en' Translate the string. string locale string In addition to translation, the t function also handles insertion of values into placeholders in strings. The values are typically...

The user Object

Drupal requires that the user have cookies enabled in order to log in a user with cookies turned off can still interact with Drupal as an anonymous user. During the session phase of the bootstrap process, Drupal creates a global user object that represents the identity of the current user. If the user is not logged in and so does not have a session cookie , then he or she is treated as an anonymous user. The code that creates an anonymous user looks like this and lives in bootstrap.inc function...

Displaying Menu Items As Tabs

In Drupal's admittedly obscure menu lingo, a callback that is displayed as a tab is known as a local task and has the type MENU_LOCAL_TASK or MENU_DEFAULT_LOCAL_TASK. The title of a local task should be a short verb, such as add or list. Local tasks usually act on some kind of object, such as a node, user, or workflow. Local tasks must have a parent item in order for the tabs to be rendered. A common practice is to assign a callback to a root path like milkshake, and then assign local tasks to...

Separate Database Server and a Web Server Cluster

Multiple web servers provide failover and can handle more traffic. The minimum number of computers needed for a cluster is two web servers. Additionally, you need a way to switch traffic between the machines. Should one of the machines stop responding, the rest of the cluster should be able to handle the load. Load balancers distribute web traffic among web servers. There are other kinds of load balancers for distributing other resources such as a hard disks and databases, but we'll cover those...

Understanding Template Files

Drupal Template Files Diagram

Some themes have all sorts of template files, while others only have page.tpl. php. So how do you know which template files you can create and have be recognized in Drupal What naming conventions surround the creation of template files You'll learn the ins and out of working with template files in the following section. page.tpl.php is the granddaddy of all template files, and provides the overall page layout for the site. Other template files are inserted into page.tpl.php, as the diagram in...

The punchline hookview

Now you have a complete system to enter and edit jokes. However, your users will be frustrated because although punchlines can be entered on the node submittal form, you haven't provided a way to make your module-provided punchline field visible when viewing the joke Let's do that now with hook_view Implementation of hook_view . function joke_view node, teaser FALSE, page FALSE if teaser Use Drupal's default node view. node node_prepare node, teaser Add a random number of Ha's to simulate a...

External Login

Wordpress External Authentication Login

True to Drupal's nature, external authentication can simply be plugged into Drupal by implementing hooks in a module. An overview of the process Drupal goes through when performing external authentication is shown in Figure 6-6. If no module that provides external authentication i.e., responds to the auth hook is enabled, Drupal will treat all usernames as local usernames. So both joe and joe example .com are simply considered strings with no special meaning. However, when a module that...

Technology Stack

Drupal Technology Stack

Drupal's design goals include both being able to run well on inexpensive web hosting accounts and being able to scale up to massive distributed sites. The former goal means using the most popular technology, and the latter means careful, tight coding. Drupal's technology stack is illustrated in Figure 1-1. Figure 1-1. Drupal's technology stack Figure 1-1. Drupal's technology stack The operating system is at such a low level in the stack that Drupal does not care much about it. Drupal runs...

Indexing Content That Isnt a Node hookupdateindex

In the case that you need to wrap the search engine around content that isn't made up of Drupal nodes, you can hook right into the indexer and feed it any textual data you need, thus making it searchable within Drupal. Suppose your group supports a legacy application that has been used for entering and viewing technical notes about products for the last several years. For political reasons you cannot yet replace it with a Drupal solution, but you'd love to be able to search those technical...

Dynamic Queries

If you have a varying number of values in your SQL that cannot be determined until runtime, it doesn't excuse you from using placeholders. You'll need to create your SQL programmatically using placeholder strings such as ' s' or d, then pass along an array of values to fill these placeholders. If you're calling db_escape_string yourself, you're doing something wrong. Here's an example showing the generation of placeholders, supposing that we want to retrieve a list of published node IDs and...

Using filterxss

Cross Site Scripting XSS is a common form of attack on a web site where the attacker is able to insert his or her own code into a web page, which can then be used for all sorts of mischief. Note For examples of XSS attacks, see http ha.ckers.org xss.html. Suppose that you allow users to enter HTML on your web site, expecting them to enter lt em gt Hi lt em gt My name is Sally, and I but instead they enter lt script Whoops Again, the lesson is never trust user input. Here is the function...

Overriding Theme Functions

The core philosophy behind Drupal's theme system is similar to that of the hook system. By adhering to a naming convention, functions can identify themselves as theme-related functions that are responsible for formatting and returning your site's content. Themeable functions are identifiable by their function names, which all begin with theme_. This naming convention gives Drupal the ability to create a function-override mechanism for all themeable functions. Designers can instruct Drupal to...

Xmlrpc Client Example Getting the Time

The site that hosts the XML-RPC specification http www.xmlrpc.com also hosts some test implementations. In our first example, let's ask the site for the current time via XML-RPC time xmlrpc 'http time.xmlrpc.com RPC2', 'currentTime.getCurrentTime' You're calling Drupal's xmlrpc function, telling it to contact the server time.xmlrpc.com with the path RPC2, and to ask that server to execute a method called currentTime.getCurrentTime . You're not sending any parameters along with the call. Drupal...

Adding Metadata to Nodes nodeapiupdate index

When Drupal indexes a node for faster searching, it first runs the node object through node_view to generate the same output you would see in your web browser. This means any parts of the node that are visible will be indexed. For example, assume we have a node with an ID of 26. The parts of the node that are visible when viewing the URL http example.com q node 26 are what the indexer also sees. What if we have a custom node type that contains hidden data that needs to influence search results...

Creating a Custom Filter

Sure, Drupal filters can make links, format your content, and transform text to pirate-speak on the fly, but what'd be really slick would be for it to write our blog entries for us, or at least help us get our creative juices flowing. Sure, it can do that, too Let's build a module with a filter to insert random sentences into a blog entry. We'll set it up so that when you run out of juice in your post and need a creative spurt, you can simply type juice while writing, and when you save your...

Other Web Server Optimizations

There are a few other things that you can do to make your web server run more efficiently. Apache is the most common web server used with Drupal, and it can be tweaked to provide better performance. The following sections will suggest some approaches to try. This Apache module will let Drupal send out Expires HTTP headers, caching all static files in the user's browser for two weeks or until a newer version of a file exists. This goes for all images, CSS and JavaScript files, and other static...

Fastpath The Hidden Cache Setting

The fastpath cache setting is not configurable from within the Drupal administration interface because of its highly advanced nature fastpath gives developers the ability to bypass Drupal to implement a highly customized cache solution, such as memory or file-based caching see Figure 15-3. Figure 15-3. The request life cycle of anonymous user page caching under Drupal's fastpath cache setting Figure 15-3. The request life cycle of anonymous user page caching under Drupal's fastpath cache...

Understanding hookuserview

Hook_user 'view' is used by modules to add information to user profile pages e.g., what you see at http example.com q user 1 see Figure 6-1 . Figure 6-1. The user profile page, with the blog module and the user module implementing hook_user 'view' to add additional information Let's examine how the blog module added its information to this page function blog_user op, amp edit, amp user if op 'view' items 'blog' array 'title' gt t 'Blog' , 'value' gt l t 'View recent blog entries' , blog user-...

Simple External Authentication

Let's implement a very simple external authentication module that might be used inside a company where simple usernames are used. Suppose your company only hires people named Dave, and usernames are assigned based on first and last names. This module authenticates anyone whose username begins with the string dave, so the users davebrown, davesmith, and davejones will all successfully log in. Implementation of hook_auth function authdave_auth username, pass, server Does username begin with...

B

B placeholder, 52 bandwidth optimization, 349 banned hosts, 9 base property, forms, 171 base URL, establishing, 8 base_path variable, page template, 117 betty.userland.com server, 293 blocks, 5, 131-145 adding Pending Users block, 143 block.tpl.php template file, 120-121 block block_id block_zebra variables, 121 blocks table, 134, 135, 368 blocks_roles table, 135, 368 boxes table, 135 building blocks, 137-144 configuration options, 132-133 custom blocks, 132 database schema for blocks, 134...

Hookfilter

Now that the basics of the module are in place, let's add our implementation of hook_filter to creativejuice.module Implementation of hook_filter . function creativejuice_filter op, delta 0, format -1, text '' switch op return array 0 gt t 'Creative Juices filter' case 'description' return t 'Enables users to insert random sentences into their posts.' No settings user interface for this filter. break case 'process' return 'creativejuice_sentence', text The filter API passes through several...

How Caching Works

Module developers can store a cache of their data into one of the tables reserved for caching within the Drupal database, or they can create a new table for cache storage. The next time this information is needed, it can be quickly retrieved with a single query and bypass expensive data manipulations. The default table to which your module can write cached information is named cache. Using this table is the best option when storing only a couple rows of cached information. If you're caching...

Pruning the Sessions Table

Drupal stores user sessions in its database rather than in files see Chapter 16 . This makes Drupal easier to set up across multiple machines, but it also adds overhead to the database for managing each user's session information. If a site is getting tens of thousands of visitors a day, it's easy to see how quickly this table can become very large. PHP gives you control over how often it should prune old session entries. Drupal has exposed this configuration in its settings. php file. ini_set...

Multipage Forms

We've been looking at simple one-page forms. But you may need to have users fill out a form that spans several pages or has several different steps for data entry. Let's build a short module that demonstrates the multipage form technique by collecting three ingredients from the user in three separate steps. Our approach will be to pass values forward in hidden form fields. We'll call the module formwizard.module. Of course, we'll need a formwizard.info file. description An example of a...

Building a jQuery Voting Widget

Let's write our first jQuery-enabled Drupal module. We'll build an Ajax voting widget as shown in Figure 17-2, which lets users add a single point to a post they like. We'll use jQuery to cast the vote and change the total vote score without reloading the entire page. We'll also add a role-based permission so only users with the rate content permission are allowed to vote. Because users can only add one point per vote, let's name the module plusl. We'll have to get some basic module building...

Building the Module

Drupal Plus1 Css

Open up the empty plus1. module in a text editor and add the standard Drupal header documentation Next you'll start knocking off the Drupal hooks you're going to use. An easy one is the use of hook_perm , which lets you add the rate content permission to Drupal's role-based access control page. You'll use this permission to prevent anonymous users from voting without first creating an account or logging in. Implementation of hook_perm . function plus1_perm return array 'rate content' Now you'll...