I’ve recently upgraded a couple sites to Drupal 7. The first one (7.0 rc1) was a bit of a trial run to see how ‘ready’ 7 is. It was a simpler site, and in the process of an upgrade/retrofit already. It went pretty much according to plan with only a few slight hiccups, which are a subset of the items listed below. They certainly have made a concerted effort to do things the right way(kudos to the Drupal team and all contributors!), and to try to tie up the loose ends – and the effort shows. The overall impression one gets in the backend is of a tighter and more cohesive package. However – migrating my second site (using 7.0) over was a bit more challenging, as this site was quite a bit more complex.
The Process
Of course you need to follow the recommended paths on the www.drupal.org site – upgrade your D6 site to the latest point release, upgrade each module to the latest, look at all of your modules and determine which are now included in core, and which are ready for 7. You may need to dig into the issues or google it to see if people are successful with that module in 7 already. Do you really need that module? I’ve had to shift a few things around, and I’ve migrated from tinyMCE to the Wysiwyg module + CKeditor.
If you’re ready to do the upgrade, back everything up at least once, disable all your modules that aren’t in core or required, set your theme to garland, download the latest D7 release, and then follow the upgrade instructions. Hopefully all of your custom code, themes, modules and functions are under the sites dir.
So, many changes have been made to D7 – you’re going to find that you’ll probably have to tweak at least a few things to complete your upgrade.
Database
A number of the tables have been renamed, some fields have been refactored into different (new or renamed) tables, etc. If you don’t access the db directly, then this may not affect you(the actual D7 upgrade process does a pretty good job of converting all of your existing D6 db), but if you do…read on…
The table node_revisions is now node_revision and does not contain the fields teaser nor body – you’ll need to check out and join the new table field_revision_body (possibly on revision_id) and access fields body_summary and body_value. The naming conventions have migrated to a more human readable, sensible approach, and are an improvement upon the minimalist origins – but – it does lead to a bit of a ‘in-between’ feeling now – as it is a bit of both. This is by no means an exhaustive list, but the schema has also been modified concerning uploaded files as well as taxonomies, among other things.
db_query returns a result that is still a db object, but can be treated more like an array – so, to iterate through it,
you now (drupal 7):
foreach ($result as $object)
instead of (drupal 6):
while ($object = db_fetch_object($result))
and to get the number of items returned, call (drupal 7):
$result->rowCount()
instead of (drupal 6):
mysqli_num_rows($result)
Themes
If you’ve created your own theme, or tweaked an existing one, you’ll need to be aware of all the changes here – this isn’t a comprehensive list here, just a few of the things I ran into. Study the new Seven theme or D7′s Garland implementation. Dashes have been doubled in most of the filenames:
page-front.tpl.php becomes page--front.tpl.php
page-node-1.tpl.php becomes page--node--1.tpl.php
The outer shell of your html template – including DOCTYPE, html declaration, header and body, etc., is now kept in
html.tpl.php
(look for the default in modules/system/html.tpl.php)
For display of regions (drupal 6),
if($region_name) print $region_name;
becomes (drupal 7):
print render($page['region_name']);
If you want to suppress the printing of file uploads and taxonomy on your pages, override node.tpl.php (see default in modules/node/node.tpl.php) and explicitly hide those node fields:
hide($content['upload']);
hide($content['taxonomy_vocabulary_1']);
before the call:
print render($content);
To suppress the submitted by tag, I’m sure there’s a more elegant solution, but I just commented it out in node.tpl.php:
<?php //print $submitted; ?>
CCK
If you used CCK and some of its associated modules like Filefield to help create forms and or custom content types, you’ll need to be aware that the standard upgrade path will not migrate all of those CCK-related entities. Once your upgrade is complete, you’ll need to load the D7 CCK module from http://drupal.org/project/cck and enable it and its sub-entity ‘Content Migrate’. Then go to Structure >> Migrate Fields to actually migrate your custom fields and regain access to their content. This is evidently a work in progress – read more here: http://drupal.org/node/781088. The standard field types convert over correctly, but some of the less frequently used field types may present a problem. If you run into an error, try to determine what data was carried over correctly by examining the new field_data_field_* tables. Any fields that aren’t correctly populated can be rolled back, and then re-migrated.
I’ve found that the fields that you use to have in your custom content type are still there, but now the field contents will be saved into its own field-specific table. For instance, I have a custom content type named meeting and it was stored in the database in D6 with its own table: content_type_meeting. Any fields unique to meeting (for instance ‘Theme’ which would have been a field field_theme_value) would have been kept in that table provided that they weren’t also used by another custom content type, in which case they’d be kept in a separate, shared table. Now, in D7 – that field is always kept in its own tables: field_data_field_theme and field_revision_field_theme. So – now when you edit and save a node of type meeting, it will populate from(edit) and to(save) the new D7 field-specific tables, not the fields with the old content type table. This means that if you have custom code that picks up a list of meetings and formats it for display, you’ll need to modify your code to pick up meetings distributed in the database in the new format. A node_load() called on a meeting node will return the correct data in the affiliated fields, provided that the conversion has completed correctly.
Menus
All the menus came over fine as part of the upgrade, but custom, hierarchical menus situated in a block (that expanded and collapsed correctly in D6) would not display correctly out of the box in Drupal 7. I had to apply the following patch: http://drupal.org/node/942782#comment-3943328 (and running update.php after application) to get these menu items to correctly display – expanding and collapsing by clicking on the menu title.
When you edit a node now, have your Menu settings disappeared? Edit your content types, and down toward the bottom in Menu settings, enable the content type to be able to appear in the menus that you specify.
Imagecache
The image handling provided by ImageAPI and Imagecache is now contained within drupal core. See admin/config/media/image-styles for its admin in the backend.
D6 references to theme('imagecache', … need to be rewritten:
Drupal 6:
$imagecache_html = theme('imagecache',
'meeting_screenshot',
get_filename($screenshot),
"screenshot of " . $image_title,
$image_title);
Drupal 7:
$image_settings = array(
'style_name' => 'meeting_screenshot',
'path' => 'public://' . get_filename($screenshot),
'alt' => 'screenshot of ' . $links->title,
'title' => $links->title,
'attributes' => array('class' => 'image'),
'getsize' => FALSE,
);
$imagecache_html = theme_image_style($image_settings);
My get_filename function just returns the filename – and the default public files area is set to ‘sites/default/files’ (configurable in the backend) – which is what ‘public://’ indicates to drupal. This is relative to the site root.
[note: if using 'getSize' => TRUE, need to apply this patch: http://drupal.org/node/1012416]
The new imagecache tmp files SHOULD be written out to (and referenced from):
sites/default/files/styles/{image_preset_name}/public
(check existence/permissions on this tree if the files aren’t showing up)
I found that not all of my imagecache presets from my D6 site got carried over (or correctly) when upgrading from 6 to 7.
I had to go in and delete and recreate some, and just create some in 7.
Custom Modules
I’ve created a number of custom modules on this site, and then I’ll utilize the output of that module in a given custom region, associating the two in the Blocks configuration screen. I found that those relationships survived the upgrade, but the relationships are not displayed correctly in the D7 Blocks configuration screen. Haven’t gotten to the bottom of that entirely yet…
The module_name.module file contains a series of required (and optional) functions -
hook_perm has become:
function hook_permission() {
return array(
'administer module_name module' => array(
'title' => t('Administer module_name module'),
'description' => t('Perform administration tasks for module_name module.'),
),);
}
the real action typically has happened in the function named hook_block – (where ‘hook’ gets replaced by your module_name) and this function has been split into a series of functions, including(with relatively common contents):
function hook_block_info() {
$blocks['block_name'] = array(
'info' => t('Human Readable Block Name'),
'cache' => DRUPAL_NO_CACHE,
);
return $blocks;
}
function hook_block_configure($delta) {
if ($delta == 'block_name') {
$form['list_size'] = array(
'#type' => 'textfield',
'#title' => t('Number of module items to display in the list'),
'#default_value' => variable_get('module_name_list_size', 3),
'#size' => '3',
'#maxlength' => '4',
);
return $form;
}
}
function hook_block_save($delta, $edit = array()) {
if ($delta == 'block_name') {
variable_set('module_name_list_size', $edit['list_size']);
}
}
and hook_block is now:
function hook_block_view($delta='')
and the code in your if view block would be kept in the new function – just delete section under if list
Entity module
I was getting errors on each node view:
Warning: array_flip(): Can only flip STRING and INTEGER values! in DrupalDefaultEntityController->load() (line 167 of includes/entity.inc).
Warning: array_flip(): Can only flip STRING and INTEGER values! in DrupalDefaultEntityController->cacheGet() (line 343 of includes/entity.inc).
Googling this problem did not help me find a definitive solution or root cause -it seems that the array values it is trying to flip are strings, so I think it may be an error in array_flip() – I’m not sure – but in order to get this working, I’ve hacked includes/entity.inc to convert the string vals to ints right before these array_flip() calls, and then everything appears to work…
$new_ids = array();
foreach($ids as $key => $value){
// echo $value . " is of type " . gettype($value) . "<br>";
$new_ids[$key] = intval($value);
}
$ids = $new_ids;
Taxonomy
I found that the taxonomies and node relationships survived the upgrade. However – there were a few changes in the db schema: term_node becomes taxonomy_index and term_data becomes taxonomy_term_data
where, in D6 I use to resort to this PHP snippet in ‘Page specific visibility settings’ to determine whether or not to display a block on a give node:
<?php
$content_area = 'some_content_area_term';
if (arg(0) == 'node' && is_numeric(arg(1))) {
$nid = arg(1);
$node = node_load(array('nid' => $nid));
if(isset($node->taxonomy) && is_array($node->taxonomy)){
foreach($node->taxonomy as $taxonomy){
if($taxonomy->name == $content_area){
return TRUE;
}
}
}
}
return FALSE;
?>
Given the changes D7 brought, I had to craft the following function to determine if there existed a relationship between node and term id:
(reference http://drupal.org/node/733856)
function connection_exists($nid, $term){
if (module_exists('taxonomy') && $nid > 0 && $term) {
$node = node_load($nid);
$vocabs_exist = TRUE;
$vocab_index = 1;
while($vocabs_exist){
$field_name = 'taxonomy_vocabulary_' . $vocab_index;
$term_index = 0;
$terms_exist = TRUE;
if(!empty($node->{$field_name})){
while($terms_exist){
if(isset($node->{$field_name}['und'][$term_index]['taxonomy_term'])) {
$term_obj = $node->{$field_name}['und'][$term_index++]['taxonomy_term'];
if($term_obj){
if(strcasecmp($term_obj->name, $term) == 0){
return TRUE;
}
}
}
else{
$terms_exist = FALSE;
}
}
}
else{
$vocabs_exist = FALSE;
}
$vocab_index++;
}
}
return FALSE;
}
I’m not sure if this is actually kosher Drupal – the suggested method of calling view_field_view on the node tag field_tags did not work for me – perhaps since I was using taxonomies that had just been upgraded from D6 and I wasn’t actually adding terms in 7?
Node Privacy Byrole
I was concerned about this module in D7, as I did not see much discussion or many issues(or recent updates for that matter) with it that were related to Drupal 7. It turns out to work fine. The db entries from the D6 site carried over fine, and I was able to verify that the users, roles and permission settings were pretty much intact. However – I did have to rebuild the content access permissions (admin/content/node-settings/rebuild) to get this to work correctly.
WYSIWYG
This module seems to work fine in D7. I’ve been impressed with the CKEditor, so I plug that in and it has been a good combination. Keep in mind – CKeditor installs out of the box with NO buttons displaying in toolbar. You must go in and configure presets to enable those menu items within the specific input formats that you want to expose.
user-profile.tpl.php
I needed to override this template, and in the past I was able to add regions by overriding template_preprocess_user_profile and adding a line like:
$variables['editable_node_list_region'] = theme('blocks', 'editable_node_list_region');
But this function has changed, and it’s not clear at all how to make this happen(If yo know, please tell me!). So for now I’m explicitly taking the output directly from the module that produces the html for this region, and displaying it from within the user-profile.tpl.php
$block_content = editable_node_list_block_view();
if(isset($block_content['content']) && $block_content['content'] != ''){
echo "<br><h3>Editable Pages</h3>";
print $block_content['content'];
}
Undefined index: description
I also got these errors on each node edit:
Notice: Undefined index: description in field_multiple_value_form() (line 156 of /xxxx/modules/field/field.form.inc).
Notice: Undefined index: required in field_multiple_value_form() (line 178 of /xxxx/modules/field/field.form.inc).
Notice: Undefined index: required in field_multiple_value_form() (line 207 of /xxxx/modules/field/field.form.inc).
What this is not exactly clear(I’ve come across some involved descriptions), but if you go into each Content Type and click on Manage Fields and then Save, it will go away. This is a known bug and I expect it will
be fixed in the near term.
jQuery and collapsible fieldsets
I was using this in D6, but in D7 you must also make sure that your legend contains a span with class ‘fieldset-legend’, and that your content is contained in a div with class ‘fieldset-description’, and that you include ‘misc/form.js’ for your javascript. Then it’ll work.
drupal_add_js('misc/form.js');
drupal_add_js('misc/collapse.js');
<fieldset id='fieldset-id' class='collapsible'>
<legend><span class='fieldset-legend'>Fieldset title</span></legend>
<div class='fieldset-wrapper'>
<div class='fieldset-description'>Fieldset description</div>
Fieldset content
</div>
</fieldset>
Access for site editors
When assigning your editors permissions, make sure they have (Node) ‘Access the content overview page’, and if you want them to access ‘Publishing options’ (as well as ‘Authoring information’ and ‘Revision information’), give them (Node) ‘Administer content’ permission. Of course, give them edit/view/delete on any content types they own. Also give them (Overlay) ‘Access the administrative overlay’ and (System) ‘View the administration theme’ and (Toolbar) ‘Use the administration toolbar’






