Feature module example to bundle WYSIWYG Configuration

This is a concise example showing how you can use the Features module to bundle configurations in Drupal into a module.  It's great to see the Drupal community moving towards using code to capture, reuse, and deploy configuration settings.  It's been the biggest pain point in working with Drupal for me.

One of the things Drupal core does not do well at all is provide an easy way to switch on a WYSIWYG editor. There is no editor out of the box and setting it up requires some custom configuration of several Drupal core settings, installing and configuring several contributed modules, and also installing one or more external libraries, like the TinyMCE library.

WYSIWYG as a Feature 

TekX – Introduction to Testing with Selenium

Arne Blankerts – part of thePHP.cc, premium PHP consulting and training.

  • Selenium is a java based portable testing framework.
    • Seleniumm RC runs any browser supported by the framework, firefox, opera, etc
      • Can operate/execute ANY browser available on the host machine
      • On Linux, have to tweak how process is shutdown.
    • Selenium IDE – extension for Firefox to Create and Edit Tests.  Can record clicks instead of writign code.
  • Where to use it?
    • Acceptance/Integration Tests – test behaviors in the browser.
    • Compatibility Tests – test that new functions work across browsers/versions
    • Monitoring – you can record behavior and trigger events.
      • Can test that, for example, your registration form actually works not just makes the page available.
    • Test case recording for Bugreports.
      • Provide more detailed, reproduceable bug reports.
    • Development – automate filling out forms during development.
  • Selenium does not replace unit testing or other forms of testing.
  • Useful RC options
    • -port alternate listening port
    • -log save console output to logfile
    • -debug more trace output
    • -timeout seconds to wait before giving up on a test
    • -html run a single standalone test
  • Selenium Tests
    • Written in browser independent JavaScript based DSL
    • Same test can run on any OS with Java runtime and many browsers
      • Firefox 2+, Internet Explorer 6+, Opera 8+, Webkit from Safari 2+
    • Can write tests using PHP as well.
    • Can test for presence of elements – seems to use XPath to select elements, but you can use other notations.
    • Pretty powerful selectors for finding elements.

Talk derailed a bigt by technical difficulties, but provided a good introduction into selenium.

Day 2 Keynote – 10 Developer Trends

