A Colophon for Drupal

I wanted to put together a colophon for my Drupal site that would show the current installation details—pulling the information from the live site rather than relying on me to remember to update it after each change. With one module, three views, and a tiny bit of PHP and JavaScript, it’s now working.

Module needed: Views System (and any dependencies not yet installed).

Put the code snippets below in blocks, make the views described below as blocks also, then mix together on a page. Voilà!

Code to obtain Drupal version:

<?php print VERSION; ?>

Code to obtain paragraph font:

<p id="fontnameforcolophon">not determined</p>
<script type="text/JavaScript">
(function() {
   var elem1 = document.getElementById("fontnameforcolophon");
   var style = window.getComputedStyle(elem1, null);
   elem1.firstChild.nodeValue = style.fontFamily;

View to display themes in use:

fields: System: Display name, System: Base theme (Base theme)
filter criteria: System: Type (=Theme), System: Status (Enabled)

View to display core modules:

fields: System: Display name, System: Project
filter criteria: System: Status (Enabled), System: Drupal core (Yes), System: Type (=Module)

View to display additional modules:

fields: System: Display name, System: Project
filter criteria: System: Status (Enabled), System: Drupal core (No), System: Type (=Module)


Displaying weather XML from NOAA using PHP and XSLT

NOAA now has XML feeds with weather forecasts based upon your latitude and longitude—allowing you to get a forecast much closer to your house if you wish. The URL is http://forecast., where you substitute your own latitude and longitude for those in the example. Using XSLT, the feed can be quickly styled into something useable on a web page. The XSL style sheet (noaa.xsl) follows:

  1. <?xml version=“1.0” encoding=“utf-8”?>
  2. <xsl:style sheet version=“1.0” xmlns:xsl=“” xmlns:xsi=“” exclude-result-prefixes=“xsi”>
  3. <xsl:output method=“xml” omit-xml-declaration=“yes” encoding=“utf-8”/>
  4. <xsl:variable name=“timelayoutkey” select=“/dwml/data/parameters/weather/@time-layout” />
  5. <xsl:template match=”/”>
  6. <h2>Weather Forecast <xsl:value-of select=“dwml/data/location/area-description” /></h2>
  7. <p>
  8. <xsl:apply-templates select=“dwml/data/parameters/hazards/hazard-conditions/hazard” />
  9. <xsl:text>More information on the forecast is at the </xsl:text>
  10. <a href=“{dwml/data/moreWeatherInformation}”><xsl:text>National Weather Service site</xsl:text></a>
  11. <xsl:text>. Forecast created </xsl:text>
  12. <xsl:value-of select=“dwml/head/product/creation-date” />
  13. <xsl:text>.</xsl:text>
  14. </p>
  15. <xsl:call-template name=“forloop”>
  16. <xsl:with-param name=“first” select=“1” />
  17. <xsl:with-param name=“last” select=“substring($timelayoutkey,9,2)” />
  18. </xsl:call-template>
  19. </xsl:template>
  20. <xsl:template name=“forloop” >
  21. <xsl:param name=“first” />
  22. <xsl:param name=“last” />
  23. <p class=“weather”>
  24. <img src=“{dwml/data/parameters/conditions-icon/icon-link[$first]}” alt=“{dwml/data/parameters/weather/weather-conditions[$first]/@weather-summary}” />
  25. <span><strong><xsl:value-of select=“dwml/data/time-layout[layout-key = $timelayoutkey]/start-valid-time[$first]/@period-name” /></strong><xsl:text>: </xsl:text>
  26. <xsl:value-of select=“dwml/data/parameters/wordedForecast/text[$first]” /></span>
  27. </p>
  28. <xsl:if test=“$first &lt; $last“>
  29. <xsl:call-template name=“forloop”>
  30. <xsl:with-param name=“last” select=“$last” />
  31. <xsl:with-param name=“first” select=“$first + 1” />
  32. </xsl:call-template>
  33. </xsl:if>
  34. </xsl:template>
  35. <xsl:template match=“hazard”>
  36. <a href=“{hazardTextURL}”><em><xsl:value-of select=”@headline”/></em></a><xsl:text>. </xsl:text>
  37. </xsl:template>
  38. </xsl:style sheet>

If you haven't coded an XSLT style sheet before, you should read about the language before attempting to alter this. Lines 20–34 may look a bit odd—there is no “for” loop in XSLT (at least not in XSLT 1.0, which is what the PHP processor uses). This sheet uses tail recursion to mimic a for loop. Line 28 checks to see if any more work is needed. If so, the template calls itself, but with a different “first” parameter. In XSLT, variables and parameters, once assigned, cannot be changed, necessitating things like recursive calls.

  1. <?php
  2. $xsl = new DOMDocument ;
  3. $xsl->load($_SERVER['DOCUMENT_ROOT'].'/inc/noaa.xsl') ;
  4. $xslProc = new XSLTProcessor() ;
  5. $xslProc->importStylesheet($xsl) ;
  6. $xmldoc = new DOMDocument ;
  7. $xmldoc->load('http://forecast.') ;
  8. echo $xslProc->transformToXML($xmldoc) ;
  9. ?>

To apply an XSL style sheet in PHP, you need to load the style sheet into an xml document, then start up an XSLTProcessor and load the style sheet into that. As you see, the PHP is very simple—all the formatting is done in the style sheet. Learning XSLT is worthwhile if you are transforming XML data into another form (e. g., into a web document). The learning curve is a bit steep, but the code is much more compact and easier to maintain. XSL forces you to code cleanly.

Creative Commons License This work by Rory Jaffe is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License.


Simple Accessible CAPTCHA in PHP

In just a few lines of PHP code, this CAPTCHA will allow you to block most spambots. I developed this after some spammers discovered this web site’s contact form. There already were protections to avoid certain exploits (e. g., hijacking the form to send spam to third parties), but they still were able to send me spam emails.

This one is also accessible—instead of showing a difficult-to-read word, this one gives some instructions that must be followed (and thus requires a human) to succeed. Because there is a vulnerability in this code (a spammer could just use a human to answer correctly once, then send the page repeatedly with the correct answer and hash), this is not suitable for major spammer targets, but should work well on small sites.

On the form page, this will show up as instructions (“Enter the first three letters…”) and a string of six capital letters with spaces between so they are read as individual letters by a screen reader. A hidden field is filled with the hash of the correct three letters. If the hash were only made from the three letters, a spammer could figure out the hash algorithm, then fill the two fields correctly without having to follow the instructions. By adding a secret string to the three letters, the hash function is extremely hard to decode. The secret string must be duplicated on the receiving page in order for it to calculate the same hash.

In your user form, add the following:

  1. <label>Please enter the first three letters of the following:
  2. <?php
  3. $ranstr = chr(mt_rand(65,90)) . chr(mt_rand(65,90)) . chr(mt_rand(65,90)) . chr(mt_rand(65,90)) . chr(mt_rand(65,90)) . chr(mt_rand(65,90));
  4. echo chunk_split($ranstr,1,' ');
  5. ?>
  6. <input name=“ranstr” type=“text” size=“90” maxlength=“75” />
  7. </label>
  8. <input name=“checkran” type=“hidden” id=“checkran” value=<?php echo hash('sha1',substr($ranstr,0,3).'hard to guess the hash function');?> />

Line 3 forms a string of six random upper-case letters. Line 4 prints them out with a space between each. Line 6 (with the label from line 7 and 1) is where the human enters the requested substring. Line 8 provides a hashed version of the substring (plus some ‘salt’ to make guessing the has function more difficult). The first digit in the substr function provides the start character (0 = the first character in the string) and the second digit specifies the number of characters to use — each of those can be changed, but make sure you change the instructions to correspond. To best protect your site, you should change the string in this example — line 8 above and line 2 below (hard to guess the hash function) to something of your own choice.

On the back end, the following PHP code (line 2) cleans up the user input (removing spaces; capitalizing all letters), and calculates the hash with the same appended string as in line 8 of the form page (above). If you submit your form with a GET instead of a POST, change $_POST to $_GET.

If the hashes agree, the user has entered the correct letters, and lines 3 through 17 are skipped. Lines 4 through 15 is the html that is emitted when the hashes fail to agree, and line 17 terminates the program. Normal form processing starts at line 18 or 19.

  1. <?php
  2. if ($_POST['checkran']!=hash('sha1',strtoupper(preg_replace('/\s+/','',$_POST['ranstr'])).'hard to guess the hash function'))
  3. {?>
  4. <html>
  5. <head>
  6. <title>Error</title>
  7. </head>
  8. <body>
  9. <h1>Error</h1>
  10. <p>Oops, it appears that you didn't type the correct three letters in the last box. Please go back to the form and try again.</p>
  11. <form>
  12. <input type=“button” value=“Back” onClick=history.go(-1);return true; />
  13. </form>
  14. </body>
  15. </html>
  16. <?php
  17. die(); }
  18. ?>
  19. <!— The rest of your email form handling goes after this —>

Creative Commons License This work by Rory Jaffe is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License.


Subscribe to PHP