array( 'title' => t('Block rebuild'), 'description' => 'Rebuilds blocks in MongoDB', 'page callback' => 'mongodb_block_rehash', 'page arguments' => array(TRUE), 'access arguments' => array('administer site configuration'), ), ); } /** * Implements hook_flush_caches(). */ function mongodb_block_flush_caches() { return array('cache_block'); } /** * Implements hook_theme(). */ function mongodb_block_theme() { return array( 'block' => array( 'render element' => 'elements', 'template' => 'block', 'path' => drupal_get_path('module', 'block'), ), ); } /** * Implements hook_form_system_modules_alter(). * * Hide block, dashboard module. */ function mongodb_block_form_system_modules_alter(&$form, &$form_state) { unset($form['modules']['Core']['block']); unset($form['modules']['Core']['dashboard']); } /** * Implements hook_page_build(). * * Render blocks into their regions. */ function mongodb_block_page_build(&$page) { global $theme; // The theme system might not yet be initialized. We need $theme. drupal_theme_initialize(); // Grab only the current regions. $all_regions = system_region_list($theme); $collection = mongodb_collection('block'); if (!$collection->findOne()) { mongodb_block_rehash(); } $query = array(); if ($node = menu_get_object()) { $query['node_type']['$in'] = array('*', $node->type); } $parts = array_slice(arg(), 0, MENU_MAX_PARTS); $ancestors = menu_get_ancestors($parts); $ancestors[] = '%'; $query['pages']['$in'] = $ancestors; $query['region']['$in'] = array_keys($all_regions); $blocks = mongodb_block_render_blocks($collection->find($query)->sort(array('weight' => 1))); foreach ($blocks as $region => $region_blocks) { $page[$region] = mongodb_block_get_renderable_array($region_blocks); } } function mongodb_block_render_blocks($blocks_result) { $return = array(); foreach ($blocks_result as $block) { // Copy module and delta out of the _id. $block += $block['_id']; $block = (object)$block; // Render the block content if it has not been created already. if (!isset($block->content)) { // Try fetching the block from cache. Block caching is not compatible // with node_access modules. We also preserve the submission of forms in // blocks, by fetching from cache only if the request method is 'GET' // (or 'HEAD'). if (($_SERVER['REQUEST_METHOD'] == 'GET' || $_SERVER['REQUEST_METHOD'] == 'HEAD') && ($cid = mongodb_block_get_cache_id($block)) && ($cache = cache_get($cid, 'cache_block'))) { $array = $cache->data; } else { $array = module_invoke($block->module, 'block_view', $block->delta); // Allow modules to modify the block before it is viewed, via either // hook_block_view_MODULE_DELTA_alter() or hook_block_view_alter(). drupal_alter("block_view_{$block->module}_{$block->delta}", $array, $block); drupal_alter('block_view', $array, $block); if (isset($cid)) { cache_set($cid, $array, 'cache_block', CACHE_TEMPORARY); } } if (isset($array) && is_array($array)) { foreach ($array as $k => $v) { $block->$k = $v; } } } if (isset($block->content) && $block->content) { // Normalize to the drupal_render() structure. if (is_string($block->content)) { $block->content = array('#markup' => $block->content); } // Override default block title if a custom display title is present. if (!empty($block->title)) { // Check plain here to allow module generated titles to keep any // markup. $block->subject = $block->title == '' ? '' : check_plain($block->title); } if (!isset($block->subject)) { $block->subject = ''; } $return[$block->region]["{$block->module}_{$block->delta}"] = $block; } } return $return; } /** * Assemble the cache_id to use for a given block. * * The cache_id string reflects the viewing context for the current block * instance, obtained by concatenating the relevant context information * (user, page, ...) according to the block's cache settings (BLOCK_CACHE_* * constants). Two block instances can use the same cached content when * they share the same cache_id. * * Theme and language contexts are automatically differentiated. * * @param $block * @return * The string used as cache_id for the block. */ function mongodb_block_get_cache_id($block) { global $user; // User 1 being out of the regular 'roles define permissions' schema, // it brings too many chances of having unwanted output get in the cache // and later be served to other users. We therefore exclude user 1 from // block caching. if (variable_get('block_cache', 0) && !in_array($block->cache, array(DRUPAL_NO_CACHE, DRUPAL_CACHE_CUSTOM)) && $user->uid != 1) { // Start with common sub-patterns: block identification, theme, language. $cid_parts[] = $block->module; $cid_parts[] = $block->delta; $cid_parts += drupal_render_cid_parts($block->cache); return implode(':', $cid_parts); } } function mongodb_block_get_renderable_array($region_blocks) { $weight = 0; $build = array(); foreach ($region_blocks as $key => $block) { $build[$key] = $block->content; unset($block->content); $build[$key] += array( '#block' => $block, '#weight' => ++$weight, ); $build[$key]['#theme_wrappers'][] ='block'; } $build['#sorted'] = TRUE; return $build; } /** * Implements hook_mongodb_block_info_alter(). */ function mongodb_block_mongodb_block_info_alter(&$blocks) { // Enable the main content block. $blocks['system_main']['region'] = 'content'; $blocks['system_main']['weight'] = 0; $blocks['system_main']['status'] = 1; } function mongodb_block_rehash($redirect = FALSE) { $collection = mongodb_collection('block'); $theme = variable_get('theme_default', 'garland'); $regions = system_region_list($theme); $ids = array(); foreach (module_implements('block_info') as $module) { if ($blocks_current = module_invoke($module, 'block_info')) { foreach ($blocks_current as $delta => $block) { $block = array( '_id' => array('module' => $module, 'delta' => $delta), 'module' => $module, 'delta' => $delta, ) + $block + array( 'weight' => 0, 'pages' => array('%'), 'status' => 0, 'region' => BLOCK_REGION_NONE, ); if (isset($block['node_type'])) { $block['pages'] = array('node/%'); } else { $block['node_type'] = array('*'); } $blocks[$module . '_' . $delta] = $block; } } } drupal_alter('mongodb_block_info', $blocks); // Only store the block in the mongodb, if the status is enabled. foreach ($blocks as $key => $block) { if ($block['region'] != BLOCK_REGION_NONE && !isset($regions[$block['region']])) { drupal_set_message(t('The block %info was assigned to the invalid region %region and has been disabled.', array('%info' => $block['info'], '%region' => $block['region'])), 'warning'); $blocks[$key]['status'] = 0; $blocks[$key]['region'] = BLOCK_REGION_NONE; $block = $blocks[$key]; } $ids[] = $block['_id']; if ($block['status']) { mongodb_block_save_block($collection, $block); } } $collection->remove(array('_id' => array('$nin' => $ids))); if ($redirect) { drupal_set_message('Blocks rebuild done.'); drupal_goto('admin/structure'); } return $blocks; } function mongodb_block_save_block($collection, $block) { unset($block['module'], $block['delta'], $block['status']); $collection->save($block); } function mongodb_block_load($module, $delta) { $find = array('module' => $module, 'delta' => $delta); if ($block = mongodb_collection('block')->findOne(array('_id' => $find))) { $block += $block['_id']; return $block; } return $find; } /** * Process variables for block.tpl.php * * Prepare the values passed to the theme_block function to be passed * into a pluggable template engine. Uses block properties to generate a * series of template file suggestions. If none are found, the default * block.tpl.php is used. * * Most themes utilize their own copy of block.tpl.php. The default is located * inside "modules/block/block.tpl.php". Look in there for the full list of * variables. * * The $variables array contains the following arguments: * - $block * * @see block.tpl.php */ function template_preprocess_block(&$variables) { $block_counter = &drupal_static(__FUNCTION__, array()); $variables['block'] = $variables['elements']['#block']; // All blocks get an independent counter for each region. if (!isset($block_counter[$variables['block']->region])) { $block_counter[$variables['block']->region] = 1; } // Same with zebra striping. $variables['block_zebra'] = ($block_counter[$variables['block']->region] % 2) ? 'odd' : 'even'; $variables['block_id'] = $block_counter[$variables['block']->region]++; // Create the $content variable that templates expect. $variables['content'] = $variables['elements']['#children']; $variables['classes_array'][] = drupal_html_class('block-' . $variables['block']->module); $variables['theme_hook_suggestions'][] = 'block__' . $variables['block']->region; $variables['theme_hook_suggestions'][] = 'block__' . $variables['block']->module; $variables['theme_hook_suggestions'][] = 'block__' . $variables['block']->module . '__' . $variables['block']->delta; // Create a valid HTML ID and make sure it is unique. $variables['block_html_id'] = drupal_html_id('block-' . $variables['block']->module . '-' . $variables['block']->delta); }