Today's keynote is about trends that developers across platforms and languages will face.  None of these should be big surprise if you keep up to date and "read the internet".  Presented by the VP of Technology at dzone.com.

  • The Rise of Agile and Lean
    • Majority of developers report using Agile, but it means different things to different people – i.e. using Scrum, User Stories, etc.
    • Agile is a hot topic at DZone, so there's an audience looking for help and information on using it.
    • Noticed a rise in consultants and software claiming to help implement Agile.
    • People with small teams/enterpreneurs are the ones practicing agile.  Hasn't broken into "the Enterprise".
    • But, does Agile lead to better software?  Do more frequent releases mean we hit schedules, are more secure,  and solve user problems?
      • More frequent releases DO cut down on having to explain stuff to clients, because you can show them stuff.
  • Switch to Standards based Browsers
    • Chrome has had tremendous growth, particularly with technical audience.
    • Windows developers are switching quickly to IE8.
    • Overall developer population is still largely on Microsoft.  But those developers don't go to conferences?
  • Everyone needs to know Ajax/Javascript
    • Back end developers need to understand the user interface too.
    • Should learn at least on framework such as jQuery or Prototype
    • Understand how ajax works, and its data transports like JSON, XML.
  • Focus on Security
    • Vulnerabilities get more and more media coverage.
    • Are there more security holes or are attackers getting smarter.
      • XSS, SQL Injection, weak passwords, etc…
    • Can't assume your framework takes care of all security.
    • Give equal time to security audits of your code.  Think about security when you're developing.
  • Rich User Interfaces
    • Users expect much more dynamic interfaces, even if they prefer style over substance.
    • Lots of options for RIA – Flash, Silverlight, Flex, Ajax, HTML5
    • Must RIAs run outside the browser?
      • it can run on the desktop – what opportunities does this create?
    • Benefits of RIA vs. normal website?
  • Mobile and other UI Form Factors
    • Can't presume that everyone has a mouse.  Link targets will have to get larger, more touch friendly.
    • Different platforms have touch interfaces that behave differently.
    • Supporting multiple platforms will be a point of pain.
    • But users expect application to just work.
  • Death of Relational Databases
    • Can mean different things to different people.
    • Data store that is focused on horizontal scaling, massive data, and lookups based on primary keys.
    • Large Web 2.0 sites are moving to NoSQL – Digg, Twitter, Reddit
    • But relational databases are not going away any time soon.
      • Most developers are fie on RDMS.  Few need to Scale at this level or deal with massive data storage.
      • Not all queries convert easily.
    • NoSQL data stores very common on the cloud  – App Engine, Amazon SimpleDB
  • Distributed Version Control
    • Many choices emerged – Git, Mercurial, Baazar
    • Git seems to have most momentum
    • Adoption driven by Open Source
    • Does anyone still use CVS?
    • Subversion still big, driven by tool support.  Its entrenched in the enterprise because the IDE's have good support built in.
    • Distributed VCS encourages outside participation.
  • Cloud Computing
    • What does the term really mean?
      • non-root access on someone else's hosting platform (ie my blog is on wordpress.com)
      • or virtual servers with root access that can be easily replicated.
    • Lot of "Cloud" platforms – App Engine, Azure, VMForce, hosting companies, etc…
    • Hand-in-hand with NoSQL
    • Most apps don't need that kind of scalability
      • Complex systems require more management and automation.
    • Is it worth rewriting our application to run in the cloud.
  • Rise of Interpreted Languages
    • Ruby, Python, Clojure, Scala, Groovy
    • New languages to solve new problems
      • Better support for the cloud
      • Take advantage of multiple cores and parallel processing.
      • Often early takers of NoSQL
    • Is the JVM empowering language developers?

Habari Project impressions

I have to admit that i jumped over to using Habari for my blog mainly based on how well designed and obejct oriented the underlying API seemed from the documentation. Coming from looking at Drupal code all day, which is painfully NOT Object Oriented, yes with capitals, the code is more readable and, so far, less mysterious.

Don’t believe me? Go read the instructions for Creating a Content Type. Like Drupal, it depends on function name conventions for some hooks, but beyond that everything else is nicely encapsulated in a proper class.

But I haven’t developed my own plugins yet, although I have an idea for one. My main impressions are from installing it and using it.

The installation itself is very straightforward, at least on par with WordPress. If, like me, you find yourself installing it multiple times, one can Predefine configuration settings and not have to manually enter them each time.

The admin interface is elegant and useful, certainly less cluttered than Drupal’s admin pages. A simple pull down menu in the top left corner of the page lets you add posts, configure plugins, and access other admin functions.

The plugin system is in a state of flux, due to a change in how plugins are defined/discovered by the system between versions 0.6 and 0.7. This means a lot of plugins are available for the former, but maybe not the latter. It’s also difficult to track down a solid list of plugins, so I’ve been using http://svn.habariproject.org/habari/trunk/htdocs/user/plugins with svn:externals to install and update plugins.

The new "Bytes in the Margin"

Its time to relaunch my blog as Bytes in the Margin. I like the new name, which is not an anagram of my own name. I was inspired by all the notebooks I have from school and work meetings, which are decorated with pen and pencil doodles in the margin.

While the title is new, the content won’t really change, and I’ve left the Feedburner feed at RecordAsIAm still enabled, so hopefully I don’t lose any subscribers.

This also gives me a chance to check out a new blogging/content management platform – Habari Project, which, from looking at the code, looks like a very nicely designed PHP blogging framework. Given that my major point of reference is Drupal, that may not be saying much. If you’re particularly geek, learn Why Habari? or look at how they’ve implemented Plugins

Getting XDebug workign on Mac OSX

