Drupal 7

Customize the Subscriptions terms list

Tags: 

A client recently requested instructions on how to customize the Subscriptions module's user subscriptions page to show more details instead of just the term title, e.g. show a description, image, etc. Thankfully this turns out to be pretty easy and just involves using hook_form_alter to customize the labels.

To use the following code just add it to a custom module and replace the "mymodule" bit with your module's name.

FYI this depends upon the patch from http://drupal.org/node/1522306 and is for Drupal 7.


/**
 * Implements hook_form_FORM_ID_alter().
 *
 * Replace the term labels on the User Subscriptions page. This works by
 * building a full display of the term, thus the term's display should be
 * configured & themed as necessary.
 */
function mymodule_form_subscriptions_page_user_overview_alter(&$form, &$form_state, $form_id) {
  // Only proceed if the 'taxa' field is present, thus the Taxonomy
  // Subscriptions module is enabled.
  if (isset($form['taxa'])) {
    // Process each vocabulary.
    foreach ($form['taxa'] as $key => $data) {
      // Vocabularies will have a numeric $key and the $data will be an array.
      if (is_numeric($key) && is_array($data)) {
        // Change the label to a copy of the term rendered out fully.
        foreach ($data[0]['labels'] as $tid => $label) {
          // Load the term.
          $term = taxonomy_term_load($tid);

          // Build the display array for the term. This can be changed by adding
          // another attribute to the function to specify the view mode - see
          // http://api.drupal.org/taxonomy_term_view for full details.
          $term_array = taxonomy_term_view($term);

          // Render the final output & replace the label.
          $form['taxa'][$key][0]['labels'][$tid][-1]['#markup'] = render($term_array);
        }
      }
    }
  }
}

This results in the page listing taxonomy terms as follows:
The subscriptions list now shows the full taxonomy term display, not just the title.

Simple HTTP authentication for Drupal sites [updated]

Tags: 

There are lots of times when you need some simple password protection, typically via HTTP Authentication, on your Drupal site; my most common use being for publicly accessible development sites that I don't want accidentally indexed by search engines. Thanks to some simple code, based on something originally written by someone at Acquia (thank you, phantom coder!), it's pretty easy to add HTTP authentication to your site and then enable it per site instance; this will keep your codebase and your modules list pretty clean. FYI this works equally well on both Drupal 6 and 7.

There are three parts to this solution:

  1. Modifying the .htaccess file to allow the authentication.
  2. A function that can be added to settings.php or elsewhere in the execution process.
  3. A function call that is used as the on/off switch.

Step 1: Customize the .htaccess file

The first piece is a new line that needs to be added to the .htaccess file:

  # Password protection.
  RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization},L]

This should be added at the end of the file, just before the final , so that it looks like so:

  # Password protection.
  RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization},L]
</IfModule>

Step 2: Add the Special Function

All of the logic is housed in a PHP function that needs to be added to your site, probably the best location being your settings.php file.

/**
 * Password protect the site with a single function.
 */
function secure_the_site_please($username = 'monkey', $password = 'monkey', $message = 'This site is protected') {
  // Password protect this site but ignore drush and other command-line
  // environments.
  if (php_sapi_name() != 'cli') {
    // PHP-cgi fix.
    $a = base64_decode(substr($_SERVER["HTTP_AUTHORIZATION"], 6));
    if ((strlen($a) == 0) || (strcasecmp($a, ":") == 0)) {
      header('WWW-Authenticate: Basic realm="Private"');
      header('HTTP/1.0 401 Unauthorized');
    }
    else {
      list($entered_username, $entered_password) = explode(':', $a);
      $_SERVER['PHP_AUTH_USER'] = $entered_username;
      $_SERVER['PHP_AUTH_PW'] = $entered_password;
    }
    if (!(isset($_SERVER['PHP_AUTH_USER']) && ($_SERVER['PHP_AUTH_USER'] == $username && $_SERVER['PHP_AUTH_PW'] == $password))) {
      header('WWW-Authenticate: Basic realm="' . $message . '"');
      header('HTTP/1.0 401 Unauthorized');
      // Fallback message when the user presses cancel / escape.
      echo 'Access denied';
      exit;
    }
  }
}

Step 3: The on/off switch

The final step is to add the following line to the end of the settings.php file somewhere after the function above:

// Password protect the site.
secure_the_site_please();

This is the master on/off switch that lets you control the authentication functionality. You can easily disable it as needed just by commenting out the line, then the site will continue to work as-is without any problems.

You may have noticed that the function had some arguments? You can use these arguments to change the default username & password from "monkey" to something more relevant, or easily remembered, e.g.:

// Use a custom username & password.
secure_the_site_please('albatross', 'mint');

The function accepts a third argument which controls the message displayed so it can be changed from the default "This site is protected" to something more suitable, e.g.:

// Use a custom username & password.
secure_the_site_please('albatross', 'mint', 'What flavor is it?');

Or even:

// Use a custom username, password & message.
secure_the_site_please('albatross', 'mint', 'The username is "albatross" and the password is "mint"');

Final notes & thoughts

The main reason I like this solution vs e.g. SecureSite is that it doesn't require a different set of modules for one site vs another, all that's needed is one change to the settings.php file and it's easily turned on or off.

I include a settings_shared.php at the top of each settings.php file to store shared settings between all of the per-hostname site instances, and this is where I added the function. On one site I had 44 different per-hostname settings files for four different instances of eleven sites, half of which needed to be protected, so it was very easy to have the function in the settings_shared.php file and then just add the function call to the individual settings.php files.

The HTTP authentication variable, "HTTP_AUTHORIZATION", was so-named so it could be compatible with Drupad, which also uses HTTP authentication; please note that you currently must have this authentication disabled in order for Drupad to work.

So, what do you use to product your in-development sites from prying eyes or being accidentally scraped by search engines?

Updated April 9th:
Thanks for all the feedback. It turns out that the Shield module can basically do this too, using variables (that can be added to settings.php) to control everything; also the SecureSite module has an on/off switch variable so in effect it could be used too. FYI the reason I didn't use any Drupal 7 functions was so that the same code snippet would work with D6 too.

Updated April 12th:
Fixed a small =) bug that inadvertently allowed any password to be passed through, once the username matched.

Subscribe to Drupal 7