tips

Fixing update_sql() to accept parameters

Tags: 

A pretty simple yet cool feature of Drupal's db_query() function is that you can pass in parameters that will make it automatically adjust the query to correctly escape the arguments. This is one of the simple security features in Drupal as it will properly escape the string to avoid SQL injection attacks, and just simply safe you hassle. Good stuff!

When it comes to module development you'll occasionally have to update the database schema for one reason or another, so you'd figure the corresponding update_sql() function, which has some extra purpose for update scripts, would also handle parameters. Not so, unfortunately. In Drupal 5 and 6 the only argument that update_sql() accepts is the query itself, so there's no direct way of doing the parameter auto-magic gravy. Thankfully, all is not lost.

If you look at the code in update_sql(), it's actually very simple - it just runs the standard db_query() string and compiles a nice array to let the update script give a reasonable return message. So, simply put, to do the parameter processing in Drupal 5 and 6 all you have to do is replicate the update_sql() function inline, e.g.:

<?php
/**
 * Change the primary color from blue to green.
 */
function funkychicken_update_6001() {
  $ret = array();
  
  $sql = "UPDATE {funkychicken} SET color='%s' WHERE color='blue'";
  $result = db_query($sql, 'green');
  $ret[] = array('success' => $result !== FALSE, 'query' => check_plain($sql));

  return $ret;
}

So that's all there is to it!

Going a step further, here's an alternative to update_sql() to make it even easier. This can be added to your module's update file, e.g.:

<?php
/**
 * Change the primary color from blue to green.
 */
function funkychicken_update_6001() {
  $ret = array();
  
  $ret[] = update_sql2("UPDATE {funkychicken} SET color='%s' WHERE color='blue'", 'green');

  return $ret;
}

/**
 * An alternative to update_sql() that accepts parameters.
 * Params:
 *   $sql - The query to execute, as a string.
 *   $args - The parameters to pass to the query.
 * Return:
 *   An array containing the success status (boolean) and the query.
 */
if (!function_exists('update_sql2')) {
  function update_sql2($sql, $args = NULL) {
    $result = db_query($sql, $args);
    return array('success' => $result !== FALSE, 'query' => check_plain($sql));
  }
}

FYI there's a patch available for Drupal 6 that might eventually make it into a release, it just needs to be worked on a bit more. Either way, hopefully a fix for this will work its way into Drupal 7.

Posting to Drupal through XMPRPC / BlogAPI

Tags: 

When I migrated my blog from Wordpress to Drupal one key thing I still wanted to do was be able to use ecto to post messages from my laptop rather than through the web interface - it can be just easier at times. Thankfully Drupal has a plugin/module built in which does this for you, the BlogAPI module. So I activate the module then try to connect using ecto only to be greeted with the lovely message:

The response from the server did not contain valid data.

This was frustrating, it should just work, right? Thankfully ecto has a console feature which displays all communications with the server, and I was able to see it submit:

<?xml version="1.0" encoding="UTF-8"?>
<methodCall>
<methodName>blogger.getUsersBlogs</methodName>
<params>
        <param>
                <value><string>ignore</string></value>
        </param>
        <param>
                <value><string>Damien</string></value>
        </param>
        <param>
                <value><string>*****************</string></value>
        </param>
</params>
</methodCall>

This is a normal Blogger API call to find out what blogs a given user (me) has permission to write on at a given blog installation, and should have returned some valid data. Here's what came back:

<?xml version="1.0"?>

<methodResponse>
  <params>
  <param>
    <value><array><data>
</data></array></value>
  </param>
  </params>
</methodResponse>

Obviously this isn't quite right.. I dug through the code, adding print and print_r statements where I thought they might help, until I came to blogapi.module line 937 which says:

$available_types = array_keys(array_filter(variable_get('blogapi_node_types', array('blog' => 1))));

This is supposed to grab the blogapi_node_types system variable which stores a list of the Drupal content types you told it were allowed to be written to using the BlogAPI.. and I had set them, right?

200904201148.jpg

No, as it turned out I hadn't, though I thought I had.

Once I went to /admin/settings/blogapi I was able to mark a few content types to be editable, hit save, go back to ecto and finally see what I wanted:

