<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-7330761068601183997</id><updated>2011-10-19T22:44:27.285-07:00</updated><category term='users'/><category term='url'/><category term='bootstrap.inc'/><category term='core hack'/><category term='path'/><category term='s3'/><category term='ec2'/><category term='profile fields'/><category term='Bacteria Man'/><category term='Amazon'/><category term='import'/><category term='hosting'/><category term='string overrides'/><category term='event'/><category term='module'/><category term='stewart robinson'/><category term='node'/><category term='browser'/><category term='rss'/><category term='6.6'/><category term='sun'/><category term='roles'/><category term='link'/><category term='access'/><category term='username'/><category term='ipc'/><category term='economist'/><category term='broken'/><category term='alias'/><category term='user.module'/><category term='facebook'/><category term='ipc media'/><category term='IE7'/><category term='ami'/><category term='rss feed'/><category term='array_shift'/><category term='user access'/><category term='unserialize'/><category term='mysql'/><category term='IE6'/><category term='drupal4pub'/><category term='convert'/><category term='url alias'/><category term='core'/><category term='views'/><category term='phpmyadmin'/><category term='field module'/><category term='drupal 5'/><category term='hook_user'/><category term='cck'/><category term='role'/><category term='cloud'/><category term='instance'/><category term='user'/><category term='publishing'/><category term='AWS'/><category term='user_load'/><category term='wikipedia'/><category term='permissions'/><category term='&quot;stop supporting IE6&quot;'/><category term='&quot;drop IE6 support&quot;'/><category term='digg'/><category term='search'/><category term='drupal'/><category term='server'/><category term='drupal 6'/><category term='index'/><category term='bootstrap'/><category term='publishers'/><category term='error'/><category term='6.7'/><category term='user_selectable_roles'/><category term='&quot;browser support&quot;'/><category term='problem'/><category term='profile'/><title type='text'>Chris Cohen's blog</title><subtitle type='html'>A collection of tips, tricks and observations regarding Drupal development.</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://c-cohen.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7330761068601183997/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://c-cohen.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>fw190a8</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>13</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-7330761068601183997.post-4766696376680615691</id><published>2009-06-30T15:19:00.000-07:00</published><updated>2009-06-30T15:47:16.304-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='event'/><category scheme='http://www.blogger.com/atom/ns#' term='ipc media'/><category scheme='http://www.blogger.com/atom/ns#' term='sun'/><category scheme='http://www.blogger.com/atom/ns#' term='publishers'/><category scheme='http://www.blogger.com/atom/ns#' term='stewart robinson'/><category scheme='http://www.blogger.com/atom/ns#' term='drupal4pub'/><category scheme='http://www.blogger.com/atom/ns#' term='ipc'/><category scheme='http://www.blogger.com/atom/ns#' term='drupal'/><category scheme='http://www.blogger.com/atom/ns#' term='cck'/><category scheme='http://www.blogger.com/atom/ns#' term='views'/><category scheme='http://www.blogger.com/atom/ns#' term='publishing'/><category scheme='http://www.blogger.com/atom/ns#' term='economist'/><title type='text'>Drupal for Publishers, London, 30th June 2009</title><content type='html'>I've just returned from the &lt;a href="http://drupal.org"&gt;Drupal&lt;/a&gt; for Publishers event held at &lt;a href="http://uk.sun.com"&gt;Sun&lt;/a&gt; in London. 100 attendees, including a good mix of Drupalites and potential Drupal users, were presented with a series of talks on a range of issues relating to using Drupal to create websites (and other solutions) for newspapers and magazines.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Sun were great hosts and despite the event being free to attend, a lavish array of sandwiches, fruit and cake was laid on, washed down with a selection of tea, coffee and fruit juice. The venue was great too, with a very large, clear projector, and a nice cool room in spite of the scorching weather in the City.&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;For me in particular, the most useful talk of the day was &lt;a href="http://www.stewsnooze.com"&gt;Stewart Robinson&lt;/a&gt;'s on some of the development and management techniques being used by the team redeveloping &lt;a href="http://www.economist.com"&gt;the Economist&lt;/a&gt; in Drupal. It was good to discover that even on large-scale projects, the usual Drupal problems can rear their ugly heads (everything is in the database, like &lt;a href="http://drupal.org/views"&gt;views&lt;/a&gt;, so making testing changes live can be problematic), and it was good to hear about some of the proposed solutions, such as putting everything in the code (views, content types, &lt;a href="http://drupal.org/project/cck"&gt;CCK&lt;/a&gt; field definitions, etc) which is then kept under version control, and using a contributed module to generate the database structure or records from the code.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;For a lot of smaller Drupal developments, corners are cut to save time or money, but the Economist's approach obviously has to be extremely comprehensive, including full unit testing and browser testing, but these words can often be thrown about very conceptually. Stewart presented some very tangible methods of implementing these (&lt;a href="http://drupal.org/project/simpletest"&gt;simpletest&lt;/a&gt; and &lt;a href="http://seleniumhq.org/"&gt;Selenium&lt;/a&gt;, in this case), and these are valuable insights that are often found only occasionally while scouring the personal blogs of Drupal developers.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The other talks were entertaining but most were of less value to me personally, serving as an introduction to Drupal and what its capabilities are in the online publishing world. No doubt a large proportion of the audience would have found this relevant, and I think it was good to provide a little bit that everyone could take away with them.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The penultimate talk was on the development of a solution for &lt;a href="http://www.ipcmedia.com"&gt;IPC Media&lt;/a&gt;. Although it was a Drupal solution, it showcased how, in this case, Drupal was being used for its administration and data entry interface, completely ignoring the front-end side of it. Although it was interesting to learn that this kind of thing can be achieved, the audience was then shown custom modules that could provide a graphical image uploader, selector and cropper, but told that these modules were not available to the wider community.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Unfortunately, I found this was like having to watch a man guzzle a nice cold beer after I've just crossed a desert on foot. These are certainly modules that would be very useful additions to the Drupal community, and it seems odd to bring to light the existence of such modules at an open source event and then snatch them away again behind lock and key.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Overall, I'd like to thank and commend Sun for hosting such a useful event and the individual speakers and organisers for their hard work. I think smaller events like this, as well as the twice-yearly &lt;a href="http://paris2009.drupalcon.org/"&gt;DrupalCon&lt;/a&gt;, are key to the continuity and expansion of the Drupal community.&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7330761068601183997-4766696376680615691?l=c-cohen.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://c-cohen.blogspot.com/feeds/4766696376680615691/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7330761068601183997&amp;postID=4766696376680615691' title='43 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7330761068601183997/posts/default/4766696376680615691'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7330761068601183997/posts/default/4766696376680615691'/><link rel='alternate' type='text/html' href='http://c-cohen.blogspot.com/2009/06/drupal-for-publishers-london-30th-june.html' title='Drupal for Publishers, London, 30th June 2009'/><author><name>fw190a8</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>43</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7330761068601183997.post-4668286428524708541</id><published>2009-06-10T06:52:00.000-07:00</published><updated>2009-06-10T07:01:19.006-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='user_load'/><category scheme='http://www.blogger.com/atom/ns#' term='profile fields'/><category scheme='http://www.blogger.com/atom/ns#' term='problem'/><category scheme='http://www.blogger.com/atom/ns#' term='hook_user'/><category scheme='http://www.blogger.com/atom/ns#' term='drupal 5'/><category scheme='http://www.blogger.com/atom/ns#' term='roles'/><category scheme='http://www.blogger.com/atom/ns#' term='profile'/><category scheme='http://www.blogger.com/atom/ns#' term='drupal'/><category scheme='http://www.blogger.com/atom/ns#' term='role'/><category scheme='http://www.blogger.com/atom/ns#' term='module'/><title type='text'>Drupal 5: Automatically assign a role on user profile edit</title><content type='html'>Using &lt;a href="http://drupal.org"&gt;Drupal&lt;/a&gt; 5, I recently had cause to create a system whereby when a user updates checkboxes in his or her profile, roles would automatically be assigned or unassigned. No problem, I thought, I would just use &lt;a href="http://api.drupal.org/api/function/hook_user/5"&gt;hook_user()&lt;/a&gt; to achieve this. According to the API, I would need the two $ops &lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;insert&lt;/span&gt; and &lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;update&lt;/span&gt;.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Writing the one for &lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;update&lt;/span&gt; was easy. The $account contained all the user's profile fields (the ones starting with &lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;profile_&lt;/span&gt;) and the roles could be assigned based on these (1 for add role, 0 for delete role).&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;I ran into a problem with &lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;insert&lt;/span&gt;. The $account contained the &lt;i&gt;keys&lt;/i&gt; for the profile fields, but the &lt;i&gt;values&lt;/i&gt; were all blank! I still don't really know why this is. The solution, as cumbersome as it might be, is to wait until the new user has a uid, then call &lt;a href="http://api.drupal.org/api/function/user_load/5"&gt;user_load()&lt;/a&gt; on the user, at which point the profile fields will have their proper values. Then, exactly the same method can be used as in the &lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;update&lt;/span&gt; case.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;As a footnote, we don't actively develop in Drupal 5 any more; all of our development occurs in Drupal 6, but we still support Drupal 5 sites. Here is the finished code in case anyone finds it useful:&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;pre name="code" class="php"&gt;&lt;br /&gt;/**&lt;br /&gt; * Implementation of hook_user().&lt;br /&gt; */&lt;br /&gt;function mymodule_autorole_user($op, &amp;$edit, &amp;$account, $category = NULL) {&lt;br /&gt;  if ($op == 'insert') {&lt;br /&gt;    mymodule_autorole_apply_roles($account-&gt;uid);&lt;br /&gt;  }&lt;br /&gt;  else if ($op == 'update') {&lt;br /&gt;    mymodule_autorole_apply_roles($account-&gt;uid);&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;/**&lt;br /&gt; * Takes a user account object and uses it to update the user's roles.&lt;br /&gt; * &lt;br /&gt; * @param $uid&lt;br /&gt; *    A fully populated user account object such as one returned by user_load().&lt;br /&gt; */&lt;br /&gt;function mymodule_autorole_apply_roles($uid) {&lt;br /&gt;  $account = user_load(array('uid' =&gt; $uid));&lt;br /&gt;  &lt;br /&gt;  // Filter out the profile fields from the account information.&lt;br /&gt;  $profile_fields = array();&lt;br /&gt;  &lt;br /&gt;  foreach ($account as $fieldname =&gt; $field) {&lt;br /&gt;    // Split up the field name by the underscore character. The field names we&lt;br /&gt;    // are looking for are named like profile_something, but they could be&lt;br /&gt;    // profile_something_something, so join back together after the split.&lt;br /&gt;    $pieces = explode('_', $fieldname);&lt;br /&gt;    &lt;br /&gt;    if (array_shift($pieces) == 'profile') {&lt;br /&gt;      $profile_fields[implode('_', $pieces) . ' club member'] = $field;&lt;br /&gt;      &lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  $myaccount = user_load(array('uid' =&gt; $account-&gt;uid));&lt;br /&gt;  $roles = user_roles();&lt;br /&gt;  &lt;br /&gt;  foreach ($profile_fields as $field =&gt; $value) {&lt;br /&gt;    if ($value) {&lt;br /&gt;      // The checkbox was checked, or the textfield had something in it. Add a&lt;br /&gt;      // new role corresponding to this, if there is one.&lt;br /&gt;      foreach ($roles as $key =&gt; $role) {&lt;br /&gt;        if ($role == $field) {&lt;br /&gt;          $myaccount-&gt;roles[$key] = $role;&lt;br /&gt;        }&lt;br /&gt;      }&lt;br /&gt;    }&lt;br /&gt;    else {&lt;br /&gt;      // The checkbox was unchecked, or the textfield was empty. Unset the user&lt;br /&gt;      // role corresponding to this, if there is one.&lt;br /&gt;      foreach ($roles as $key =&gt; $role) {&lt;br /&gt;        if ($role == $field) {&lt;br /&gt;          unset($myaccount-&gt;roles[$key]);&lt;br /&gt;        }&lt;br /&gt;      }&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  // Update the user with the new role assignments.&lt;br /&gt;  db_query("DELETE FROM {users_roles} WHERE uid = %d", $myaccount-&gt;uid);&lt;br /&gt;  &lt;br /&gt;  foreach ($myaccount-&gt;roles as $rid =&gt; $role) {&lt;br /&gt;    db_query("INSERT INTO {users_roles} (uid, rid) VALUES (%d, %d)", $myaccount-&gt;uid, $rid);&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7330761068601183997-4668286428524708541?l=c-cohen.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://c-cohen.blogspot.com/feeds/4668286428524708541/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7330761068601183997&amp;postID=4668286428524708541' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7330761068601183997/posts/default/4668286428524708541'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7330761068601183997/posts/default/4668286428524708541'/><link rel='alternate' type='text/html' href='http://c-cohen.blogspot.com/2009/06/drupal-5-automatically-assign-role-on.html' title='Drupal 5: Automatically assign a role on user profile edit'/><author><name>fw190a8</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7330761068601183997.post-4276120244509870225</id><published>2009-05-20T03:07:00.001-07:00</published><updated>2009-05-20T03:33:26.813-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='unserialize'/><category scheme='http://www.blogger.com/atom/ns#' term='problem'/><category scheme='http://www.blogger.com/atom/ns#' term='phpmyadmin'/><category scheme='http://www.blogger.com/atom/ns#' term='drupal 5'/><category scheme='http://www.blogger.com/atom/ns#' term='mysql'/><category scheme='http://www.blogger.com/atom/ns#' term='bootstrap'/><category scheme='http://www.blogger.com/atom/ns#' term='drupal'/><category scheme='http://www.blogger.com/atom/ns#' term='error'/><category scheme='http://www.blogger.com/atom/ns#' term='bootstrap.inc'/><title type='text'>Drupal 5: Problems with unserialize in bootstrap.inc</title><content type='html'>Today I had a problem where Drupal 5 kept reporting a problem with unserialize in bootstrap.inc on line 428.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;On closer inspection, bootstrap.inc, which is in the includes directory in the root of Drupal 5, contains a number of functions that are used when Drupal 'boots up'. The function in question here was &lt;a href="http://api.drupal.org/api/function/variable_init/5"&gt;variable_init()&lt;/a&gt;, and this is where all variables are drawn from the database. These variables are in serialized form, allowing Drupal to store anything, from objects to arrays, in string format.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;If the serialized item got corrupted somehow, it wouldn't be able to unserialize properly in variable_init(), leading to this error. My problem was that I was not able to see &lt;span class="Apple-style-span" style="font-style: italic;"&gt;which&lt;/span&gt; variables were causing the problem; only that the problem existed. With over 200 variables on the site, manually checking each one for valid serialization was not a viable option!&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;My solution was to &lt;span class="Apple-style-span" style="font-style: italic;"&gt;change&lt;/span&gt; the core bootstrap.inc to print the names of the offending variables, thereby enabling me to find them in the database and fix them. Here's the original snippet from line 427 of bootstrap.inc:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;pre name="code" class="php"&gt;&lt;br /&gt;while ($variable = db_fetch_object($result)) {&lt;br /&gt;$variables[$variable-&gt;name] = unserialize($variable-&gt;value);&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Here's what I changed it to, temporarily:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;pre name="code" class="php"&gt;&lt;br /&gt;while ($variable = db_fetch_object($result)) {&lt;br /&gt;if (($variables[$variable-&gt;name] = unserialize($variable-&gt;value)) === FALSE) {&lt;br /&gt;  print $variable-&gt;name;&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Once I had my variable names, I was able to find them in the database, using phpMyAdmin, and edit them. I found that I had something like &lt;span class="Apple-style-span" style="font-family: 'courier new';"&gt;s:5:"&lt;/span&gt; in there. This had been truncated, and should have been something like &lt;span class="Apple-style-span" style="font-family: 'courier new';"&gt;s:5:"hello"&lt;/span&gt;. The first letter indicates that the variable is a string. The number indicated how many characters the string has, and the value of the string is encapsulated within double quotes.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Afterwards, I just changed bootstrap.inc back to the way it was before, and my unserialize problems vanished!&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7330761068601183997-4276120244509870225?l=c-cohen.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://c-cohen.blogspot.com/feeds/4276120244509870225/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7330761068601183997&amp;postID=4276120244509870225' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7330761068601183997/posts/default/4276120244509870225'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7330761068601183997/posts/default/4276120244509870225'/><link rel='alternate' type='text/html' href='http://c-cohen.blogspot.com/2009/05/drupal-5-problems-with-unserialize-in.html' title='Drupal 5: Problems with unserialize in bootstrap.inc'/><author><name>fw190a8</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7330761068601183997.post-4515765282401506427</id><published>2009-02-16T14:05:00.001-08:00</published><updated>2009-02-16T14:35:34.400-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='wikipedia'/><category scheme='http://www.blogger.com/atom/ns#' term='&quot;drop IE6 support&quot;'/><category scheme='http://www.blogger.com/atom/ns#' term='&quot;stop supporting IE6&quot;'/><category scheme='http://www.blogger.com/atom/ns#' term='&quot;browser support&quot;'/><category scheme='http://www.blogger.com/atom/ns#' term='IE7'/><category scheme='http://www.blogger.com/atom/ns#' term='facebook'/><category scheme='http://www.blogger.com/atom/ns#' term='digg'/><category scheme='http://www.blogger.com/atom/ns#' term='IE6'/><category scheme='http://www.blogger.com/atom/ns#' term='browser'/><title type='text'>When will we be free from Internet Explorer 6?</title><content type='html'>Another standards-compliant site completed, another day or so consumed developing non-standard workarounds to make it work in Internet Explorer 6. I found myself asking, “why do I need to spend all this extra time deviating from standards just to accommodate one poorly-designed browser?” The unfortunate truth is always, “because over one third of all the site's visitors will be using it.”&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;When will IE6 finally die, and what will be the straw that breaks its back? Now that XP is no longer sold with new computers, and Windows Update is installing IE7 even on XP, IE6's market share decreased rapidly for a while, but now seems to have levelled off. Although its user base is decreasing, it's not decreasing at any kind of rate that would make me confident that I can stop supporting it when I develop sites.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;For anyone who isn't aware of the issue, this browser, released in 2001, fails to respect countless web standards. Of course, some standards came along after IE6 was developed, but this is just no excuse, because every other browser has been patched to accommodate this.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;What's very clear is that in the late 20th or early 21st century, a lot of businesses and corporations developed their own internal computer systems using IE5, 5.5 or 6 as a web interface. For example, a bank might have written a customer relationship system that only works with IE6 as a front end. Back then, standards were far less important to web design (because fewer browsers actively supported them all) and were far less… well… standard.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;When IE7 came along, a lot of these corporations found that their internal software, on which they had invested thousands or millions, would no longer work, and other corporations were unwilling to upgrade from Windows NT, due to the cost, and therefore could not install IE7 at all. Many workplaces are stuck with IE6. IE7 is not available and other browsers cannot be installed whatsoever.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;It might well cost a lot of money to upgrade from NT to XP, Vista or the forthcoming Windows 7, it's true. However, consider how many (wo)man hours, and therefore how much money, has been wasted unnecessarily on developing sites that work in IE6. Consider Microsoft's own 5-year support policy as the cutoff for IE6, which would be about 2006. In this 3-year period, just how much time could have been saved by developing all websites without IE6 support? This would easily outweigh the cost to businesses and corporations of updating their internal systems or operating systems.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;What would it take to finally get rid of IE6? I don't think it would take too much. One large-scale site launches a banner on every page, for IE6 users, declaring that they should upgrade their browser or the site will no longer support it in 3 months. Another follows suit. Pretty soon, enough home users are persuaded to upgrade, and enough employees, disgruntled at not being able to check their eBay auctions or Facebook profile at work, pester their employers into upgrading.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The main problem here is one of competition. Putting a banner on your site claiming that you no longer support a browser used by a third of your customers will no doubt send a large proportion of your customers to another site. No large site is going to want to commit commercial suicide like that, because the amount of money they would lose in the short term would outweigh the amount of money they waste on IE6-specific development in the long term.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Instead, the task should be put to the non-commercial websites. Social sites like &lt;a href="http://www.digg.com"&gt;digg.com&lt;/a&gt; or online tools like Google Documents. The problem here is that while the sites aren't actively selling things, they do benefit from revenue brought in by large amounts of traffic, and certainly wouldn't want to take a hit in the number of visitors. I don't believe that the immediate loss of a proportion of traffic, especially from sites that are visited by those with a higher average level of technical awareness (and therefore less likely to be using IE6), would be that great, so I think it is a feasible idea. Digg will still be popular after dropping IE6. Facebook would still be visited by millions using IE7 and other browsers (and in fact, maybe more work would actually get done at work if people couldn't pointlessly update their status every 5 minutes while there).&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Supposing one or more larger sites would actually agree to act for the greater good and commit to the outlawing of IE6, how should it be done? The idea would be to gently encourage the user to upgrade, rather than tell the user how stupid he or she is by not using a modern browser. People will respond far better if they think they are upgrading in order to get more out of their favourite site, rather than if they are insulted into upgrading. With that in mind, it could be beneficial to apply IE6-only drawbacks to using a particular site. Perhaps an eBay listing would have fewer pictures and no AJAXy interface in IE6, or perhaps the BBC would be unable to show more advanced, prettier interface elements. Whatever the case, users should be given a carrot and not a stick.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;We need to start somewhere, at some point in time, to resolve to rid the world of IE6 for good. Not because it's the root of all evil, or because Microsoft sucks and should die, but because it's genuinely sucking up millions of hours of extra development time and holding back some really creative development techniques. We've already ganged together on the whole to create some fantastic things on the Internet. &lt;a href="http://www.wikipedia.org"&gt;Wikipedia&lt;/a&gt; is a completely user-created free encyclopedia, as an example. Almost everybody in web design dislikes having to create special styles or rules for IE6, yet we seem to just accept that we're powerless to do anything about it, but that's just not the case. We should be putting on the pressure to get rid of an 8-year-old browsing relic. Are you still using the computer you bought in 2001? Would it even work nowadays, with today's Internet? It's about time we pressed a bit harder for change, and then get really creative with the web.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7330761068601183997-4515765282401506427?l=c-cohen.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://c-cohen.blogspot.com/feeds/4515765282401506427/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7330761068601183997&amp;postID=4515765282401506427' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7330761068601183997/posts/default/4515765282401506427'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7330761068601183997/posts/default/4515765282401506427'/><link rel='alternate' type='text/html' href='http://c-cohen.blogspot.com/2009/02/when-will-we-be-free-from-internet.html' title='When will we be free from Internet Explorer 6?'/><author><name>fw190a8</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7330761068601183997.post-1368783740967782448</id><published>2009-02-13T10:26:00.000-08:00</published><updated>2009-02-13T11:05:58.707-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='server'/><category scheme='http://www.blogger.com/atom/ns#' term='cloud'/><category scheme='http://www.blogger.com/atom/ns#' term='drupal'/><category scheme='http://www.blogger.com/atom/ns#' term='s3'/><category scheme='http://www.blogger.com/atom/ns#' term='ec2'/><category scheme='http://www.blogger.com/atom/ns#' term='Amazon'/><category scheme='http://www.blogger.com/atom/ns#' term='hosting'/><category scheme='http://www.blogger.com/atom/ns#' term='ami'/><category scheme='http://www.blogger.com/atom/ns#' term='instance'/><category scheme='http://www.blogger.com/atom/ns#' term='AWS'/><title type='text'>Drupal on Amazon web hosting</title><content type='html'>Cloud computing has been around for a while, but only recently have we, the general populace, had access to it. &lt;a href="http://aws.amazon.com/"&gt;Amazon&lt;/a&gt; offer one such manifestation: an environment where it's possible to set up any number of virtual dedicated servers and use them for hosting, in our case, &lt;a href="http://www.drupal.org"&gt;&lt;span class="blsp-spelling-error" id="SPELLING_ERROR_0"&gt;Drupal&lt;/span&gt;&lt;/a&gt; sites! I wanted to share some of my experiences (both good and bad) using the Amazon cloud, so you can make a better decision about whether it's right for your &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_1"&gt;Drupal&lt;/span&gt; sites.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;This is attractive compared to paying for a shared hosting account. One can never quite be sure what else is running on the system you're sharing, leading to potential performance woes. The Amazon EC2 (Elastic Compute Cloud), as they call it, appears more attractive than smaller companies offering &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_2"&gt;VPSs&lt;/span&gt; (Virtual Private Servers) because of the sheer scale of Amazon. It is unlikely to disappear tomorrow and is backed up by the excellent S3 (Simple Storage Service) for backing up data.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Having said that, Amazon solutions can be costly. At the time of writing, Amazon's most modest offering, a reasonably &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_3"&gt;specced&lt;/span&gt; dedicated machine with 1.8GB of memory, goes for 11 cents ($0.11) per hour. This doesn't sound like much, but there are 168 hours in a week, and based on the 4.3-week month, that's 722 hours per month, or about $80 per month. Writing from the UK, with the exchange rates as they are at the time of writing, this is about £55.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Still, the alternative for us was to purchase a new machine and buy some rack space (which is billable in advance, often for several months). Compared to Amazon, who bill for usage at the end of each calendar month, with no &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_4"&gt;initial&lt;/span&gt; hardware cost, the choice seemed clear.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Starting out, it's striking how little documentation there is. Concepts like elastic &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_5"&gt;IPs&lt;/span&gt;, &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_6"&gt;keypairs&lt;/span&gt; and elastic block stores are very alien, even to the average techie, and whilst there is introductory material, it feels incomplete. Since the Amazon system is fairly new, and is quite pioneering in its approach, this is understandable, but doesn't make the task any easier.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;One of the biggest surprises is that, at the time of writing, Amazon's own web interface does not allow the management of EU-based instances (virtual machines), despite allowing control over US-based ones. If there's one thing that really winds people up on this side of the Atlantic, it's that Americans (and often Canadians) are given preferential treatment with this type of thing. Nevertheless, we were soon able to locate the excellent &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_7"&gt;&lt;a href="http://developer.amazonwebservices.com/connect/entry.jspa?externalID=609"&gt;ElasticFox&lt;/a&gt;&lt;/span&gt; &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_8"&gt;firefox&lt;/span&gt; extension, which allows management of European instances, and we had our very own fresh copy of Ubuntu installed and running at the touch of a button.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;This is incredibly powerful stuff, especially because in theory, you can launch as many virtual servers as you like with the click of a button. In practice, Amazon impose sensible limits (although you can apply for more if you genuinely need them) and after all, you're paying per hour for all these machines. We found &lt;a href="http://alestic.com/"&gt;Alestic's site&lt;/a&gt; very valuable indeed. It allows you to quickly find the right Amazon Machine Image (AMI) to use when starting your system, rather than poring over a huge list. A lot of these systems come pre-installed with all the things you're likely to need. There weren't any with specific Drupal installs, but since this is only a 5-minute job (download, unzip), it wasn't too much of an issue.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;When you turn off (terminate) an instance, or the power fails at Amazon (very rare but can happen), the instance disappears completely, and so does its local storage. This is a concept alien to those unaccustomed to a VPS environment. After all, if I turn off my laptop right now, the data will be saved to the hard disk and when I start it up again, it's all there. This is not so with Amazon's hosting. The virtual machine powers down and all data stored locally, including program setting and even the operating system itself, is gone forever.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;With this in mind, it's clear that a backup solution is needed. Luckily, there are some decent tools in the &lt;a href="http://developer.amazonwebservices.com/connect/entry.jspa?externalID=368"&gt;Amazon EC2 AMI tools&lt;/a&gt; package, which is pre-installed on many of Alestic's images. The idea is that you regularly take an image of the entire machine and copy it to S3 where it can be stored safely, and permanently.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Writing a simple script to do this proved more difficult, however. Firstly, it wasn't clear from the documentation that we needed to explicitly state that we wanted to back up the data to an EU S3 bucket. Without this option, the files were sent to a US bucket, taking a very long time indeed and costing $0.17 per gigabyte (a machine image is usually at least 1GB, if not more). Secondly, the bundling process, as Amazon calls it, is less than reliable. Sometimes it would just bail for no apparent reason. Sometimes it would bundle the image and fail during the upload process, again, for no apparent reason. I personally still don't trust the automated backup script I wrote because of these shortcomings, so I find myself checking manually a lot of the time, which diminishes the value of an automated system.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The next concept that was slightly alien was the Elastic Block Store (EBS), which is a system whereby it's possible to create virtual hard disks and mount them to your instances. This is much better than storing files on the instance itself, because if the instance dies, your data are safe. It's possible to take what Amazon calls snapshots of the volumes, enabling a simple backup system, and again, this process can be automated, but you will need to know your way around a shell script, since this is not a point-and-click affair.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The EBS makes it easy to split data into different volumes (database volume, websites volume, miscellaneous volume, etc). Initially we wanted to run MySQL and Apache on the same low-traffic system to see how good Amazon really was, but we always wanted the ability to migrate MySQL to a dedicated machine at a later date. It's a doddle with Amazon: you can simply unmount the database EBS volume from one machine and mount it to another.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;We have used the EBS to store some of our more persistant configuration settings too, such as Apache configuration, Apache log files and configurations for the awesome &lt;a href="http://www.nagios.org"&gt;Nagios&lt;/a&gt; monitoring system.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;To administer these Amazon systems, shell access is needed at a minimum. Unlike other systems where it's possible to simply connect on port 22, Amazon uses a keypair system. Each instance must be created with a specific keypair, and then a key must be downloaded and used with the terminal application (such as &lt;a href="http://www.chiark.greenend.org.uk/~sgtatham/putty/"&gt;PuTTY&lt;/a&gt;) before it will allow you to connect. Terminal is nice and all, but sometimes it's useful to do more with the system, like have multiple terminals open or use a GUI tool (like &lt;a href="http://dev.mysql.com/downloads/gui-tools/5.0.html"&gt;MySQL Administrator&lt;/a&gt;). For this, we set up &lt;a href="http://www.nomachine.com/download-client-windows.php"&gt;NX&lt;/a&gt;, which is similar to VNC in that it provides an interface to the remote machine's desktop that you can use exactly as though it were your own desktop. We found a &lt;a href="http://groups.google.com/group/ec2ubuntu/browse_thread/thread/4f3ee728c5eff36d?pli=1"&gt;Google Groups article&lt;/a&gt; by Eric Hammond very useful in setting up NX, and thought it was preferrable to VNC because of the default encryption method and insistance on avoiding the root user.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Performance-wise, our Drupal machine has been running Apache 2, MySQL 5, and a host of monitoring software (Munin, Nagios and AWStats) so that we can keep an eye on things, and it has been running for around a month so far with no outages, crashes or other problems at all. The learning curve is pretty steep and the documentation is fairly sparse, but there is a very active community out there on the AWS forums and places like Google Groups. Overall we are very impressed with Amazon as a Drupal hosting environment and although not entirely convinced at the current time, will be looking towards moving more and more sites over there in the future.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7330761068601183997-1368783740967782448?l=c-cohen.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://c-cohen.blogspot.com/feeds/1368783740967782448/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7330761068601183997&amp;postID=1368783740967782448' title='12 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7330761068601183997/posts/default/1368783740967782448'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7330761068601183997/posts/default/1368783740967782448'/><link rel='alternate' type='text/html' href='http://c-cohen.blogspot.com/2009/02/drupal-on-amazon-web-hosting.html' title='Drupal on Amazon web hosting'/><author><name>fw190a8</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>12</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7330761068601183997.post-828078674682463448</id><published>2008-12-09T15:53:00.000-08:00</published><updated>2008-12-09T16:15:54.949-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='users'/><category scheme='http://www.blogger.com/atom/ns#' term='core'/><category scheme='http://www.blogger.com/atom/ns#' term='user'/><category scheme='http://www.blogger.com/atom/ns#' term='drupal'/><category scheme='http://www.blogger.com/atom/ns#' term='core hack'/><category scheme='http://www.blogger.com/atom/ns#' term='drupal 6'/><category scheme='http://www.blogger.com/atom/ns#' term='username'/><category scheme='http://www.blogger.com/atom/ns#' term='user.module'/><category scheme='http://www.blogger.com/atom/ns#' term='string overrides'/><category scheme='http://www.blogger.com/atom/ns#' term='6.6'/><category scheme='http://www.blogger.com/atom/ns#' term='6.7'/><category scheme='http://www.blogger.com/atom/ns#' term='module'/><title type='text'>Drupal 6: Core hacking for the simplest of things</title><content type='html'>I was recently faced with a problem concerning changing something in &lt;a href="http://drupal.org/"&gt;Drupal&lt;/a&gt;'s core user module. This wasn't even a big change; it was as simple as changing the line of text that appears underneath the username field on the registration form. You know, the one that helps the user to choose an appropriate username.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The core has no option for changing this text, so the most obvious and worst thing possible is to open up &lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;user.module&lt;/span&gt; in the core and edit the string. This is bad because the first rule of Drupal club is do not hack the core. Actually, the second rule of Drupal club is... you get the idea. It would mean that when Drupal is upgraded, the change will disappear.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;After ruling out the above method, I was faced with a number of opportunities to accomplish what I needed, but they all came with downsides.&lt;/div&gt;&lt;br /&gt;&lt;h3&gt;Override the core&lt;/h3&gt;&lt;br /&gt;&lt;div&gt;This involves making a copy of the core user module inside my site's modules folder. Drupal will then use the copy instead of the one in the core and changes can be made safely to this copy, because it is not going to be upgraded when a new version of Drupal is released, unlike the core itself.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;My main concern here is that technically it's a core hack in disguise. In my case, I would be making a copy of Drupal 6.6's user module and then changing it. But, what happens when Drupal gets upgraded to 6.7? The core user module will be 6.7, but the site won't be using it. Any new or changed functionality in the user module will be missing from the site, and worse, this could introduce incompatibilities.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Suppose something has changed in 6.7's user module, and the other core modules expect this change to be in place? This would break Drupal, because it's not expecting to encounter the 6.6 user module here. How likely is this to happen? I'd say very unlikely in an upgrade from 6.6 to 6.7, but still possible. More than that, it's far more likely to happen once 6.7 becomes 6.8, 6.9 and so forth.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;h3&gt;Locale&lt;/h3&gt;&lt;br /&gt;&lt;div&gt;Drupal's translation system makes it possible to provide a translation string for the text underneath the username field, as though it were being translated into French or Italian, but this feels wrong. It's not a ‘real’ translation because we just want to change the string itself, not change it into another language.&lt;/div&gt;&lt;br /&gt;&lt;h3&gt;String Overrides&lt;/h3&gt;&lt;br /&gt;&lt;div&gt;There exists a module called &lt;a href="http://drupal.org/project/stringoverrides"&gt;string overrides&lt;/a&gt;. It provides a way of changing any text that is passed through the &lt;a href="http://api.drupal.org/api/function/t/6"&gt;t()&lt;/a&gt; function, by saving these translations in the variables table. This seemed like an ideal solution at first, but on closer examination, there are a few drawbacks.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Firstly, since the overrides are stored in the database, we lose version control, which is never a good thing. Secondly, the more modules you add to a site, the more overhead there is, both in terms of memory and in terms of processing times.&lt;/div&gt;&lt;br /&gt;&lt;h3&gt;Custom module&lt;/h3&gt;&lt;br /&gt;&lt;div&gt;It would be possible to write a custom module specifically for the site in question. The module would mimic the functionality of the string overrides module, but store the overrides in a file instead of the database, thereby providing version control. The only slight downside to this is that it requires a lot more time than the other approaches. It seems like extreme overkill to have to write an entire module simply to change one string.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;h3&gt;To conclude&lt;/h3&gt;&lt;br /&gt;&lt;div&gt;In the end we decided to go with the core override method. I am not entirely happy with this, but I am sure I would not have been entirely happy with any of the solutions I've presented here. I can acknowledge and understand that there are many compromises to be made on the way to a completely custom Drupal site, but there is always room for improving Drupal to accommodate many of the more common requests. The questions are: is this one of the more common requests? and: what is the best way forward here?&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7330761068601183997-828078674682463448?l=c-cohen.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://c-cohen.blogspot.com/feeds/828078674682463448/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7330761068601183997&amp;postID=828078674682463448' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7330761068601183997/posts/default/828078674682463448'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7330761068601183997/posts/default/828078674682463448'/><link rel='alternate' type='text/html' href='http://c-cohen.blogspot.com/2008/12/drupal-6-core-hacking-for-simplest-of.html' title='Drupal 6: Core hacking for the simplest of things'/><author><name>fw190a8</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7330761068601183997.post-8982773276494731789</id><published>2008-11-12T02:21:00.000-08:00</published><updated>2008-11-12T02:42:28.777-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='drupal 5'/><category scheme='http://www.blogger.com/atom/ns#' term='drupal'/><category scheme='http://www.blogger.com/atom/ns#' term='cck'/><category scheme='http://www.blogger.com/atom/ns#' term='field module'/><category scheme='http://www.blogger.com/atom/ns#' term='error'/><category scheme='http://www.blogger.com/atom/ns#' term='drupal 6'/><category scheme='http://www.blogger.com/atom/ns#' term='array_shift'/><category scheme='http://www.blogger.com/atom/ns#' term='convert'/><title type='text'>Drupal: Pitfalls when converting CCK field modules from Drupal 5 to Drupal 6</title><content type='html'>I recently needed to convert a CCK field module from &lt;a href="http://drupal.org/"&gt;Drupal&lt;/a&gt; 5 to Drupal 6. There is a &lt;span class="Apple-style-span" style="font-style: italic;"&gt;lot&lt;/span&gt; to take in, but I was faced with a particular problem:&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;warning: array_shift() [function.array-shift]: The argument should be an array in D:\wamp\www\drupal6\includes\form.inc on line 1320.&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Oh dear. It's always difficult to debug this kind of thing because the problem lies in the code that called the function on line 1320 of form.inc, rather than there being a problem with form.inc. I used &lt;a href="http://php.net/debug_backtrace"&gt;debug_backtrace()&lt;/a&gt; to see what parameters the previous functions in the call stack were using, and noticed that only the first two parameters in &lt;a href="http://api.drupal.org/api/function/_form_set_value/6"&gt;_form_set_value()&lt;/a&gt; were populated; the other two were NULL. This was the immediate source of the error. The following line was failing because &lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;$parents&lt;/span&gt; was NULL:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;pre name="code" class="php"&gt;$parent = array_shift($parents);&lt;br /&gt;&lt;/pre&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Obviously you can't &lt;a href="http://php.net/array_shift"&gt;array_shift()&lt;/a&gt; NULL. I then went back further in the backtrace, to the &lt;a href="http://api.drupal.org/api/function/form_set_value/6"&gt;form_set_value()&lt;/a&gt; function (note the function is not preceded by an underscore, like the last one). In this function, the second parameter (&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;$value&lt;/span&gt;) was NULL. This was causing the NULL in the _form_set_value() function.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-size:large;"&gt;&lt;span class="Apple-style-span" style="font-weight: bold;"&gt;The solution&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;It turns out that I was using this code to handle the form widget's processing in my CCK field module:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;pre name="code" class="php"&gt;/**&lt;br /&gt;* Process the mcimage element.&lt;br /&gt;*/&lt;br /&gt;function mcimage_mcimage_process($element, $edit, $form_state, $form) {&lt;br /&gt; $field_name = $element['#field_name'];&lt;br /&gt; $field = $form['#field_info'][$field_name];&lt;br /&gt; $field_key = $element['#columns'][0];&lt;br /&gt; $value = isset($element['#value'][$field_key]) ? $element['#value'][$field_key] : '';&lt;br /&gt;&lt;br /&gt; $element[$field_key] = array(&lt;br /&gt;   '#type'           =&gt; 'hidden',&lt;br /&gt;   '#default_value'  =&gt; $value,&lt;br /&gt;   // The following values were set by the content module and need&lt;br /&gt;   // to be passed down to the nested element.&lt;br /&gt;   '#title' =&gt; $element['#title'],&lt;br /&gt;   '#description' =&gt; $element['#description'],&lt;br /&gt;   '#required' =&gt; $element['#required'],&lt;br /&gt;   '#field_name' =&gt; $element['#field_name'],&lt;br /&gt;   '#type_name' =&gt; $element['#type_name'],&lt;br /&gt;   '#delta' =&gt; $element['#delta'],&lt;br /&gt;   '#columns' =&gt; $element['#columns'],&lt;br /&gt; );&lt;br /&gt;}&lt;/pre&gt;&lt;div&gt;My mistake? The function does not return anything! It was as simple as adding a return statement at the end of the function, so that the form element could be processed correctly:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;pre name="code" class="php"&gt;/**&lt;br /&gt; * Process the mcimage element.&lt;br /&gt; */&lt;br /&gt;function mcimage_mcimage_process($element, $edit, $form_state, $form) {&lt;br /&gt;  $field_name = $element['#field_name'];&lt;br /&gt;  $field = $form['#field_info'][$field_name];&lt;br /&gt;  $field_key = $element['#columns'][0];&lt;br /&gt;  $value = isset($element['#value'][$field_key]) ? $element['#value'][$field_key] : '';&lt;br /&gt;  &lt;br /&gt;  $element[$field_key] = array(&lt;br /&gt;    '#type'           =&gt; 'hidden',&lt;br /&gt;    '#default_value'  =&gt; $value,&lt;br /&gt;    // The following values were set by the content module and need&lt;br /&gt;    // to be passed down to the nested element.&lt;br /&gt;    '#title' =&gt; $element['#title'],&lt;br /&gt;    '#description' =&gt; $element['#description'],&lt;br /&gt;    '#required' =&gt; $element['#required'],&lt;br /&gt;    '#field_name' =&gt; $element['#field_name'],&lt;br /&gt;    '#type_name' =&gt; $element['#type_name'],&lt;br /&gt;    '#delta' =&gt; $element['#delta'],&lt;br /&gt;    '#columns' =&gt; $element['#columns'],&lt;br /&gt;  );&lt;br /&gt;  &lt;br /&gt;  return $element;&lt;br /&gt;}&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7330761068601183997-8982773276494731789?l=c-cohen.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://c-cohen.blogspot.com/feeds/8982773276494731789/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7330761068601183997&amp;postID=8982773276494731789' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7330761068601183997/posts/default/8982773276494731789'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7330761068601183997/posts/default/8982773276494731789'/><link rel='alternate' type='text/html' href='http://c-cohen.blogspot.com/2008/11/drupal-pitfalls-when-converting-cck.html' title='Drupal: Pitfalls when converting CCK field modules from Drupal 5 to Drupal 6'/><author><name>fw190a8</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7330761068601183997.post-6668357114347767524</id><published>2008-11-06T18:06:00.000-08:00</published><updated>2008-11-06T19:28:48.948-08:00</updated><title type='text'>Drupal: Creating the perfect production environment</title><content type='html'>For small-time developments, it's simple: buy an account with a &lt;a href="http://drupalhosting.net/"&gt;Drupal hosting&lt;/a&gt; provider, set up your single install, then create the site. This is a live server setup and is probably the simplest way to do things. While this works fine for simple sites that can be thrown together in hours, what is the best way to create an environment in which many sites can be independently designed, created, tested and hosted, while keeping track of large-scale developments? I want to share the method I use. It's not perfect by any means, but hopefully it will be useful to someone, and maybe you'll let me in on how you do things, too.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-size:x-large;"&gt;&lt;span class="Apple-style-span" style="font-weight: bold;"&gt;Version Control&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-size:medium;"&gt;Version control is essential for medium-scale projects upwards and projects where multiple individuals collaborate. Most people have experienced a situation where hours have been spent making a particular change, only to find that it's a mistake, and things need to be changed back to the way they were. A living nightmare under normal circumstances; a two-minute job with version control.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-size:medium;"&gt;Also, I think a significant number of people are familiar with a situation where multiple individuals have edited the same file, overwriting one another's changes and generally becoming frustrated at the lost time and effort. Difficult to avoid under normal circumstances; a breeze with version control.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;I use &lt;a href="http://subversion.tigris.org/"&gt;Subversion&lt;/a&gt; for version control, and use the excellent &lt;a href="http://tortoisesvn.tigris.org/"&gt;TortoiseSVN&lt;/a&gt; for Windows as a client. There is a learning curve involved: for the most part, terms like &lt;span class="Apple-style-span" style="font-style: italic;"&gt;checkout&lt;/span&gt;, &lt;span class="Apple-style-span" style="font-style: italic;"&gt;merge&lt;/span&gt;, &lt;span class="Apple-style-span" style="font-style: italic;"&gt;branch&lt;/span&gt;, and &lt;span class="Apple-style-span" style="font-style: italic;"&gt;tag&lt;/span&gt; will be less than obvious at first, but the ability to easily go back to any version of the file you're working on is soon indispensible and I often wonder how I ever did without it. I wish more things could come with version control. Maybe I've decided I don't like the redecoration I've done in the kitchen. It would be great if I could just revert it at the touch of a button!&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Beyond this, version control allows you to keep logs for each &lt;span class="Apple-style-span" style="font-style: italic;"&gt;commit&lt;/span&gt; (each time something is changed) so you know &lt;span class="Apple-style-span" style="font-style: italic;"&gt;why&lt;/span&gt; things were altered, and even to &lt;span class="Apple-style-span" style="font-style: italic;"&gt;blame&lt;/span&gt; each line in the file on the person who committed it, so when you see that line 134 in style.css is messing up your entire page layout, you know &lt;span class="Apple-style-span" style="font-style: italic;"&gt;who&lt;/span&gt; wrote it, &lt;span class="Apple-style-span" style="font-style: italic;"&gt;when&lt;/span&gt; it was written, and &lt;span class="Apple-style-span" style="font-style: italic;"&gt;why&lt;/span&gt;.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The Subversion server I use is its own entity: it is an &lt;a href="http://www.ubuntu.com/"&gt;Ubuntu&lt;/a&gt; box that exists purely to serve version-controlled files from the repositories. The repositories themselves are not even on the Subversion server; instead, they're on a NAS (network-attached storage) device, so if the Subversion server dies, the repositories can be retrieved and used with a replacement server. Of course, the repositories are backed up too, because if the NAS device fails and there is no backup, it would be catastrophic.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-size:x-large;"&gt;&lt;span class="Apple-style-span" style="font-weight: bold;"&gt;Individual Development&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;Each individual working on Drupal projects has a &lt;a href="http://www.wampserver.com/en/"&gt;WAMP&lt;/a&gt; server on his or her local machine. Drupal is installed here and runs under &lt;a href="http://httpd.apache.org/"&gt;Apache 2&lt;/a&gt;. One important decision here is that there is only a single Drupal installation running multiple sites. These sites reside in subdirectories in the &lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;sites&lt;/span&gt; folder, and each site has its own repository.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Site-specific modules are installed in the site's own &lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;modules&lt;/span&gt; folder, whereas modules that will be used on all or most sites (such as &lt;a href="http://drupal.org/project/cck"&gt;cck&lt;/a&gt;, &lt;a href="http://drupal.org/project/webform"&gt;webform&lt;/a&gt;, etc) are kept in the &lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;sites/all/modules&lt;/span&gt; folder, which means there is just one codebase for each of these modules. This folder is kept in its own repository, so when modules are updated, changes are committed and a tag is created.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;This means that only one member of the production team handles module updates. The others simply update their working copy of the &lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;sites/all/modules&lt;/span&gt; repository and everything is up to date.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-size:x-large;"&gt;&lt;span class="Apple-style-span" style="font-weight: bold;"&gt;Potential Problems with a Shared Environment&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;The alternative to having developers work locally on their own machines is to have a shared development server with something like a &lt;a href="http://en.wikipedia.org/wiki/Samba_(software)"&gt;Samba&lt;/a&gt; share that the production team use to access and edit the files. The main drawback with this approach is that it's all too easy to have two individuals editing the same file at the same time, potentially overwriting one another's changes. The &lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;style.css&lt;/span&gt; and &lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;template.php&lt;/span&gt; files in Drupal themes are especially vulnerable.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Using local copies and version control all but eliminates this kind of thing. Let's say two people have edited &lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;style.css&lt;/span&gt;. Person A makes her changes, updates her working copy (this should always be done before committing) and sees that there are no changes, so she commits. Person B makes his changes, and updates his working copy. Assuming that A and B have edited &lt;span class="Apple-style-span" style="font-style: italic;"&gt;different lines&lt;/span&gt; in the file, person B will see that person A's changes have been &lt;span class="Apple-style-span" style="font-style: italic;"&gt;merged&lt;/span&gt; into his changes, so when he commits, the combined efforts of both will be saved.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;If A and B have edited the &lt;span class="Apple-style-span" style="font-style: italic;"&gt;same lines&lt;/span&gt; in the file, when person B updates his copy, he will see that there has been a conflict, but will be offered both versions of the file and &lt;span class="Apple-style-span" style="font-style: italic;"&gt;shown&lt;/span&gt; the exact lines that are conflicting. He can then select which side of the conflict ‘wins’, or if neither will be correct, he can alter the conflicting line to incorporate both edits. Nothing will get overwritten unless B wants it to be.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-weight: bold;"&gt;&lt;span class="Apple-style-span"  style="font-size:x-large;"&gt;Development and Testing Server&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;When changes are ready to show to a client, a dedicated development and testing server is used. The working copy on this system is simply updated, and the client is given a special development URL on which to view the work that has been carried out.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-size:x-large;"&gt;&lt;span class="Apple-style-span" style="font-weight: bold;"&gt;Shared Development Database&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;When I said that the production team work locally on their own machines using WAMP, this is true for the site's files, but not for the database. It's not possible to keep a database under version control, so consider this:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Person A is working locally with a local database and creates a node. There were 10 nodes already, so this is node 11. Person B is working locally with a local database too, and is working on a different aspect of the site. B creates a node too, but in B's database, this is node 11. Therefore, node 11 exists in two places, and is actually two separate nodes. Now imagine this kind of thing happening quite often as various team members work on different bits of the site.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;When it comes time to merge all these databases together to create a final testing version of the site, all hell breaks loose. It simply isn't possible to automatically handle situations where the same ID in two different databases refers to two different things.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;For this reason, I use a &lt;a href="http://www.mysql.com/"&gt;MySQL&lt;/a&gt; server on the development and testing system as the ‘central’ development system. Each individual is running his or her own copy of Drupal and has his or her own copy of the files, but utilises the &lt;span class="Apple-style-span" style="font-style: italic;"&gt;same&lt;/span&gt; database. That way, when person A adds a new node, view, user or whatever else, it is instantly visible on person B's copy of the site too.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;There are problems with this approach, for sure. Suppose person A has created a view with some templates. The view exists in the database, so it is visible on all of the production team's copies. Person A has not yet committed the templates to the Subversion repository, so they don't exist on person B's copy of the site. The view will almost certainly look very odd to person B until the templates are committed.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Another situation can arise where modules differ. Suppose person A is using a special module on the site to import some content. Once it's in the database, the module can be removed, and nobody else on the production team needs to do anything, because they're all sharing the same database. The module implements &lt;a href="http://api.drupal.org/api/function/hook_menu/6"&gt;hook_menu()&lt;/a&gt; to provide an import page where settings can be chosen. When the module is installed on person A's copy of the site, the menu cache is updated to include the path to this import page.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;All is well so far, but suppose that person B clears the site's cache for some reason. The import module does not exist on B's copy of the site, so the regenerated menu cache will not include the path to the import page any more. Person A will suddenly find that the menu option for the import page has disappeared! Luckily this is easily resolved (person A simply needs to clear the cache again) but this shows the type of unexpected event that might arise as a result of sharing a Drupal database across multiple copies of the site.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;With all that in mind, the benefits of sharing a single database far outweigh the drawbacks, so this is my preferred method, rather than having multiple copies of the database and attempting to merge them together.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-size: x-large;"&gt;&lt;span class="Apple-style-span" style="font-weight: bold;"&gt;Multisite Hostname Problems&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;One big step I had to take in getting to grips with the multisite approach combined with each member of the production team running their own copy of the site was overcoming the problems associated with Drupal's handling of sites in a multisite environment.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;When using a single Drupal install and single copy of Apache for multiple sites, all of Apache's virtual hosts use the Drupal install as their document root, and Drupal selects the appropriate site (and therefore the database, via &lt;span class="Apple-style-span" style="font-family: 'courier new';"&gt;settings.php&lt;/span&gt;) by examining the hostname.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;For example, suppose we want to run two sites on the same copy of Drupal: www.foo.com and www.bar.com. In Drupal's &lt;span class="Apple-style-span" style="font-family: 'courier new';"&gt;sites&lt;/span&gt; folder, we create one folder called &lt;span class="Apple-style-span" style="font-family: 'courier new';"&gt;foo.com&lt;/span&gt; and one called &lt;span class="Apple-style-span" style="font-family: 'courier new';"&gt;bar.com&lt;/span&gt;. When a user's browser makes a request for www.foo.com, Apache handles this request, serves the document from the Drupal install folder (with Drupal, it's almost always &lt;span class="Apple-style-span" style="font-family: 'courier new';"&gt;index.php&lt;/span&gt;), and Drupal knows that because the request asked for www.foo.com, it should use the &lt;span class="Apple-style-span" style="font-family: 'courier new';"&gt;foo.com&lt;/span&gt; site and not the &lt;span class="Apple-style-span" style="font-family: 'courier new';"&gt;bar.com&lt;/span&gt; one.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;If we apply this to our development environment, our main development and testing server (example.com, for argument's sake) can be accessed from either foo.example.com or bar.example.com, to see each site's development and testing copy. Great, but how does person A access her local copy of the site? She could add an entry to her &lt;a href="http://en.wikipedia.org/wiki/Hosts_file"&gt;hosts file&lt;/a&gt; to redirect foo.example.com to 127.0.0.1, but then how would she see the copy on the development and testing server? She would have to remove or comment the hosts file entry, which will get confusing, because it will be easy to forget which version of the site she is looking at during any given moment.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Luckily, there's a better way. The folder &lt;span class="Apple-style-span" style="font-family: 'courier new';"&gt;sites/foo.example.com&lt;/span&gt; exists on the development and testing server and also on person A's local copy. Person A alters the Apache virtual host entry for her local machine from &lt;span class="Apple-style-span" style="font-style: italic;"&gt;foo.example.com&lt;/span&gt; to &lt;span class="Apple-style-span" style="font-style: italic;"&gt;local.foo.example.com&lt;/span&gt;. She then adds an entry into the hosts file, directing &lt;span class="Apple-style-span" style="font-style: italic;"&gt;local.foo.example.com&lt;/span&gt; to &lt;span class="Apple-style-span" style="font-style: italic;"&gt;127.0.0.1&lt;/span&gt;. Now, she can access her local copy by putting &lt;span class="Apple-style-span" style="font-style: italic;"&gt;local.foo.example.com&lt;/span&gt; into her browser, and the development and testing copy by using &lt;span class="Apple-style-span" style="font-style: italic;"&gt;foo.example.com,&lt;/span&gt; all the while keeping the structure of the &lt;span class="Apple-style-span" style="font-family: 'courier new';"&gt;sites&lt;/span&gt; folder the same in both places.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-size: x-large;"&gt;&lt;span class="Apple-style-span" style="font-weight: bold;"&gt;Keeping up with Site Files&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;Quite often, clients will want to populate their new sites with stories and articles before the site launches. Sometimes, I will want to give them this privilege before all areas of the site are finished. A situation arises where the client has uploaded files such as thumbnails or PDFs. These exist on the development and testing server, but not on any of the local copies used by the production team.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Luckily, &lt;a href="http://en.wikipedia.org/wiki/SyncToy"&gt;SyncToy&lt;/a&gt; can help with this. Simply create a folder pair that echoes the development and testing server's files to the local system. Each time any member of the production team notices that their local copy is missing some thumbnails or other files, he or she can just synchronise the folder pair to receive all of the latest files. Alternatively, a schedule can be created to do this automatically at regular intervals.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Of course, this will not help in situations where the production team has uploaded files to their local machines that need copying to the development and testing server, but this is probably going to happen much less frequently, and there is always the option of synchronising both ways for this eventuality.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-size: x-large;"&gt;&lt;span class="Apple-style-span" style="font-weight: bold;"&gt;Live Server Setup&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;The live sites run on a single Drupal install on a dedicated Apache server, while the live databases run on a separate system that does nothing but run databases (and has MySQL in dedicated mode for best performance). I would always recommend separating these functions out on medium loads upwards, so that Apache does not slow MySQL and MySQL does not slow Apache.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;I have not seen any viable clustering solutions for hosting Drupal sites, but luckily I am in a position where all the sites can be run from one web server and one database server. Even so, I would be interested to hear about any successful clusters out there.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-weight: bold;"&gt;&lt;span class="Apple-style-span" style="font-size: x-large;"&gt;Amending the Live Site&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;Unfortunately, making amendments to the live site is probably the biggest hurdle left in this particular production environment. If there is a rogue CSS class, that's fine. The change is made on a production team member's local system, and tested. The change is committed, then the development and testing server is updated, so that it receives this change too. The change is tested there. Assuming it is ready to be made live, the live server is updated and everyone is happy.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The real problem comes when, for example, a new node needs to be added. Suppose that the client has asked for a new webform. First, the live database is copied down to the development and testing server. A member of the production team produces the webform on her local system. This shares the database with the development and testing server, so unless template or CSS changes are required, the job is done and it can be tested.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The problem is that suppose there were 100 nodes before the work began. On the development and testing database, the webform is node 101. Now suppose that in the time taken to create and test the webform, 5 nodes have been created on the live site. The development and testing database cannot simply be copied back over, because this would wipe those 5 nodes!&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;One solution is to put the live site into maintenance mode while the webform is created. This way, the database will be locked off to the general public, so it can be copied down to development and testing, worked upon, then copied back up without the risk of wiping content.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;This might be fine for a 5-minute job, but what if the work requires 4 hours? The site cannot very well be put into maintenance mode for 4 hours during the working day just so a small change can be made!&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The answer at the moment seems to be to write everything down. Write down the settings you use to create the new webform. Once it has been tested and is ready to go live, just create the webform again on the live server. This is not sophisticated or classy in any way, but appears to be the best option.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-size: x-large;"&gt;&lt;span class="Apple-style-span" style="font-weight: bold;"&gt;Your Input&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;I would be interested to hear thoughts on the way this particular production environment is set up and how it might be improved, especially where I have noted problems. I'd also love to hear how you do things and why, because I think I learn a lot from other people's experiences.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7330761068601183997-6668357114347767524?l=c-cohen.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://c-cohen.blogspot.com/feeds/6668357114347767524/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7330761068601183997&amp;postID=6668357114347767524' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7330761068601183997/posts/default/6668357114347767524'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7330761068601183997/posts/default/6668357114347767524'/><link rel='alternate' type='text/html' href='http://c-cohen.blogspot.com/2008/11/drupal-creating-perfect-production.html' title='Drupal: Creating the perfect production environment'/><author><name>fw190a8</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7330761068601183997.post-5000425766385435704</id><published>2008-11-06T10:20:00.000-08:00</published><updated>2008-11-06T10:57:36.748-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='drupal 5'/><category scheme='http://www.blogger.com/atom/ns#' term='users'/><category scheme='http://www.blogger.com/atom/ns#' term='profile'/><category scheme='http://www.blogger.com/atom/ns#' term='user'/><category scheme='http://www.blogger.com/atom/ns#' term='drupal'/><title type='text'>Drupal 5: user_save and profile fields</title><content type='html'>I was recently required to import a large number of users into a &lt;a href="http://www.drupal.org/"&gt;Drupal&lt;/a&gt; 5 site, so I wrote a simple import module to take rows from a CSV file and pass them to &lt;a href="http://api.drupal.org/api/function/user_save/5"&gt;user_save()&lt;/a&gt;. In addition to the basic user information in the &lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;{users}&lt;/span&gt; table, I needed to create several profile fields too. This was incredibly complicated, but probably shouldn't have been.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The first thing I noticed is that the documentation for user_save() is not exactly stellar.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;span class="Apple-style-span" style="font-style: italic;"&gt;$account The $user object for the user to modify or add. If $user-&gt;uid is omitted, a new user will be added.&lt;/span&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-style: italic;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;div&gt;Fine, but is this where I should be putting my new user's information, or perhaps I should use the next parameter?&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-style: italic;"&gt;$array An array of fields and values to save. For example array('name' =&gt; 'My name'); Setting a field to NULL deletes it from the data column.&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-style: italic;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;Ok, fine. Maybe I should use this one for adding my new user data. This doesn't mention anything about the profile fields though, or explain what I should be doing with the $account parameter. Maybe there's something else?&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;span class="Apple-style-span" style="font-style: italic;"&gt;$category (optional) The category for storing profile information in.&lt;/span&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;What? Category? Now I'm really confused. There's nothing obvious in user_save() that suggests how the profile fields get saved, or even where to put the profile fields. My only real clue is the call to &lt;a href="http://api.drupal.org/api/function/user_module_invoke/5"&gt;user_module_invoke()&lt;/a&gt; towards the end of the function. This calls hook_user() in all the active modules on the site, and the one I'm interested in is the profile module, so my next stop was &lt;a href="http://api.drupal.org/api/function/profile_user/5"&gt;profile_user()&lt;/a&gt;. In turn, this calls &lt;a href="http://api.drupal.org/api/function/profile_save_profile/5"&gt;profile_save_profile()&lt;/a&gt; with the details from the original call to user_save().&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;It was at this stage that I noticed that $category must refer to the various groups that profile information can be put in. For example, you can create a category for personal information, and one for notification preferences, and doing so will split the fields onto different tabs when the user edits his or her profile. Unfortunately, $category is a string, not an array, so for each call to profile_save_profile(), only one category can be changed.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Because profile_save_profile() is only called once per user_save(), it appears that when creating a user, it is only possible to create profile fields in one group! This causes a problem for me because I needed to import lots of profile fields in several groups.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;My solution was to temporarily move all the profile fields into a single group. Once I had done that, I could populate $array with the information destined for the &lt;span class="Apple-style-span" style="font-family: 'courier new';"&gt;{users}&lt;/span&gt; table &lt;span class="Apple-style-span" style="font-style: italic;"&gt;and&lt;/span&gt; the profile fields (this was not documented anywhere). It turns out I could just use NULL for $account (again, this was not documented).&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Surely this is not the ideal way of creating new users programatically. My solution does work, but it is annoying and time-consuming. Is there another way to create users with profile fields in multiple categories?&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7330761068601183997-5000425766385435704?l=c-cohen.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://c-cohen.blogspot.com/feeds/5000425766385435704/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7330761068601183997&amp;postID=5000425766385435704' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7330761068601183997/posts/default/5000425766385435704'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7330761068601183997/posts/default/5000425766385435704'/><link rel='alternate' type='text/html' href='http://c-cohen.blogspot.com/2008/11/drupal-5-usersave-and-profile-fields.html' title='Drupal 5: user_save and profile fields'/><author><name>fw190a8</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7330761068601183997.post-1363655657796857460</id><published>2008-11-04T07:31:00.000-08:00</published><updated>2008-11-06T10:20:07.003-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='url alias'/><category scheme='http://www.blogger.com/atom/ns#' term='drupal 5'/><category scheme='http://www.blogger.com/atom/ns#' term='node'/><category scheme='http://www.blogger.com/atom/ns#' term='drupal'/><category scheme='http://www.blogger.com/atom/ns#' term='broken'/><category scheme='http://www.blogger.com/atom/ns#' term='link'/><category scheme='http://www.blogger.com/atom/ns#' term='alias'/><category scheme='http://www.blogger.com/atom/ns#' term='path'/><title type='text'>Drupal 5: Don't rely on the node's path attribute</title><content type='html'>I recently noticed that some of the nodes on a site I was working on were not linking properly from the teaser to the full node view. It turns out that the hyperlink tag's href was empty. Checking the template for the node teaser, I found that it was using this:&lt;br /&gt;&lt;br /&gt;&lt;pre name="code" class="php"&gt;&lt;br /&gt;print l($node-&gt;title, $node-&gt;path);&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;This appears to work fine for nodes with a path alias set up (via the path or pathauto modules), but not for other nodes, because the &lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;$node-&gt;path&lt;/span&gt; part was empty. I believe the following code should have been used, to always start with the base URL for the node, and let the &lt;a href="http://api.drupal.org/api/function/l/5"&gt;l()&lt;/a&gt; function choose the most appropriate alias:&lt;br /&gt;&lt;br /&gt;&lt;pre name="code" class="php"&gt;&lt;br /&gt;print l($node-&gt;title, 'node/' . $node-&gt;nid);&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7330761068601183997-1363655657796857460?l=c-cohen.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://c-cohen.blogspot.com/feeds/1363655657796857460/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7330761068601183997&amp;postID=1363655657796857460' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7330761068601183997/posts/default/1363655657796857460'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7330761068601183997/posts/default/1363655657796857460'/><link rel='alternate' type='text/html' href='http://c-cohen.blogspot.com/2008/11/drupal-5-dont-rely-on-nodes-path.html' title='Drupal 5: Don&apos;t rely on the node&apos;s path attribute'/><author><name>fw190a8</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7330761068601183997.post-8490216338452510496</id><published>2008-10-28T11:02:00.000-07:00</published><updated>2008-10-28T11:14:26.514-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='url alias'/><category scheme='http://www.blogger.com/atom/ns#' term='problem'/><category scheme='http://www.blogger.com/atom/ns#' term='drupal 5'/><category scheme='http://www.blogger.com/atom/ns#' term='url'/><category scheme='http://www.blogger.com/atom/ns#' term='drupal'/><category scheme='http://www.blogger.com/atom/ns#' term='broken'/><category scheme='http://www.blogger.com/atom/ns#' term='alias'/><category scheme='http://www.blogger.com/atom/ns#' term='rss feed'/><category scheme='http://www.blogger.com/atom/ns#' term='rss'/><title type='text'>Drupal: Broken RSS feeds</title><content type='html'>In &lt;a href="http://www.drupal.org/"&gt;Drupal&lt;/a&gt; 5, the path &lt;a href="http://www.example.com/rss.xml"&gt;www.example.com/rss.xml&lt;/a&gt; will produce an &lt;a href="http://en.wikipedia.org/wiki/RSS"&gt;RSS&lt;/a&gt; feed of the most recent nodes (the number of items can be customised in the RSS publishing options; the default is ten), and other core modules provide feeds, such as taxonomy, via &lt;a href="http://www.example.com/taxonomy/term/1/0/feed"&gt;www.example.com/taxonomy/term/1/0/feed&lt;/a&gt;. I had a problem recently where this functionality appeared to stop working. RSS feeds would not be displayed, and a ‘page not found᾿ (&lt;a href="http://en.wikipedia.org/wiki/HTTP_404"&gt;404&lt;/a&gt;) error would appear instead.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;At first, I suspected a module might have been to blame. After all, the RSS functionality worked on a simple site, but not the one I was working on, which had been customised with lots of modules. I disabled all the modules that could have possibly affected the RSS feeds, but this did not help at all.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;It turns out that URL aliasing was to blame. I had inserted some rows manually into the &lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;{url_alias}&lt;/span&gt; table, but I had put the values into the wrong columns. The two columns in question were &lt;span class="Apple-style-span" style="font-family: 'courier new';"&gt;src&lt;/span&gt; and &lt;span class="Apple-style-span" style="font-family: 'courier new';"&gt;dst&lt;/span&gt;. The &lt;span class="Apple-style-span" style="font-family: 'courier new';"&gt;dst&lt;/span&gt; column is the column containing the URL the user goes to, and the &lt;span class="Apple-style-span" style="font-family: 'courier new';"&gt;src&lt;/span&gt; column is the one that it is aliases to, ie the ‘real’ URL. I had put the values into these columns the wrong way around, so when requesting the RSS feed's URL, Drupal was redirecting to a gibberish URL that did not exist!&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7330761068601183997-8490216338452510496?l=c-cohen.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://c-cohen.blogspot.com/feeds/8490216338452510496/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7330761068601183997&amp;postID=8490216338452510496' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7330761068601183997/posts/default/8490216338452510496'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7330761068601183997/posts/default/8490216338452510496'/><link rel='alternate' type='text/html' href='http://c-cohen.blogspot.com/2008/10/drupal-broken-rss-feeds.html' title='Drupal: Broken RSS feeds'/><author><name>fw190a8</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7330761068601183997.post-1015629513186104613</id><published>2008-10-24T04:29:00.000-07:00</published><updated>2008-10-24T04:40:22.344-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='drupal 5'/><category scheme='http://www.blogger.com/atom/ns#' term='roles'/><category scheme='http://www.blogger.com/atom/ns#' term='user access'/><category scheme='http://www.blogger.com/atom/ns#' term='permissions'/><category scheme='http://www.blogger.com/atom/ns#' term='access'/><category scheme='http://www.blogger.com/atom/ns#' term='drupal'/><category scheme='http://www.blogger.com/atom/ns#' term='user_selectable_roles'/><category scheme='http://www.blogger.com/atom/ns#' term='Bacteria Man'/><category scheme='http://www.blogger.com/atom/ns#' term='user.module'/><category scheme='http://www.blogger.com/atom/ns#' term='role'/><title type='text'>Drupal: Allowing users to edit roles</title><content type='html'>In &lt;a href="http://www.drupal.org"&gt;Drupal&lt;/a&gt; 5, I needed some way of giving users with a specific role permission to set the roles of others. It turns out that by default, the administrator is the only one who can assign roles to users. I also found the &lt;a href="http://drupal.org/project/user_selectable_roles"&gt;user_selectable_roles&lt;/a&gt; module by &lt;a href="http://drupal.org/user/25964"&gt;Bacteria Man&lt;/a&gt;, which allows users to assign themselves roles, but this was not quite what I wanted.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;After some digging around in the core, specifically &lt;a href="http://api.drupal.org/api/file/modules/user/user.module/5"&gt;user.module&lt;/a&gt;, I found that when the user edit page is displayed, the system checks whether the user (the user who is seeing the edit page, not the user being edited) has permission to administer access control, and if so, grants them the ability to edit roles.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;It turns out that in order to edit roles, the user must be given privileges to edit the access rights of all the roles, which is not really the same thing! In my opinion, there should be sufficient granularity between assigning roles to users and deciding what permissions those roles have. Perhaps this is something for a future Drupal release? It might even exist in Drupal 6 or Drupal 7, but not having played with them yet, I don't know.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7330761068601183997-1015629513186104613?l=c-cohen.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://c-cohen.blogspot.com/feeds/1015629513186104613/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7330761068601183997&amp;postID=1015629513186104613' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7330761068601183997/posts/default/1015629513186104613'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7330761068601183997/posts/default/1015629513186104613'/><link rel='alternate' type='text/html' href='http://c-cohen.blogspot.com/2008/10/drupal-allowing-users-to-edit-roles.html' title='Drupal: Allowing users to edit roles'/><author><name>fw190a8</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7330761068601183997.post-6070415596272960271</id><published>2008-10-22T10:36:00.000-07:00</published><updated>2008-10-23T02:08:55.023-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='drupal 5'/><category scheme='http://www.blogger.com/atom/ns#' term='index'/><category scheme='http://www.blogger.com/atom/ns#' term='drupal'/><category scheme='http://www.blogger.com/atom/ns#' term='search'/><category scheme='http://www.blogger.com/atom/ns#' term='import'/><title type='text'>Drupal: Search indexing manually imported nodes</title><content type='html'>Recently I was required to import about 7000 nodes into &lt;a href="http://www.drupal.org/"&gt;Drupal&lt;/a&gt; 5. I was not entirely sure how to achieve this, but I decided to use SQL scripting. As it turns out, a more proper method would have been to use a PHP script to build nodes and use &lt;a href="http://api.drupal.org/api/function/node_save/5"&gt;node_save()&lt;/a&gt; to accomplish the import.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;That aside, I ran into a problem where none of the manually imported nodes were appearing in search results. Nodes created within Drupal worked fine, appearing in search results as expected. My manually imported nodes worked fine on the site itself: by nagivating to &lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;node/1234&lt;/span&gt; it was possible to view the node as expected, and they worked fine with &lt;a href="http://drupal.org/project/views"&gt;Views&lt;/a&gt;, but there was nothing in the search results.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;My first stop was to tell Drupal to regenerate the search index, by visiting &lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;admin/settings/search&lt;/span&gt; and using the functionality there. The correct number of nodes was reported on this page, but after clicking the reindex button and running &lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;cron.php&lt;/span&gt;, it reported that 97% of the nodes had been indexed. This was far too short a time for this to happen, and a quick search showed that the manually imported nodes were still not appearing in search results.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;I began to speculate that because the manually imported nodes were last modified (the &lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;changed&lt;/span&gt; column in the database) &lt;span class="Apple-style-span" style="font-style: italic;"&gt;before&lt;/span&gt; the last time that the indexer was run, they weren't being ‘seen’ by the indexer, since it only pays attention to things that have changed since last time.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;I did try backing up the &lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;{node}&lt;/span&gt; table, setting the &lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;changed&lt;/span&gt; timestamp for &lt;span class="Apple-style-span" style="font-style: italic;"&gt;all&lt;/span&gt; nodes to something &lt;span class="Apple-style-span" style="font-style: italic;"&gt;after&lt;/span&gt; the last time the indexer was run, then reindexing everything again, but this seemed to make no difference. If I didn't know better, I would have said that Drupal was taking one look at the sheer amount of stuff it was going to have to index, and freaking out!&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;In the end, my solution was to create a simple module that took advantage of the &lt;a href="http://api.drupal.org/api/function/_node_index_node/6"&gt;_node_index_node()&lt;/a&gt; function from Drupal 6. This seemed to work with no compatibility issues (I was using Drupal 5 for this), and my simple module checked to see if the node's &lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;nid&lt;/span&gt; existed in the &lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;{search_dataset}&lt;/span&gt; table (as a &lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;sid&lt;/span&gt;, not a &lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;nid&lt;/span&gt;), and if it didn't, called _node_index_node() to forcibly index it. It was a very slow process but it appears to have done the trick.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;I suppose the question I am left with after this project is: why didn't Drupal reindex these nodes? Was my suspicion about the indexer not ‘seeing’ them correct? Should one &lt;span class="Apple-style-span" style="font-style: italic;"&gt;always&lt;/span&gt; import nodes using PHP and Drupal's node_save()? While my method works, it is cumbersome and I would always seek the most efficient way of doing this, so please leave a comment if you can elaborate on this!&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;a href="http://malone82.googlepages.com/force_search-5.x-1.0.zip"&gt;Download&lt;/a&gt; the completed module (2K).&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7330761068601183997-6070415596272960271?l=c-cohen.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://c-cohen.blogspot.com/feeds/6070415596272960271/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7330761068601183997&amp;postID=6070415596272960271' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7330761068601183997/posts/default/6070415596272960271'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7330761068601183997/posts/default/6070415596272960271'/><link rel='alternate' type='text/html' href='http://c-cohen.blogspot.com/2008/10/drupal-search-indexing-manually.html' title='Drupal: Search indexing manually imported nodes'/><author><name>fw190a8</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry></feed>
