'Dashboard', 'page callback' => 'dashboard_admin', 'access arguments' => array('access dashboard'), // Make this appear first, so for example, in admin menus, it shows up on // the top corner of the window as a convinient "home link". 'weight' => -100, ); $items['admin/dashboard/customize'] = array( 'title' => 'Dashboard', 'page callback' => 'dashboard_admin', 'page arguments' => array(TRUE), 'access arguments' => array('access dashboard'), 'type' => MENU_CALLBACK, ); $items['admin/dashboard/drawer'] = array( 'page callback' => 'dashboard_show_disabled', 'access arguments' => array('administer blocks'), 'type' => MENU_CALLBACK, ); $items['admin/dashboard/block-content/%/%'] = array( 'page callback' => 'dashboard_show_block_content', 'page arguments' => array(3, 4), 'access arguments' => array('administer blocks'), 'type' => MENU_CALLBACK, ); $items['admin/dashboard/update'] = array( 'page callback' => 'dashboard_update', 'access arguments' => array('administer blocks'), 'type' => MENU_CALLBACK, ); return $items; } /** * Implement hook_block_info_alter(). * * Skip rendering dashboard blocks when not on the dashboard page itself. This * prevents expensive dashboard blocks from causing performance issues on pages * where they will never be displayed. */ function dashboard_block_info_alter(&$blocks) { if (!dashboard_is_visible()) { foreach ($blocks as $key => $block) { if (in_array($block->region, dashboard_regions())) { unset($blocks[$key]); } } } } /** * Implement hook_page_build(). * * Display dashboard blocks in the main content region. */ function dashboard_page_build(&$page) { global $theme_key; if (dashboard_is_visible()) { $block_info = array(); // Create a wrapper for the dashboard itself, then insert each dashboard // region into it. $page['content']['dashboard'] = array('#theme_wrappers' => array('dashboard')); foreach (dashboard_regions() as $region) { // Insert regions even when they are empty, so that they will be // displayed when the dashboard is being configured. $page['content']['dashboard'][$region] = !empty($page[$region]) ? $page[$region] : array(); $page['content']['dashboard'][$region]['#dashboard_region'] = $region; // Allow each dashboard region to be themed differently, or fall back on // the generic theme wrapper function for dashboard regions. $page['content']['dashboard'][$region]['#theme_wrappers'][] = array($region, 'dashboard_region'); unset($page[$region]); $blocks_found = array(); foreach ($page['content']['dashboard'][$region] as $item) { if (isset($item['#theme_wrappers']) && is_array($item['#theme_wrappers']) && in_array('block', $item['#theme_wrappers'])) { // If this item is a block, ensure it has a subject. if (empty($item['#block']->subject)) { // Locally cache info data for the object for all blocks, in case // we find a block similarly missing title from the same module. if (!isset($block_info[$item['#block']->module])) { $block_info[$item['#block']->module] = module_invoke($item['#block']->module, 'block_info'); } $item['#block']->subject = $block_info[$item['#block']->module][$item['#block']->delta]['info']; } $blocks_found[$item['#block']->module . '_' . $item['#block']->delta] = TRUE; } } // Find blocks which were not yet displayed on the page (were empty), and // add placeholder items in their place for rendering. $block_list = db_select('block') ->condition('theme', $theme_key) ->condition('status', 1) ->condition('region', $region) ->fields('block') ->execute(); foreach ($block_list as $block) { if (!isset($blocks_found[$block->module . '_' . $block->delta])) { $block->enabled = $block->page_match = TRUE; $block->content = array('#markup' => '
(empty)
'); if (!isset($block_info[$block->module])) { $block_info[$block->module] = module_invoke($block->module, 'block_info'); } $block->subject = t('@title', array('@title' => $block_info[$block->module][$block->delta]['info'])); $block_render = array($block->module . '_' . $block->delta => $block); $build = _block_get_renderable_array($block_render); $page['content']['dashboard'][$block->region][] = $build; } } } } } /** * Implement hook_permission(). */ function dashboard_permission() { return array( 'access dashboard' => array( 'title' => t('View the administrative dashboard'), 'description' => t('Access the site-wide dashboard. Modifying the dashboard requires the "Administer blocks" permission.'), ), ); } /** * Implement hook_system_info_alter(). * * Add regions to each theme to store the dashboard blocks. */ function dashboard_system_info_alter(&$info, $file, $type) { if ($type == 'theme') { $info['regions'] += dashboard_region_descriptions(); if (module_exists('overlay')) { $info['overlay_regions'] = !empty($info['overlay_regions']) ? array_merge($info['overlay_regions'], dashboard_regions()) : dashboard_regions(); } } } /** * Implement hook_theme(). */ function dashboard_theme() { return array( 'dashboard' => array( 'render element' => 'element', ), 'dashboard_admin' => array( 'render element' => 'element', ), 'dashboard_region' => array( 'render element' => 'element', ), 'dashboard_disabled_blocks' => array( 'variables' => array('blocks' => NULL), ), 'dashboard_disabled_block' => array( 'variables' => array('block' => NULL), ), ); } /** * Dashboard page callback. * * @param $launch_customize * Whether to launch in customization mode right away. TRUE or FALSE. */ function dashboard_admin($launch_customize = FALSE) { $js_settings = array( 'dashboard' => array( 'drawer' => url('admin/dashboard/drawer'), 'blockContent' => url('admin/dashboard/block-content'), 'updatePath' => url('admin/dashboard/update'), 'formToken' => drupal_get_token('dashboard-update'), 'launchCustomize' => $launch_customize, 'dashboard' => url('admin/dashboard'), 'emptyBlockText' => _dashboard_get_default_string('dashboard_empty_block_text'), 'emptyRegionTextInactive' => _dashboard_get_default_string('dashboard_empty_region_text_inactive'), 'emptyRegionTextActive' => _dashboard_get_default_string('dashboard_empty_region_text_active'), ), ); $build = array( '#theme' => 'dashboard_admin', '#message' => t('To customize the dashboard page, move blocks to the dashboard regions on !block-admin, or enable JavaScript on this page to use the drag-and-drop interface.', array('!block-admin' => l('the block administration page', 'admin/structure/block'))), '#access' => user_access('administer blocks'), '#attached' => array( 'js' => array( drupal_get_path('module', 'dashboard') . '/dashboard.js', array('data' => $js_settings, 'type' => 'setting'), ), 'library' => array(array('system', 'ui.sortable')), ), ); return $build; } /** * Returns TRUE if the user is currently viewing the dashboard. */ function dashboard_is_visible() { $menu_item = menu_get_item(); return isset($menu_item['page_callback']) && $menu_item['page_callback'] == 'dashboard_admin'; } /** * Return an array of dashboard region descriptions, keyed by region name. */ function dashboard_region_descriptions() { $regions = module_invoke_all('dashboard_regions'); drupal_alter('dashboard_regions', $regions); return $regions; } /** * Return an array of dashboard region names. */ function dashboard_regions() { return array_keys(dashboard_region_descriptions()); } /** * Implement hook_dashboard_regions(). */ function dashboard_dashboard_regions() { return array( 'dashboard_main' => 'Dashboard main', 'dashboard_sidebar' => 'Dashboard sidebar', ); } /** * AJAX callback to show disabled blocks in the dashboard customization mode. */ function dashboard_show_disabled() { global $theme_key; // Blocks are not necessarily initialized at this point. $blocks = _block_rehash(); // Limit the list to disabled blocks for the current theme. foreach ($blocks as $key => $block) { if ($block['theme'] != $theme_key || (!empty($block['status']) && !empty($block['region']))) { unset($blocks[$key]); } } // Theme the output and end the page request. print theme('dashboard_disabled_blocks', array('blocks' => $blocks)); drupal_exit(); } /** * AJAX callback to display the rendered contents of a specific block. * * @param $module * The block's module name. * @param $delta * The block's delta. */ function dashboard_show_block_content($module, $delta) { drupal_theme_initialize(); global $theme_key; $blocks = array(); $block_object = db_query("SELECT * FROM {block} WHERE theme = :theme AND module = :module AND delta = :delta", array( ":theme" => $theme_key, ":module" => $module, ":delta" => $delta, )) ->fetchObject(); $block_object->enabled = $block_object->page_match = TRUE; $blocks[$module . "_" . $delta] = $block_object; $block_content = _block_render_blocks($blocks); $build = _block_get_renderable_array($block_content); $rendered_block = drupal_render($build); print $rendered_block; drupal_exit(); } /** * Set the new weight of each region according to the drag-and-drop order. */ function dashboard_update() { drupal_theme_initialize(); global $theme_key; // Check the form token to make sure we have a valid request. if (!empty($_REQUEST['form_token']) && drupal_valid_token($_REQUEST['form_token'], 'dashboard-update')) { parse_str($_REQUEST['regions'], $regions); foreach ($regions as $region_name => $blocks) { if ($region_name == 'disabled_blocks') { $region_name = ''; } foreach ($blocks as $weight => $block_string) { // Parse the query string to determine the block's module and delta. preg_match('/block-([^-]+)-(.+)/', $block_string, $matches); $block = new stdClass; $block->module = $matches[1]; $block->delta = $matches[2]; $block->region = $region_name; $block->weight = $weight; if (empty($region_name)) { $block->status = 0; } else { $block->status = 1; } db_merge('block') ->key(array( 'module' => $block->module, 'delta' => $block->delta, 'theme' => $theme_key, )) ->fields(array( 'status' => $block->status, 'weight' => $block->weight, 'region' => $block->region, 'pages' => '', )) ->execute(); } } } drupal_exit(); } /** * Theme the entire dashboard. * * @param $variables * - element: An associative array containing the properties of the dashboard region * element. Properties used: #dashboard_region, #children * @return * A string representing the themed dashboard. * * @ingroup themeable */ function theme_dashboard($variables) { extract($variables); drupal_add_css(drupal_get_path('module', 'dashboard') . '/dashboard.css'); return '
' . $element['#children'] . '
'; } /** * Theme the page containing the dashboard. * * @param $variables * An associative array containing: * - elements: An associative array containing the properties of the element. * Properties used: #message * @return * A themed HTML string representing the non-customizable part of the * dashboard page. * * @ingroup themeable */ function theme_dashboard_admin($variables) { // We only return a simple help message, since the actual content of the page // will be populated via the dashboard regions in dashboard_page_build(). return '
' . $variables['element']['#message'] . '
'; } /** * Theme a generic dashboard region. * * @param $variables * - element: An associative array containing the properties of the dashboard region * element. Properties used: #dashboard_region, #children * @return * A string representing the themed dashboard region. * * @ingroup themeable */ function theme_dashboard_region($variables) { extract($variables); $output = '
'; $output .= '
'; $output .= $element['#children']; // Closing div.region $output .= '
'; // Closing div.dashboard-region $output .= '
'; return $output; } /** * Theme a set of disabled blocks, for display in dashboard customization mode. * * @param $variables * - blocks: An array of block objects from _block_rehash(). * @return * A string representing the disabled blocks region of the dashboard * customization page. * * @ingroup themeable */ function theme_dashboard_disabled_blocks($variables) { extract($variables); $output = '

' . t('Drag and drop dashboard blocks to their place. Changes are automatically saved. You can also add a custom block.', array('@add-block' => url('admin/structure/block/add', array('query' => array('destination' => 'admin/dashboard/customize'))))) . '

'; $output .= '
'; foreach ($blocks as $block) { $output .= theme('dashboard_disabled_block', array('block' => $block)); } $output .= '
'; return $output; } /** * Theme a disabled block, for display in dashboard customization mode. * * @param $variables * - block: A block object from _block_rehash(). * @return * A string representing the disabled block. * * @ingroup themeable */ function theme_dashboard_disabled_block($variables) { extract($variables); $output = ""; if (isset($block)) { $output .= '
' . '

'.(!empty($block['title']) ? $block['title'] : $block['info']).'

' . '
' . '
'; } return $output; } /** * Central storage for strings. * * @param $key * @return string */ function _dashboard_get_default_string($key) { $defaults = array( 'dashboard_empty_region_text_inactive' => 'This dashboard region is empty, click customize to add blocks to it.', 'dashboard_empty_block_text' => '(empty)', 'dashboard_empty_region_text_active' => 'DRAG HERE', ); return t(variable_get($key, $defaults[$key])); }