Having a good debugger and error reporting can mean the difference between spending an hour hunting down the line of code causing bug and killing it quickly. For PHP, xdebug is my preferred debugger (are there others?),.  Thanks to this link on debuggable.com, I was able to find a precompiled binary for OS X. Setting up Xdebug on Mac OS X (or Win32 / Linux) » Debuggable Ltd

Anyway, one of the biggest obstacles when getting started with Xdebug
was that I had a hard time finding good instructions on how to set it
up on Mac OS X (I finally got rid of windows, yeah !). The only good
resource I found was stored in Googles cache (wasn’t live anymore) and
had instructions on how to manually build xdebug from source. However,
I didn’t have a lot of time back then and compiling would have involved
setting up all kinds of additional tools. This was the point when I
vaguely remembered that the Komodo IDE
was using xdebug. So I did some more research and sure enough, the good
folks over at ActivateState actually provide their Xdebug binaries as
stand-alone downloads for all major platforms.

HOWTO: Use VirtualBox to setup an Internet Explorer testing machine

There are a lot of cases where having one computer just isn’t enough. If you’re a web developer or webmaster, you’ll want to view your website in more than one browser. If you’re a PHP programmer, you may be itching to check out the new 5.3 release and test your web applications on it. Using virtualization, you can set up self-contained instances of any Operating System configured to your heart’s content on your computer.

There are a lot of virtualization environments out there. VirtualBox is an Open Source virtualization solution from Sun. I particularly like that it runs not only in Linux, but also on Mac OS X and windows. In this post I’ll show you how to set up VirtualBox with disk images supplied by Microsoft for testing Internet Explorer compatibility in OS X. The instructions are based on this article for setting up VirtualBox in Ubuntu.

Download supporting files and utilities

To get each environment setup, you’ll need a few files and tools installed to get everything working.

1. Install RAR

Download the RAR Utility for Mac OS X, as of this writing its labeled RAR 3.90 beta 3 for Mac OS X. The download file is a .tar.gz file. In OS X terminal you can use tar -xvzf rarosx-3.9.b3.tar.gz to extract the files. Next, you’ll want to copy the extracted files to /usr/local/bin/ or anywhere else that is in your $PATH.

2. Install Burn

We’ll need to create an .ISO file for the networking drivers expected by the Windows images. Burn 2.3u is a free and straightforward tool for burning disks or creating disk images. Simply download it and drag it into your Applications folder.

3. Download PCNET Drivers and create ISO file

Since we’ll want our virtual Windows machines to get on the Internet, you’ll need to download the correct drivers from AMD. Download the ZIP file labeled NDIS5 Driver dated August 2004 and unzip them somewhere. Launch the Burn application and drag the PCNET files from where you extracted them into the Burn app. To create the ISO file, select "Save Image" from the File menu. Save the ISO file somewhere where you’ll remember it, as you’ll need it later.

4. Install VirtualBox

Head on over to the VirtualBox downloads page and download the binary for OS X hosts. Double click on the disk image (dmg) file to mount it, and double click on VirtualBox.mpkg to run the installer. Once installed, VirtalBox will be in your Applications folder.

Setup Windows XP Images in VirtualBox

If you’ve made it this far, you should have VirtualBox installed and all the tools for setting up a Windows XP or Vista image. Repeat this part for each XP Image you want to install, MS provides a number of images combining XP/VISTA with IE6/IE7/or IE8.

5. Download a Windows Image.

Select an image to download from the Internet Explorer Application Compatibility VPC Image page. These images are fairly large, the smalles is 465MB.

6. Extract the .vhd file from the image

Microsoft’s VPC images are mean to be used with their own VirtualPC technology. Luckily, VirtualBox can read VirtualPC’s .VHD files. Use rar to extract the files from the .exe or .rar file you download, as follows.

unrar e IE6-XPSP3.exe

7. Create a new VirtualBox machine