200904201147.jpg

Success!


Ok, so next problem.. I wrote the above, including the two pasted images, hit Publish but started getting the following error:

<?xml version="1.0"?>
<methodResponse>
  <fault>
  <value>
   <struct>
   <member>
   <name>faultCode</name>
   <value><int>1</int></value>
   </member>
   <member>
   <name>faultString</name>
   <value><string>It is not possible to upload the file, because it exceeded the maximum filesize of 0 bytes.</string></value>
   </member>
   </struct>
  </value>
  </fault>
</methodResponse>

Obviously a problem persisted.

I cranked open the files and inserted some extra code that made it say::

<?xml version="1.0"?>
<methodResponse>
  <fault>
  <value>
   <struct>
   <member>
   <name>faultCode</name>
   <value><int>1</int></value>
   </member>
   <member>
   <name>faultString</name>
   <value><string>It is not possible to upload the file, because it exceeded the maximum filesize of 0 bytes and your file was 12.04 KB.</string></value>
   </member>
   </struct>
  </value>
  </fault>
</methodResponse>

From this you can see that it was getting the file, but it was basically saying that my account was not set up to accept attachments, which was not true as I was the administrator and could do everything, right?

I went back to the BlogAPI settings page, ensured that the file settings looked correct..

200904201211.jpg

.. scratched my head. Then I went to look at the permissions.. as it turned out I had not enabled "administer content with blog api" for my user group.

200904201212.jpg

Doh. I enabled that permission, saved it, and now when I went back to the BlogAPI settings page there was a new item waiting for me:

200904201210.jpg

I expanded that, set the same 1MB and 5MB... and saved..

Et voila! That did the trick, I can now post embedded images too!

menu_link_save doesn't like aliases (drupal)

Tags: 

A quick tip.. while working on an install profile in Drupal I discovered that the menu_link_save() function requires an internal URL, e.g. "node/123" instead of "my-cool-page". Once I tracked down the issue I was able to very easily create lots of menu items as needed, but it wasn't entirely obvious this was needed. I was using install_profile_api to create the menu items and figured it was going to make things easier for the end user, but alas no, so I threw together a quick patch to save others the headache, and wasted hours of development time.

Downgrading svn repositories from 1.5 to 1.4

Tags: 

It's an odd situation, but occasionally you need to convert your data to run on an older version of an application rather than the typical upgrade to newer versions. One such situation arose for me recently. I was starting a new project and was beginning to add/change code while working from home after-hours, with obviously no server admins around to IM a request for a new subversion repository. Being the agile developer I am, I figured I'd just create a local repository and upload it when I was next at the office.

As you may know, practice rarely follows theory. The theory was that I'd just do an svnadmin dump to create a data dump of my local repository and then svnadmin load to get it onto the main svn server at work. As it turned out, I forgot to check something. You see, the svn server uses v1.4 while I've been running v1.5 for about eight months, since shortly after it was first released - as a result, after several days of development my fresh 1.5 data dump wouldn't work with the server's v1.4. Bummer. I submitted a request for the IT department to upgrade the servers to 1.5 - there are several good reasons to do the upgrade - but it isn't something that's going to happen too quickly due to the need to then have all of the other users upgrade their clients.

Thinking I was going to be stuck for a while keeping my repository locally, I kept working locally and figured I'd be able to just wait the few weeks until the central upgrade. Again, life never works quite like you think it will, and I suddenly immediately needed to be able to upload my work to a staging server. Within an hour of dealing with SFTPing files to the server, trying to ensure two different instances of the servers were kept up-to-date and concurrent, the horrors of Life Before Revision Management came flooding back to me and in a mad panic I set out to get my code into a repository one way or another.

This is where my current web hosting provider, Dreamhost, saved my sanity. I had previously set up a repository on my account for some personal code development so figured I'd repeat my success there with the new project, as it would at least be accessible remotely. A couple of quick minutes had an svn dump of the repository waiting on the server for me to import into a new repository. Except it didn't want to work, it turned out that even Dreamhost was using the older v1.4. Argh! It seemed I was doomed to fail!

