Overriding URLs with Drupal panel pages
Hi, My name's Marcus, and I'm addicted to Drupal panels.
(For those of you who haven't come across this wonderdrug before, panels is a layout-engine for Drupal: it lets you add and lay out content, blocks, views and other panels on the page. And it's awesome!)
Panels has a UI which lets you setup new panel pages: e.g. you could add the URL /jobs for your jobs listing page.
A set of system URLs can also be overridden: e.g. node/%node can be overridden to use a panel-page instead of the core node_page_view function.
The full list of system URLs which can be overridden by panels are:- /node/%node
- /node/%node/edit
- /poll
- /taxonomy/term/%term
- /user/%user
/** * Implementation of hook_ctools_plugin_directory() to let the system know * where our task plugins are. */ function foo_ctools_plugin_directory($owner, $plugin_type) { return 'plugins/' . $plugin_type; }
<?php // $Id$ $plugin = array( // This is a 'page' task and will fall under the page admin UI 'task type' => 'page', 'title' => t('Notifications'), 'admin title' => t('Notifications'), 'admin description' => t('When enabled, this overrides the default Drupal behavior for the notifications page at <em>/user/%user/notifications</em>.'), 'admin path' => 'user/%user/notifications', // Menu hooks so that we can alter the default entry // foo is the name of the module; notifications is the name of the task. // (this is arbitrary and the name-structure is a convention // to avoid function-name collisions with other modules.) 'hook menu alter' => 'foo_notifications_menu_alter', // This task provides the 'user' context to content-types on the panel 'handler type' => 'context', 'get arguments' => 'foo_notifications_get_arguments', 'get context placeholders' => 'foo_notifications_get_contexts', // Allow this panel-page to be enabled or disabled: 'disabled' => variable_get('foo_notifications_disabled', TRUE), 'enable callback' => 'foo_notifications_enable', );
function foo_notifications_menu_alter(&$items, $task) { // the enable/disable callback will set a variable to control whether the panel is enabled. if (variable_get('foo_notifications_disabled', TRUE)) { // the panel is disabled: don't run the menu-alter, leave the default menu entry. return; } // Check whether the callbacks at user/%user/notifications match those // provided by the notifications module. If not, then the URL has been overridden already. $page_callback = $items['user/%user/notifications']['page callback']; $page_arguments = $items['user/%user/notifications']['page arguments']; $is_using_default = ($page_callback == 'drupal_get_form' && page_arguments == array('notifications_user_overview', 1)); // check that it either matches the callback provided by notifications, // or if not, it's already been overridden by another module, so check // whether page-manager is configured to override already-overridden pages. if ($is_using_default || variable_get('page_manager_override_anyway', FALSE)) { $items['user/%user/notifications']['page callback'] = 'foo_notifications_page'; $items['user/%user/notifications']['file path'] = $task['path']; $items['user/%user/notifications']['file'] = $task['file']; } else { // disable the panel page variable_set('foo_notifications_disabled', TRUE); // the enable-function sets a global, so the message is only displayed when trying to enable the panel, not on every cache-clear. if (!empty($GLOBALS['foo_enabling_notifications'])) { drupal_set_message(t('Foo module is unable to enable the notifications panel page because some other module already has overridden with %callback.', array('%callback' => $callback)), 'warning'); } return; } }
function foo_notifications_enable($cache, $status) { variable_set('foo_notifications_disabled', $status); // Set a global flag so that the menu routine knows it needs // to set a message if enabling cannot be done. if (!$status) { $GLOBALS['foo_enabling_notifications'] = TRUE; } }
function foo_notifications_get_arguments($task, $subtask_id) { return array( array( 'keyword' => 'user', 'identifier' => t('User'), 'id' => 1, 'name' => 'uid', 'settings' => array(), ), ); } /** * Callback to get context placeholders provided by this handler. */ function foo_notifications_get_contexts($task, $subtask_id) { return ctools_context_get_placeholders_from_argument(foo_notifications_get_arguments($task, $subtask_id)); }
/** * Entry point for our overridden notifications URL. * * This function asks its assigned handlers who, if anyone, would like * to run with it. If no one does, it passes through to notifications's * handler. */ function foo_notifications_page($user) { // Load my task plugin $task = page_manager_get_task('notifications'); // Load the user into a context. ctools_include('context'); ctools_include('context-task-handler'); $contexts = ctools_context_handler_get_task_contexts($task, '', array($user)); $args = array($user->uid); $output = ctools_context_handler_render($task, '', $contexts, $args); if ($output !== FALSE) { return $output; } // fallback to the default notifications handler: module_load_include('inc', 'notifications', 'notifications.pages'); $function = 'drupal_get_form'; $args = array('notifications_user_overview', $user); foreach (module_implements('page_manager_override') as $module) { $callback = $module . '_page_manager_override'; if (($rc = $callback('notifications')) && function_exists($rc)) { $function = $rc; break; } } // Otherwise, fall back. return $function($args); }


Comments
sms (not verified)
Sat, 04/16/2011 - 09:17
Permalink
Excellent posting. Undoubtedly you are an expert of such writing topics. This is absolutely the first time I visited your site and frankly speaking it succeeds in making me visit here now and then.And yes i have book mark your site deglos.com .
hstefl@redweb.cz (not verified)
Thu, 09/01/2011 - 20:50
Permalink
Hi,
thank you for article. It helped me a lot.
One thing I had to change. I had to implment plugin with hook. Now, I am not exactly sure why, but it helped.
SO I have somesthing like:
function MODULENAME_PLUGINAME_page_manager_tasks() { return array( // This is a 'page' task and will fall under the page admin UI 'task type' => 'page', 'title' => t('Forum'), 'admin title' => t('Forum'), 'admin description' => t('Override forum path.'), 'admin path' => 'forum/!forum', // Menu hooks so that we can alter the default entry 'hook menu alter' => 'rw_ratolesti_forum_menu_alter', // This task provides the 'forum' context to content-types on the panel 'handler type' => 'context', 'get arguments' => 'rw_ratolesti_forum_get_arguments', 'get context placeholders' => 'rw_ratolesti_forum_get_contexts', // Allow this panel-page to be enabled or disabled: 'disabled' => variable_get('rw_ratolesti_forum_disabled', TRUE), 'enable callback' => 'rw_ratolesti_forum_enable', ); }Cheers.
doswebdev (not verified)
Thu, 10/06/2011 - 20:53
Permalink
Tried to adapt this to override a Profile2 page (/profile-main/%user) using D7, but failed on several points.
Add new comment