<?php
// $Id: admin.module,v 1.1.2.23.2.26 2010/08/12 18:00:26 yhahn Exp $

/**
 * Implementation of hook_init().
 */
function admin_init() {
  if (arg(0) === 'admin') {
    menu_set_active_menu_name('admin');
  }
  if (user_access('use admin toolbar')) {
    $path = drupal_get_path('module', 'admin');
    drupal_add_js("{$path}/includes/jquery.cookie.js");
    drupal_add_js("{$path}/includes/jquery.drilldown.js");
    drupal_add_js("{$path}/includes/admin.toolbar.js");
    drupal_add_js("{$path}/includes/admin.menu.js");
    drupal_add_css("{$path}/includes/admin.toolbar.base.css");
    drupal_add_css("{$path}/includes/admin.toolbar.css");
    drupal_add_css("{$path}/includes/admin.menu.css");
  }
}

/**
 * Implementation of hook_block().
 */
function admin_block($op = 'list', $delta = 0, $edit = array()) {
  switch ($op) {
    case 'list':
      $blocks = array();
      $blocks['create'] = array(
        'info' => t('Create content'),
        'cache' => BLOCK_NO_CACHE,
        'admin' => TRUE
      );
      $blocks['theme'] = array(
        'info' => t('Theme switcher'),
        'cache' => BLOCK_CACHE_PER_ROLE,
        'admin' => TRUE
      );
      $blocks['account'] = array(
        'info' => t('My Account'),
        'cache' => BLOCK_NO_CACHE,
        'admin' => TRUE
      );
      if (module_exists('menu')) {
        $blocks['menu'] = array(
          'info' => t('Administration menu'),
          'cache' => BLOCK_CACHE_PER_ROLE,
          'admin' => TRUE,
        );
      }
      if (module_exists('devel')) {
        $blocks['devel'] = array(
          'info' => t('Devel'),
          'cache' => BLOCK_NO_CACHE,
          'admin' => TRUE
        );
      }
      return $blocks;
    case 'view':
      switch ($delta) {
        case 'create':
          $item = menu_get_item('node/add');
          $links = system_admin_menu_block($item);
          if (!empty($links)) {
            $menu = array();
            foreach ($links as $key => $link) {
              $menu[$key] = array('link' => $link, 'below' => FALSE);
            }
            return array(
              'subject' => !empty($item['title']) ? $item['title'] : t('Create content'),
              'content' => theme('admin_drilldown_menu_tree_output', $menu),
            );
          }
          break;
        case 'theme':
          module_load_include('inc', 'admin', 'includes/admin.theme');
          return admin_block_theme();
        case 'account':
          return admin_account_block();
        case 'menu':
          $item = menu_get_item('admin');
          if ($item && $item['access']) {
            return array(
              'subject' => !empty($item['title']) ? $item['title'] : t('Administer'),
              'content' => theme('admin_drilldown_menu_tree_output', menu_tree_all_data('admin')),
            );
          }
          break;
        case 'devel':
          module_load_include('inc', 'admin', 'includes/admin.devel');
          return admin_block_devel();
      }
      break;
  }
}

/**
 * Implement our own simplified block caching. Because the Drupal core block
 * caching system is so conservative (e.g. disabled when *any* node_access
 * module is enabled), we roll our own solution here. Note that except for
 * the introduction of a user 1 specific cache, the cid generated by this
 * function is compatible with those in Drupal core.
 */
function admin_block_get_cache_id($block) {
  global $theme, $base_root, $user;

  if ($block->cache != BLOCK_NO_CACHE) {
    $cid_parts = array();

    // Start with common sub-patterns: block identification, theme, language.
    $cid_parts[] = $block->module;
    $cid_parts[] = $block->delta;
    $cid_parts[] = $theme;
    if (module_exists('locale')) {
      global $language;
      $cid_parts[] = $language->language;
    }

    // In addition to the Drupal core pattern, allow caching separately for u1.
    if ($user->uid == 1) {
      $cid_parts[] = "u.$user->uid";
    }
    else if ($block->cache & BLOCK_CACHE_PER_ROLE) {
      $cid_parts[] = 'r.'. implode(',', array_keys($user->roles));
    }
    elseif ($block->cache & BLOCK_CACHE_PER_USER) {
      $cid_parts[] = "u.$user->uid";
    }
    if ($block->cache & BLOCK_CACHE_PER_PAGE) {
      $cid_parts[] = $base_root . request_uri();
    }
    return implode(':', $cid_parts);
  }
}

