Before we get into writing our own tests we need to enable the SimpleTest module, and while we're at it we may as well talk about the various configuration options it has. And then why not run a few of the tests that come with Drupal core as a way to learn how to discover and run all the tests, a group of tests, or even an individual test case.
The SimpleTest module comes with Drupal 7 core, but is not enabled by default. So we can start by enabling the module. On the administration page for enabling modules the module is listed as "Testing" instead of SimpleTest which can be a bit confusing. The the UI you'll almost always see the name "Testing", but in the code, documentation, and at the command line it's generally referred to as SimpleTest.
Once the module is enabled there are just a few new permissions and configuration options to cover. The new 'run tests' permission, and a couple of settings related to enabling verbose output for debugging of tests. Make sure you check them all out.
Tests can be run in a variety of different ways, either from your browser using the Testing module's UI, or at the command line with drush. We'll cover both of them in this lesson. And in doing so we'll look at how to run a group of tests, or an individual test case, and the output that's generated by running tests.
I've also got a module (which you can download from this page) that contains a couple of failing tests. This way we can run a test case that fails, and see what the output looks like from a failure. SimpleTest outputs some helpful debugging information, and with the verbose option enabled will even save the generated HTML of each page the SimpleTest browser saw so that we can review it after the test run is complete.
If you're not familiar with drush checkout our series on using drush.
By the end of this lesson you should be able to enable and configure the simple test module and run some tests.
Example drush commands
Run all tests in the "Block" group:
drush -l http://localhost/demos/simpletest-7x/docroot test-run Block
Run just the "BlockHashTestCase" test case:
drush -l http://localhost/demos/simpletest-7x/docroot test-run BlockHashTestCase
Note: as of Drush 7 the drush test-run
command is no longer part available. See https://github.com/drush-ops/drush/issues/599.
You can either, use Drush 6, or use the scripts/run-tests.sh
scrip that comes with Drupal core. See the documentation for the run-tests.sh script for more information.
Before we can get started creating our own tokens we'll need a basic Drupal site setup and module to start from. This lesson will walk through the prerequisites for the rest of the series including having Drupal installed, downloading and installing the databasics module, creation of some dummy content, and a quick tour of what the module does.
In order to follow along in this series you'll want to have a copy of Drupal 7 installed. If you need a refresher on installing Drupal check out this series on getting up and running with Drupal 7.
Once you've got Drupal 7 installed you'll want to download and install a copy of the databasics module that was created as part of the Drupal 7 module development series. We'll use this as a starting point for adding custom tokens so that we have some new data to play with. You can grab a copy of the files attached to this page.
Finally, we'll create some dummy content on our site and walk through what the databasics module does and talk about the problem we're trying to solve.
The databasics module has a string of text that's displayed at the bottom of each node that displays some dynamic information. We would like this text to be configurable by an administrator, so we'll add some new tokens and then update both the form where text is entered and the code that displays the text to allow for use of the newly created tokens.
Additional resources
With so much information, and so many products, on the web today, people often want to get an opinion to help rank and rate things. Should I buy this widget? Should I watch this movie? In this series, we’re going to use a handful of Drupal modules to build a product review website that lets community members give their opinions, along with a way to rate their review as well. To kick things off, in this lesson we will:
- Review the Super Duper Chefs case study
- Discuss our implementation
Additional resources
In this video we walk through two methods for downloading and installing new modules. First we use the administrative interface on our site to install a new module with a URL, and then we download a module directly from Drupal.org and place the files in the proper location in our Drupal files. We also talk about some best practices for organizing your modules in your file system.
Probably the most common use case for view modes is teaser nodes. Or, a node being rendered in the teaser view mode. View modes allow entities to be displayed different depending on context, and also allow other modules like Field API that participate in the entity rendering process to adjust their behavior depending on the requested view mode.
In this lesson we’re going to take a look at an ‘authenticated’ view mode that we can use to adjust the display for any authenticated users. Then we’ll update Entity Controller class and make use of this new view mode in our ::view() method.
NOTE: in the video Joe forgot to change the 'member' label to "Member" and then later in the video when we view it in the UI it is corrected. The sample code included with this video has the correct label.
So far we’ve seen that you can use entity_load and entity_view to display your entity. But what if you want more control over how your content gets rendered? In this lesson we’ll take a look at overriding the buildContent() method of our entity controller to spruce up the output a bit. We'll also overrid the save() method of the controller in order to populate the created_at and updated_at fields automatically when saving a video entity.
Note: In order to improve upon the security of the code in this lesson you'll want to make sure you're escaping all user input properly. The following code:
$build['embedcode'] = array(
'#type' => 'markup',
'#markup' => '<iframe width="560" height="315" src="http://www.youtube.com/embed/'. $entity->embedcode . '" frameborder="0" allowfullscreen></iframe>',
);
Should be updated to use the check_url() function when outputting $entity->embedcode
to ensure that user entered content is safe for use in the context of a URL.
$build['embedcode'] = array(
'#type' => 'markup',
'#markup' => '<iframe width="560" height="315" src="http://www.youtube.com/embed/'. check_url($entity->embedcode) . '" frameborder="0" allowfullscreen></iframe>',
);
Goes over the Macro Maker demonstration module in order to show what functionality we will be building over the next couple of chapters.
Displaying that data that was collected and saved for our a field requires creating a field formatter. Formatters consist of an implementation of hook_field_formatter_info() and hook_field_formatter_view(). The former provides meta-data about the formatter for the Field API and the latter does the heavy lifting of determining what the output is actually going to look like.
A module can define more than one field formatter.
Implementations of hook_field_formatter_view()
return a renderable array representing the content you would like to display to the end user. Generally this is an escaped version of content provided by a site administrator with some additional HTML formatting applied.
Depending on the values being output you would likely want to also use a theme function for your field formatter by implementing hook_theme() and providing either a theme() function that can be overriden or a template file. We're not going to cover that in this lesson since the focus here is on the technical requirements for impelementing a field formatter. Howerver, I would say that it's best practcie to always output any HTML with a theme function. You can find out more about creating themeable output by watching these videos from our library: http://drupalize.me/videos/integrating-theme-system and http://drupalize.me/videos/using-drupal-render-api
Example:
/**
* Implements hook_field_formatter_info().
*/
function rgb_field_formatter_info() {
return array(
'rgb_raw' => array(
'label' => t('Raw color value'),
'field types' => array('rgb_color'),
),
'rgb_box' => array(
'label' => t('Color block with label'),
'field types' => array('rgb_color'),
),
);
}
/**
* Implements hook_field_formatter_view().
*/
function rgb_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, &$items, $display) {
$element = array();
switch ($display['type']) {
case 'rgb_raw':
foreach ($items as $key => $value) {
$element[$key] = array(
'#type' => 'markup',
'#markup' => t('#@hex', array('@hex' => $value['rgb'])),
);
}
break;
case 'rgb_box':
foreach ($items as $key => $value) {
$element[$key] = array(
'#type' => 'markup',
'#markup' => '' . check_plain($value['label']) . '',
);
}
break;
}
return $element;
}
Additional resources
The Applications view will serve both as a tool for administrators and as a reference for users, with three different displays. In this lesson, we'll start to build the Applications view by creating the default display with our first listing, which is a master list of all the applications on the site. To pull in all of the information we're going to need, we'll begin working with Views relationships.
Additional resources
Goes through the process of adding a role and then creating a user reference field, which autocompletes to users within that specified role.
In this video you'll learn how to use api.drupal.org the canonical source for information about Drupal's hooks, APIs, and code documentation in order to find out information about implementing a particular hook, making use of a particular function or library of functions, and even gaining a better understanding of some of the big picture concepts behind Drupal's code and APIs.
Installing Drupal using the instructions in this tutorial will give you a working Drupal site that can be used for learning, or real-world project development.
Before you can work on a Drupal site locally (on your computer), you'll need to set up a local development environment. This includes all the system requirements like PHP and a web server, that Drupal needs in order to run. Our favorite way to accomplish this is using DDEV.
In this tutorial we'll learn:
- How to install and configure DDEV for use with a Drupal project.
- How to use DDEV's integrated Composer to download Drupal and Drush.
- How to install Drupal inside DDEV so you can access the site and start doing development.
By the end of this tutorial, you should be able to set up a local development environment for learning Drupal or working on a new Drupal project.
Shows how to change the formatting and label display for fields, and how to control whether a field appears in the teaser, full mode, search results and other display mode contexts.
We'll go a step further with our preprocess functions and look at working with node variables, and how to limit new variables to only specific content types.
Additional resources
In this chapter we continue working with Date filters in Views and look at using the created and updated core date fields.
In this tutorial we will get hands-on with Domain Access by getting the module installed. This is a more involved process than a regular module installation, but we just need to make sure we have a few things in place first. We're going to need to make sure we have our domains functioning correctly through Apache, and then add the Domain Access include file to our settings.php. With the configuration and module in place, we'll also verify that it is working properly and take a look at our domain list.
After watching this tutorial you will be able to properly install the Domain Access module, with its additional steps, and then verify that the installation was correct.
Additional resources
Domain Access project (drupal.org)
Domain Access Configuring settings.php (drupal.org handbook)
Installing the Domain Access module (Drush instructions) (drupal.org handbook)
So far we’ve got a nice start with getting our site translated, but everything is not quite smooth yet. We have translations for some of our content and menu items, but we're seeing all the content at the same time. You will also see some stray interface text still in English. To take our multilingual site further and really make it shine, in this lesson we're going to turn to a package of modules called Internationalization (i18n). There is a central Internationalization module, which comes packaged with a handful of other modules designed to work together to extend core’s multilingual features.
Additional resources
Internationalization (i18n) project
Using Drupal, 2nd edition
Using Drupal source code
Note: In the video, around 9:30, we added at the :uid
argument to the query, but did not add the corresponding portion to the WHERE clause of the query. The final query should look like the one in the code sample below.
Now that we've got placeholder tokens that we can enter into a string of text we need to provide the actual values that should be used to replace those placeholders. In this lesson we'll implement hook_tokens() in order to provide the Drupal Token API with the values that correspond to the placeholders our module provides.
Implementations of hook_tokens()
are called once for every token type that's in scope for the current string that's being processed. So, one for all user tokens, once for all node tokens, and once for all global tokens. Each time the hook is passed an array which contains all the tokens of that specific type that where found in the string being processed as well as any additional contextual information such as the current $user
or $node
object.
hook_tokens()
is expected to return an array of values for each of the tokens in question that your module is responsible for. In our case, since we added the [databasics-page:*]
and [databasics-totals:*]
tokens we're responsible for calculating and returning their value whenever they are requested.
We'll also look at using the token_find_with_prefix() function which will allow us to detect and provide values for any chained tokens. Like for example [node:view-count:last-viewed]
.
Example:
/**
* Implements hook_tokens().
*/
function databasics_tokens($type, $tokens, array $data = array(), array $options = array()) {
$replacements = array();
if ($type == 'databasics-totals') {
foreach($tokens as $name => $original) {
switch($name) {
case 'count':
$count = db_query('SELECT SUM(view_count) FROM {databasics}')->fetchField();
$replacements[$original] = $count;
break;
}
}
}
if ($type == 'databasics-page' && !empty($data['databasics_record'])) {
$record = $data['databasics_record'];
foreach ($tokens as $name => $original) {
switch ($name) {
case 'view-count':
$replacements[$original] = $record->view_count;
break;
case 'last-viewed':
$replacements[$original] = $record->last_viewed;
break;
}
}
}
if ($type == 'node' && isset($tokens['view-count']) && !empty($data['node'])) {
$node = $data['node'];
global $user;
$count = db_query('SELECT SUM(view_count) FROM {databasics} WHERE nid = :nid AND uid = :uid', array(':nid' => $node->nid, ':uid' => $user->uid))->fetchField();
$replacements[$tokens['view-count']] = $count;
// [node:view-count:last-viewed]
if ($count_tokens = token_find_with_prefix($tokens, 'view-count')) {
$record = databasics_get_record($node->nid, $user->uid);
$replacements += token_generate('databasics-page', $count_tokens, array('databasics_record' => $record), $options);
}
}
return $replacements;
}
Additional resources
In this lesson we'll cover downloading and installing the Migrate module (version 7.x-2.6) and ensuring that our local environment is ready to be able to run migrations via both the UI and drush. Once that's setup we'll take a high level look at the migrate module's UI and drush commands to familiarize ourselves with the tools that we'll be using throughout the rest of the series. This will also help formalize some of concepts introduced in the previous lesson.
Additional resources
Migrate module project page
migrate-7.x-2.6-rc1 download
Migrate module documentation
Introduction to Drush series
Drupalize.Me Migrate module series code on GitHub
In this lesson we're going to start off with the Drupal administrative interface. We'll take a tour of the major elements and get you oriented to what is where.