OUTDATED: Converting 4.5.x modules to 4.6.x
Block system
Every block now has a configuration page to control block-specific options. Modules which have configurations for their blocks should move those into hook_block().
The only required changes to modules implementing hook_block() is to be careful about what is returned. Do not return anything if $op is not 'list' or 'view'. Once this change is made, modules will still be compatible with Drupal 4.5.
If a specific block has configuration options, implement the additional $op options in your module. The implementation of 'configure' should return a string containing the configuration form for the block with the appropriate $delta. 'save' will have an additional $edit argument, which will contain the submitted form data for saving.
Search system
The search system got a significant overhaul.
Node indexing now uses the node's processed and filtered output, which means that any custom node fields will automatically be included in the index, as long as they are visible to normal users who view the node. Modules that implement hook_search() and hook_update_index() just to have extra node fields indexed no longer need to do this.
If you wish to have additional information indexed that is not visible in the node display at node/id, then you can do so using nodeapi('update index'). If you want to add extra information to the node results, use nodeapi('search result').
However, the standard search is still limited to a keyword search. Modules that implement custom, specific search forms (like project.module) can still do so. Custom search forms that do not use hook_search() should be located/moved to a local task under the /search page.
If you are unsure of what you need to do, please refer to the complete search documentation.
Module paths
The function module_get_path was renamed to drupal_get_path which now returns the path for all themes, theme engines and modules. Because of this abstraction you must pass an additional parameter identifying the type of item for which the path is requested. The following example compares retrieving the path to image module between Drupal 4.5 and 4.6.
<?php
// Drupal 4.5:
$path = module_get_path('image');
// Drupal 4.6:
$path = drupal_get_path('module', 'image');
?>All instances of module_get_path should be renamed to drupal_get_path.
Database backend
The function check_query was renamed to db_escape_string and now has a database specific implementation. All instances of check_query should be renamed to db_escape_string.
Theme system
The function theme_page() no longer takes $title or $breadcrumb arguments. Set page titles using hook_menu() or, if the title must be dynamically determined, use drupal_set_title(). Set breadcrumb trails first using hook_menu(), which can be overridden with menu_set_location() and drupal_set_breadcrumb().
Watchdog messages
The watchdog() function now takes a severity attribute, so watchdog($type, $message, $link); becomes watchdog($type, $message, $severity, $link);. Specify a severity in case you are reporting a warning or error. Possible severity constants are: WATCHDOG_NOTICE, WATCHDOG_WARNING and WATCHDOG_ERROR. Also make sure that you provide the type as a literal string, so translation extraction can pick it up.
If you are unsure of which severity to use, remember these rules:
- If the problem is caused by a definite fault and should be fixed as soon as possible, use an error message.
- If the problem could point to a fault, but could also be harmless, use a warning message. This type should also be used whenever the problem could be caused by a remote server (example: ping timeout, failed to aggregate a feed, etc).
- Normal messages should be notices.
Node markers
If you have a module calling theme('mark'), note that it is now possible to have different markers for different states of a node. The supported states are MARK_NEW, MARK_UPDATED and MARK_READ. You can get the marker state from node_mark(), which replaces the node_new() function available in previous Drupal versions.
Control over destination page after form processing
Occasionally a module might want to specify where a user should go after he submits a form. This is now possible by passing a querystring parameter &destination=<path>. For example, editing of nodes and comments from within the Admin pages now returns the user to those pages after he is done. For example usage, search drupal_get_destination() which can be found in path.module, node.module, comment.module, and user.module
Confirmation messages
Confirmations for dangerous actions should now be presented with the theme('confirm') function for consistency. Check the function's documentation or look at some of the core modules for examples.
Note that this is a themable function which should be invoked through theme('confirm') and not theme_confirm().
Inter module calls
New features are available -- it's not necessary to use them. Now you can really (and should) use module_invoke to call a function from another module. For example, taxonomy_get_tree should be called by module_invoke('taxonomy', 'get_tree') If you need to loop through the implementations of a hook, please check the new module_implements function.
Node queries
If you have a module which retrieves a list of nodes by issuing its own database query, then the following applies.
The functions node_access_join_sql() and node_access_where_sql() should not be used any more but the SELECT-queries should be wrapped in a db_rewrite_sql() call.
If you have used DISTINCT(nid) -- because of node_access_join_sql() -- you no longer need it, replace it simply with n.nid. If you have SELECT *, please replace it with SELECT n.nid, n.* -- and always make sure that n.nid field comes first in the SELECT statement -- this way the db_rewrite_sql() function can rewrite the query to use DISTINCT(nid) should there be a need for it. If the n.nid field is not first, the query will fail when node access modules are enabled. Also, at the moment db_rewrite_sql can not handle AS -- either leave it out or lowercase it.
Always use table name before the field names, especially before nid because other tables may be JOINed during the rewrite process.
Example:
<?php
// Drupal 4.5:
$nodes = db_query_range('SELECT DISTINCT(n.nid) FROM {node} n '. node_access_join_sql() .' WHERE '. node_access_where_sql() .' AND n.promote = 1 AND n.status = 1 ORDER BY n.created DESC', 0, 15);
// Drupal 4.6:
$nodes = db_query_range(db_rewrite_sql('SELECT n.nid FROM {node} n WHERE n.promote = 1 AND n.status = 1 ORDER BY n.created DESC'), 0, 15);
?>If you are not using the node table, then you shall pass the table name from which you SELECTing the nodes. For example
<?php
$result = db_query(db_rewrite_sql("SELECT f.nid, f.* from {files} f WHERE filepath = '%s'", 'f'), $file);
?>note the 'f' parameter of db_rewrite_sql().
Avoid USING because there could be JOINs before it, which will break the USING clause.
Text output
Drupal's text output was audited and several escaping bugs were found. For more info, see the check_plain patch.
You need to pay attention that all user-submitted plain-text in your module is escaped using check_plain() when you output it into HTML. No escaping should be done on data that is going into the database: only escape when outputting to HTML.
Check_plain() replaces drupal_specialchars() and check_form(), so if you are using any of those two, you should use check_plain() instead.
You should also wrap user-submitted text in messages with theme('placeholder', $text). For example for "created term %term".
Pay attention in particular to node and comment titles as their behaviour has been changed. They are now stored as plain-text, like other single-line fields in Drupal and should be escaped when output. However, the function l() now takes plain-text by default instead of HTML, which means that whenever $node->title is used as the caption for a link, it will automatically be escaped. When outputting titles literally, you still have to escape them yourself.
URLs also require attention, as the URL functions (url, request_uri, referer_uri, etc) were changed to output 'real' URLs rather than HTML-escaped URLs. When putting any of them inside an HTML tag attribute (e.g. <a href="...">), you need to pass it through check_url() first. When putting an URL into HTML outside of a tag or attribute, you can use check_url() or check_plain(), it doesn't matter. Don't use check_url() in situations where a real URL is expected (e.g. the HTTP "Location: ..." header).
The best test is to submit forms with HTML tags in the plain-text/single-line fields (e.g. "<u>test</u>"). If the underline tag is not interpreted, but displayed literally, your module is escaping the text correctly.
Nothing has changed for filtered/rich text, which still uses check_output() like before.
hook_user changes slightly
Please note that any module implementing hook_user will need a stricter function call. Previously you were able to get away with:
hook_user($op, &$edit, &$user, $category)Now you must use:
hook_user($op, &$edit, &$user, $category = FALSE)This is becasue no $category is provided on the very first user-view screen.
The l() function changes
The l() function has been changed so that it now escapes the title by default. However, you can still get the old behavior by setting the $html param to TRUE. For example, code like the following:
l(t("administer » filters"), "admin/filters")should be replaced with:
l(t("administer » filters"), "admin/filters", array(), NULL, NULL, FALSE, TRUE)Vocabularies and node types
If your module checks which node types are assigned to a vocabulary, you'll need to rework the code as the nodes field in vocabulary has been pulled into a separate table. Example (selecting all vocabularies having a particular node type):
<?php
// Drupal 4.5:
$result = db_query("SELECT * FROM {vocabulary} WHERE nodes LIKE '%%%s%%' ORDER BY weight, name", $content_type);
// Drupal 4.6:
$result = db_query("SELECT * FROM {vocabulary} v INNER JOIN {vocabulary_node_types} vn ON v.vid = vn.vid WHERE vn.type = '%s' ORDER BY v.weight, v.name", $content_type);
?>node_new is not node_new
Under the Node Markers section, node_new is actually called node_is_new in Drupal 4.5.