/**
 * Implementation of hook_elements().
 */
function admin_elements() {
  return array(
    'admin_panes' => array('#value' => ''),
  );
}

/**
 * Implementation of hook_menu_alter().
 */
function admin_menu_alter(&$items) {
  // Move admin theme item under ours as a local task.
  $items['admin/settings/admin/theme'] = $items['admin/settings/admin'];
  $items['admin/settings/admin/theme']['type'] = MENU_LOCAL_TASK;
  $items['admin/settings/admin/theme']['weight'] = 10;

  // Generate our own admin settings page.
  $items['admin/settings/admin'] =
  $items['admin/settings/admin/settings'] = array(
    'title' => 'Administration tools',
    'description' => 'Settings for site administration tools.',
    'page callback' => 'drupal_get_form',
    'page arguments' => array('admin_settings_form'),
    'access callback' => 'user_access',
    'access arguments' => array('administer site configuration'),
    'type' => MENU_NORMAL_ITEM,
    'file' => 'admin.admin.inc',
    'module' => 'admin',
  );
  $items['admin/settings/admin/settings']['title'] = 'Settings';
  $items['admin/settings/admin/settings']['type'] = MENU_DEFAULT_LOCAL_TASK;
  $items['admin/settings/admin/rebuild'] = array(
    'title' => 'Rebuild',
    'description' => 'Wipe and rebuild the administrative menu.',
    'page callback' => 'drupal_get_form',
    'page arguments' => array('admin_settings_rebuild'),
    'access callback' => 'user_access',
    'access arguments' => array('administer site configuration'),
    'type' => MENU_LOCAL_TASK,
    'file' => 'admin.admin.inc',
    'module' => 'admin',
    'weight' => 10,
  );

  foreach ($items as $path => $item) {
    $item['type'] = isset($item['type']) ? $item['type'] : MENU_NORMAL_ITEM;
    // Move all admin/* items to admin menu links except local tasks, callbacks.
    $args = explode('/', $path);
    if ($path === 'admin' || (($item['type'] & MENU_NORMAL_ITEM) && $args && $args[0] === 'admin')) {
      $items[$path]['menu_name'] = 'admin';
    }
    // Smarter access callback for poorly checked landing pages
    if (!empty($item['access arguments']) && !empty($item['page callback']) && $item['access arguments'] === array('access administration pages') && in_array($item['page callback'], array('system_admin_menu_block_page', 'system_settings_overview'))) {
      $items[$path]['access callback'] = 'admin_landing_page_access';
      $items[$path]['access arguments'] = array($path);
    }
  }
}

/**
 * Menu access callback for admin landing pages.
 *
 * For a given landing page, grant access if the current user has access
 * to any of its child menu items.
 */