Now that you have the disk image, launch VirtualBox and create a new machine. Make sure you select Windows XP as the Guest OS, and give it enough memory to run well. This depends a lot on how much memory your Host OS has, you can get away with 256MB but I like to give them 512MB if possible. When it asks you

When the new machine wizard prompts you for the hard disk image to use, select the .vhd file extracted in step 6.

8. Launch the new machine and install Guest Additions and network drivers

If you have your new machine setup, click on the green start arrow to boot it. You should see the Windows boot sequence and eventually you’ll get to the Windows desktop. You’ll get prompted a hundred times to install new hardware and connect to Windows update, but just hit cancel until Windows stops trying to be helpful.

First, install the Guest Additions which provide a more seamless transition between Host and Guest OS by clicking on "Install Guest Additions" in the Devices menu

Next, mount the ISO image created in step 3 as a CD. Click on Devices, then Mount CD/DVD-ROM and CD/DVD-ROM Image. Selct the ISO image and then you should see its contents in the virtual CD-ROM drive.

Finally update the network drivers to get the network card working. Select My Computer, click on View System Information, Go to System > Devices, Click on Hardware tab, then Device Manager button. Select ethernet device under Network Adapters and then right click, select Update Drivers, say no to Windows Update, then click Next. Windows should find and install drivers from the CD.

Gotchas and where to go from here.

At this point you should have a working virtual machine with network access. You can repeat steps 5-8 for other Windows images to have one or more environment for testing each version of Internet Explorer. If you do install other Windows images, be aware that the supplied MS Images may all have the same disk UUID, in which case VirtualBox refuses to add them to the Media list. Use VBoxManage to clone the disk image and generate a new UUID. You’ll have to remove the conflicting disk from the list first, and to remove it the virtual machine must be powered off. From the command line, the following will clone a drive:

VBoxManage clonehd XP SP3 with IE7 2009-Apr.vhd IE7_XPSP3.vhd XP_SP3_IE7.vhd

If you’re setting up IE testing environments, I highly recommend that you install the MS IE Developer toolbar too. It’s very helpful in diagnosing Internet Explorer behaviors.

Now that you have VirtualBox running, you’re not limited to Windows images. You can also setup your Linux distribution of choice in a VirtualBox machine. You could setup a copy of your LAMP environment for testing and expirementation.

Tip: Disable Drupal performance settings

On development, testing, and staging site you normally don’t want Drupal’s caching, and javascript/css aggregation features enabled.  Drop the following into your sites settings.php file to override these settings.

<ol><li class="li1"><div class="de1">&nbsp;</div></li><li class="li1"><div class="de1"><span class="co1">// disable performance caching</span></div></li><li class="li1"><div class="de1"><span class="re0">$conf</span><span class="br0">[</span><span class="st_h">'cache'</span><span class="br0">]</span> <span class="sy0">=</span> <span class="nu0">0</span><span class="sy0">;</span></div></li><li class="li1"><div class="de1"><span class="re0">$conf</span><span class="br0">[</span><span class="st_h">'block_cache'</span><span class="br0">]</span> <span class="sy0">=</span> <span class="nu0">0</span><span class="sy0">;</span></div></li><li class="li2"><div class="de2"><span class="re0">$conf</span><span class="br0">[</span><span class="st_h">'preprocess_css'</span><span class="br0">]</span> <span class="sy0">=</span> <span class="nu0">0</span><span class="sy0">;</span></div></li><li class="li1"><div class="de1"><span class="re0">$conf</span><span class="br0">[</span><span class="st_h">'preprocess_js'</span><span class="br0">]</span> <span class="sy0">=</span> <span class="nu0">0</span><span class="sy0">;</span></div></li></ol>

Writing an intelligent hook_nodeapi function in Drupal