It was around now I thought "why don't I RTFM?" A quick look at "svnadmin help dump" didn't turn up anything, ditto for "svnadmin help load". Then I tried "svnadmin help create" and saw the previously unknown (to me) option "--pre-1.5-compatible" which is described as "use format compatible with Subversion versions earlier than 1.5" - a step in the right direction! This option would allow me to create a local repository in my local v1.5 setup which would then be compatible with v1.4. I guessed that it might then also create a dump file compatible with v1.4, which could then be cleanly imported into a new v1.4 repository... and sure enough it worked!

So, the exact steps I took were:

  • Created a dump of the v1.5 repository named myproject_1_5.dump using the command "svnadmin dump myproject > myproject_1_5.dump".
  • Created a new repository that was compatible with v1.4 using the command "svnadmin create --pre-1.5-compatible myproject2".
  • Imported the v1.5 dump file into the new myproject2 with the command "svnadmin load myproject2 < myproject_1_5.dump"
  • Created a dump of the v1.4 repository named myproject_1_4.dump using the command "svnadmin dump > myproject_1_4.dump".
  • Uploaded project_1_4.dump to the hosting account.
  • Created a new repository: svnadmin create myproject
  • Loaded the new data dump: svnadmin load myproject < myproject_1_4.dump".

Then, after a few quick moments (it was only up to rev 103 at this point) I was finally able to get back to development using a centrally accessible repository for a few days until I'm back at the office again and can get it back onto company territory.

NOTE: Yes, I know that git would probably have worked better in this situation, but the company is not currently ready for that move..

Building a Drupal site - pick a theme

Tags: 

When building a new site in Drupal, unless you've got a large team of developers, one of the first things you're going to want to do is pick a new theme, i.e. a site template. Thanks to the efforts of hundreds of developers from around the world, there are over 170 themes for you to investigate for Drupal 6 (even more if you're stuck with Drupal 5), each one with its own set of traits. For example some use a table-based layout while others use a pure CSS layout, some are single-column designs though most are multi-column, some that have literally tons of slots for you to add content blocks, some with multiple color options, and some that act as just a bare starting point for you to build your own.

Here are a few things to look for when choosing a theme:

Tableless Design

When you design a page layout you have two options for positioning the various components - tables or CSS. Tables are the old way of doing the design and are very inflexible, not to mention difficult to manage, so CSS has become the defacto standard since the millennium.

An important benefit of a CSS design is that you can add an alternative CSS file to make your page instantly printable. It's both horrible from a user friendliness standpoint to require people click to a separate page for it to be printable, but I honestly also find it really dumb to require people do some special trick just to print your content rather than just clicking the browser's Print button. It also gives you, as the site developer (or the person hiring the developer) a much easier time to make global changes, e.g. if you want to make your site all green & red for the holidays or if maybe you decide to tweak the layout a small bit every few months to add some variety, it'll be much easier when there's one master CSS file.

While some of the table-based themes might look good, long-term you and your site will be better off with a CSS-based theme.

Multiple Collapsible Block Regions

Drupal allows you to place portions of content / functionality around your page using things it calls "blocks". Many plugins also can add even more blocks available for you to use, e.g. Tagadelic adds a block showing a tag cloud for your site's keywords, and as a developer you can really easily create your own. Blocks are positioned on your site into spaces called "regions", with most themes having a few standard regions - heading, left sidebar, top sidebar and footer.

While some have taken this flexibility and have gone completely nuts, e.g. AD Blueprint which as 44 regions, I tend to look for one with about eight regions - the usual header, footer and two sidebars, then also one above and below the page content body, plus maybe an additional one between the header and body for e.g. highlighting the week's most important content

Valid Code

In today's technologically competent world there's no excuse for sites to not be present valid code. Using the World Wide Web Consortium's HTML validator and CSS validator, every developer needs to ensure their code is tested and compliant with XHTML 1.0 Transitional ("Strict" is better) and has valid CSS code. Further, themes need to be tested to ensure approximately 99% browser compatibility, which means IE6 & IE7, Firefox and Safari 3. Thankfully most theme developers have done their best to stick with this, so your visitors should have a pleasant experience.

Hopefully the above will help you on your way to build the site of your dreams, with the CMS of your dreams :)

Pages

Subscribe to tips