function admin_landing_page_access($path) {
  static $paths;
  if (!isset($paths[$path])) {
    $item = db_fetch_array(db_query("SELECT mlid, menu_name FROM {menu_links} ml WHERE ml.router_path = '%s' AND module = 'system'", $path));
    $result = db_query("
      SELECT m.load_functions, m.to_arg_functions, m.access_callback, m.access_arguments, m.page_callback, m.page_arguments, m.title, m.title_callback, m.title_arguments, m.type, m.description, ml.*
      FROM {menu_links} ml
      LEFT JOIN {menu_router} m ON ml.link_path = m.path
      WHERE ml.plid = %d AND ml.menu_name = '%s' AND hidden = 0", $item['mlid'], $item['menu_name']);
    $paths[$path] = FALSE;
    while ($item = db_fetch_array($result)) {
      _menu_link_translate($item);
      if ($item['access']) {
        $paths[$path] = TRUE;
        break;
      }
    }
  }
  return $paths[$path];
}

/**
 * Implementation of hook_perm().
 */
function admin_perm() {
  return array('use admin toolbar');
}

/**
 * Implementation of hook_theme().
 */
function admin_theme($cache, $type, $theme, $path) {
  $path = drupal_get_path('module', 'admin');
  $items['admin_tab'] = array(
    'arguments' => array('tab' => array(), 'class' => ''),
    'path' => $path . '/theme',
    'file' => 'theme.inc',
  );
  $items['admin_toolbar'] = array(
    'arguments' => array(
      'blocks' => array(),
      'position' => 'ne',
      'layout' => 'horizontal',
      'behavior' => 'df',
    ),
    'template' => 'admin-toolbar',
    'path' => $path . '/theme',
    'file' => 'theme.inc',
  );
  $items['admin_drilldown_menu_tree_output'] = array(
    'arguments' => array('menu' => array()),
    'path' => $path . '/theme',
    'file' => 'theme.inc',
  );
  $items['admin_drilldown_menu_tree'] = array(
    'arguments' => array('menu' => array()),
    'path' => $path . '/theme',
    'file' => 'theme.inc',
  );
  $items['admin_drilldown_menu_item'] = array(
    'arguments' => array(
      'link' => NULL,
      'has_children' => NULL,
      'menu' => '',
      'in_active_trail' => FALSE,
      'extra_class' => NULL,
    ),
    'path' => $path . '/theme',
    'file' => 'theme.inc',
  );
  $items['admin_drilldown_menu_item_link'] = array(
    'arguments' => array('item' => array()),
    'path' => $path . '/theme',
    'file' => 'theme.inc',
  );
  $items['admin_panes'] = array(
    'arguments' => array('element' => array()),
    'template' => 'admin-panes',
    'path' => $path . '/theme',
    'file' => 'theme.inc',
  );
  $items['admin_settings_form'] = array(
    'arguments' => array('element' => array()),
    'path' => $path,
    'file' => 'admin.admin.inc',
  );
  return $items;
}

/**
 * Implementation of hook_theme_registry_alter().
 */
function admin_theme_registry_alter(&$theme_registry) {
  // Remove any existing instances of our pre page preprocessor.
  $position = array_search('admin_preprocess_pre_page', $theme_registry['page']['preprocess functions']);
  if ($position !== FALSE) {
    unset($theme_registry['page']['preprocess functions'][$position]);
  }
  // Add an additional page preprocess function prior to template_preprocess_page()
  // so that our blocks can include JS files as needed.
  $position = array_search('template_preprocess_page', $theme_registry['page']['preprocess functions']);
  if ($position !== FALSE) {
    array_splice($theme_registry['page']['preprocess functions'], $position, 0, 'admin_preprocess_pre_page');
  }
}

/**
 * Get variable settings or fallback to defaults.
 */
function admin_get_settings($key = NULL) {
  static $settings;
  if (!isset($settings)) {
    // Try to gather what information we can from the cookie.
    // Note that this information should not be trusted in any
    // way for affecting *anything* but trivial changes to the site.
    $cookie = array();
    if (isset($_REQUEST['DrupalAdminToolbar'])) {
      parse_str($_REQUEST['DrupalAdminToolbar'], $cookie);
    }

    $settings = variable_get('admin_toolbar', array()) + array(
      'layout' => 'vertical',
      'position' => 'nw',
      'behavior' => 'df',
      'blocks' => admin_get_default_blocks(),
      'expanded' => isset($cookie['expanded']) ? check_plain($cookie['expanded']) : 0,
      'active_tab' => isset($cookie['activeTab']) ? check_plain($cookie['activeTab']) : 0,
    );
    // Ensure that if behavior is set to autohide the toolbar is collapsed.
    if ($settings['behavior'] === 'ah') {
      $settings['expanded'] = 0;
    }
  }
  if (isset($key)) {
    return isset($settings[$key]) ? $settings[$key] : FALSE;
  }
  return $settings;
}

/**
 * Get all blocks that have declared themselves visible in the admin toolbar by default.
 */
function admin_get_default_blocks($reset = FALSE) {
  static $defaults;
  if (!isset($defaults) || $reset) {
    $cache = cache_get('admin_default_blocks');
    if ($cache && !$reset) {
      $defaults = $cache->data;
    }
    else {
      $defaults = array();
      foreach (module_implements('block') as $module) {
        $module_blocks = module_invoke($module, 'block', 'list');
        if ($module_blocks) {
          foreach ($module_blocks as $delta => $info) {
            if (isset($info['admin'])) {
              $defaults["{$module}-{$delta}"] = isset($info['cache']) ? $info['cache'] : BLOCK_NO_CACHE;
            }
          }
        }
      }
      cache_set('admin_default_blocks', $defaults);
    }
  }
  return $defaults;
}

/**
 * Set static cache for blocks enabled for the admin toolbar.
 */
function admin_set_admin_blocks($reset = FALSE) {
  static $blocks;
  if (!isset($blocks) || $reset) {
    $blocks = array();
    if (user_access('use admin toolbar') && $enabled_blocks = admin_get_settings('blocks')) {
      foreach ($enabled_blocks as $bid => $cache) {
        $block = NULL;
        $split = explode('-', $bid, 2);
        if (count($split) === 2) {
          list($module, $delta) = $split;
          $block = new stdClass();
          $block->module = $module;
          $block->delta = $delta;
          $block->cache = is_numeric($cache) ? $cache : BLOCK_NO_CACHE;
          $block->region = 'admin_toolbar'; // Fake the block region.
        }
        if (!empty($block)) {
          if ($_SERVER['REQUEST_METHOD'] == 'GET' && $cid = admin_block_get_cache_id($block)) {
            if ($cache = cache_get($cid, 'cache_block')) {
              $array = $cache->data;
            }
            else {
              $array = module_invoke($block->module, 'block', 'view', $block->delta);
              cache_set($cid, $array, 'cache_block', CACHE_TEMPORARY);
            }
          }
          else {
            $array = module_invoke($block->module, 'block', 'view', $block->delta);
          }
          if (!empty($array['content'])) {
            foreach ($array as $k => $v) {
              $block->{$k} = $v;
            }
            if ($block->module === 'block') {
              global $theme;
              $block->subject = db_result(db_query("SELECT title FROM {blocks} WHERE module = 'block' AND delta = '%s'", $delta));
            }
            // This is here simpy to trigger any preprocess functions that may
            // add javascript, etc. for this block. Note that the output is
            // thrown away.
            theme('block', $block);

            $blocks["{$module}-{$delta}"] = $block;
          }
        }
      }
    }
  }
  return $blocks;
}

/**
 * Get wrapper around admin blocks set.
 */
function admin_get_admin_blocks($reset = FALSE) {
  return admin_set_admin_blocks($reset);
}

/**
 * Preprocessor that runs *before* template_preprocess_page().
 */
function admin_preprocess_pre_page(&$vars) {
  admin_set_admin_blocks();
  if (user_access('use admin toolbar') && $trail = menu_get_active_trail()) {
    do {
      $last = array_pop($trail);
    } while (count($trail) && !($last['type'] & MENU_VISIBLE_IN_TREE));
    if ($last) {
      drupal_add_js(array('activePath' => url($last['href'])), 'setting');
    }
  }
}

/**
 * Implementation of hook_preprocess_page().
 */
function admin_preprocess_page(&$vars) {
  if (!admin_suppress(FALSE) && $blocks = admin_get_admin_blocks()) {
    $position = admin_get_settings('position');
    $layout = admin_get_settings('layout');
    $behavior = admin_get_settings('behavior');
    $class = admin_get_settings('expanded') ? 'admin-expanded' : '';
    $vars['body_classes'] .= " admin-{$position} admin-{$layout} admin-{$behavior} {$class} ";
  }
}

/**
 * Implementation of hook_footer().
 */
function admin_footer() {
  if (!admin_suppress(FALSE) && $blocks = admin_get_admin_blocks()) {
    $position = admin_get_settings('position');
    $layout = admin_get_settings('layout');
    $behavior = admin_get_settings('behavior');
    $class = admin_get_settings('expanded') ? 'admin-expanded' : '';
    return theme('admin_toolbar', $blocks, $position, $layout, $behavior);
  }
}

/**
 * Implementation of hook_suppress().
 *
 * Suppress display of administration toolbar.
 *
 * This function should be called from within another module's page callback
 * preferably using module_invoke_all('suppress') when the menu should not be
 * displayed. This is useful for modules that implement popup pages or other
 * special pages where the menu would be distracting or break the layout.
 *
 * @param $set
 *   Defaults to TRUE. If called before hook_footer(), the menu will not be
 *   displayed. If FALSE is passed, the suppression state is returned.
 */
function admin_suppress($set = TRUE) {
  static $suppress = FALSE;
  if (!empty($set) && $suppress === FALSE) {
    $suppress = TRUE;
  }
  return $suppress;
}

/**
 * My Account block.
 */
function admin_account_block() {
  $block = array(
    'subject' => t('My Account'),
    'content' => '',
  );
  global $user;
  if ($user->uid > 0) {
    $menu = array();
    $menu[] = array(
      'data' => l(t('View my account'), 'user/'. $user->uid),
      'class' => 'leaf',
    );
    $menu[] = array(
      'data' => l(t('Edit my account'), 'user/'. $user->uid .'/edit'),
      'class' => 'leaf',
    );
    $menu[] = array(
      'data' => l(t('Logout'), 'logout'),
      'class' => 'leaf',
    );

    $block['content'] = theme('item_list', $menu, NULL, 'ul', array('class' => 'menu'));
  }
  return $block;
}