For Drupal module developers, the hook_nodeapi function affords a lot of flexibility for interacting with nodes at various operation.  If you just start pasting or writing code, you’ll quickly end up with a giant, messy switch statement.  But there is a simpel way to keep your code nicely organized.  First, lets take a look at what operations we can affect:

  • “alter”: the $node->content array has been rendered, so the node body or teaser is filtered and now contains HTML. This op should only be used when text substitution, filtering, or other raw text operations are necessary.
  • “delete”: The node is being deleted.
  • “delete revision”: The revision of the node is deleted. You can delete data associated with that revision.
  • “insert”: The node is being created (inserted in the database).
  • “load”: The node is about to be loaded from the database. This hook can be used to load additional data at this time.
  • “prepare”: The node is about to be shown on the add/edit form.
  • “prepare translation”: The node is being cloned for translation. Load additional data or copy values from $node->translation_source.
  • “print”: Prepare a node view for printing. Used for printer-friendly view in book_module
  • “rss item”: An RSS feed is generated. The module can return properties to be added to the RSS item generated for this node. See comment_nodeapi() and upload_nodeapi() for examples. The $node passed can also be modified to add or remove contents to the feed item.
  • “search result”: The node is displayed as a search result. If you want to display extra information with the result, return it.
  • “presave”: The node passed validation and is about to be saved. Modules may use this to make changes to the node before it is saved to the database.
  • “update”: The node is being updated.
  • “update index”: The node is being indexed. If you want additional information to be indexed which is not already visible through nodeapi “view”, then you should return it here.
  • “validate”: The user has just finished editing the node and is trying to preview or submit it. This hook can be used to check the node data. Errors should be set with form_set_error().
  • “view”: The node content is being assembled before rendering. The module may add elements $node->content prior to rendering. This hook will be called after hook_view(). The format of $node->content is the same as used by Forms API.

That’s 15 operations, multiplied by the number of content types on your site, that’s potentially 15N cases we’ll have to account for.  If you want your code to run regardless of the content type, thats another 15 cases.  How much do you like spaghetti?

Our own function naming convention to the rescue

By using a simple naming convention, we can make sure that a) we can drop in code to run for any operation and/or content type b) encapsulate the code within its own function.  To do this, your actual hook_nodeapi implementation will simply delagate execution to these other functions.  First, if a function named module_nodeapi_operation exists, we’ll call it.  Then if the function module_nodeapi_operation_content-type exists, execute it.

Let’s assume you have a module foo, the hook_nodeapi function looks like:


  1. <?php
  2. /**
  3. * Implementation of hook_nodeapi that delegates operations to other functions
  4. * @see http://api.drupal.org/api/function/hook_nodeapi/6
  5. * @author Oscar Merida
  6. */
  7. function foo_nodeapi(&$node, $op,$a3 = NULL, $a4 = NULL)
  8. {
  9. // our own all too clever function api
  10. // first call foo_$op if it exists
  11. // then call foo_$op_$node->type if it exists
  12. $f_base = ‘foo_nodeapi_’ . $op;
  13. if (function_exists($f_base))
  14. {
  15. $f_base(&$node, $a3, $a4);
  16. }
  17. $f_content = $f_base . ‘_’ . $node->type;
  18. if (function_exists($f_content))
  19. {
  20. $f_content(&$node, $a3, $a4);
  21. }
  22. }

If you need to alter something about all nodes before they are saved, you would create a function named foo_nodeapi_presave.

  1. <?php
  2. function foo_nodeapi_presave(&$node, $op, $a3, $a4)
  3. {
  4. // do something to all nodes before they are saved
  5. }

Likewise, to affect what is loaded with a story node, we need a function named foo_nodeapi_load_story:

  1. <?php
  2. function foo_nodeapi_load_story(&$node, $op, $a3, $a4)
  3. {
  4. // do something to story nodes when they are loaded
  5. }


Keep in mind that both functions are called, but the content type specific one is called after the more general one so you can undo/override the latter. As a side note, I was trying to use the presave operation to set the value of a CCK Text field if it was empty. Since the order of hook calls depend on the weight of the module in the system table, I had to make sure the content module had a higher weight than my own. If you’re trying to set the value of a CCK field but it’s not being saved, you’ll have to do the same.