diff --git a/modules/block/block.admin.inc b/modules/block/block.admin.inc index e3fb253..7952f0b 100644 --- a/modules/block/block.admin.inc +++ b/modules/block/block.admin.inc @@ -59,46 +59,45 @@ function block_admin_display_form($form, &$form_state, $blocks, $theme) { $form['#action'] = arg(4) ? url('admin/structure/block/list/' . $theme) : url('admin/structure/block'); $form['#tree'] = TRUE; - foreach ($blocks as $i => $block) { - $key = $block['module'] . '_' . $block['delta']; - $form[$key]['module'] = array( + foreach ($blocks as $block) { + $form[$block['block_id']]['block_instance_id'] = array( '#type' => 'value', - '#value' => $block['module'], + '#value' => $block['instances'][$theme]['block_instance_id'], ); - $form[$key]['delta'] = array( + $form[$block['block_id']]['status'] = array( '#type' => 'value', - '#value' => $block['delta'], + '#value' => $block['status'], ); - $form[$key]['info'] = array( + $form[$block['block_id']]['info'] = array( '#markup' => check_plain($block['info']), ); - $form[$key]['theme'] = array( + $form[$block['block_id']]['theme'] = array( '#type' => 'hidden', '#value' => $theme, ); - $form[$key]['weight'] = array( + $form[$block['block_id']]['weight'] = array( '#type' => 'weight', - '#default_value' => $block['weight'], + '#default_value' => $block['instances'][$theme]['weight'], '#delta' => $weight_delta, ); - $form[$key]['region'] = array( + $form[$block['block_id']]['region'] = array( '#type' => 'select', - '#default_value' => $block['region'], + '#default_value' => $block['instances'][$theme]['region'], '#options' => $block_regions, ); - $form[$key]['configure'] = array( + $form[$block['block_id']]['configure'] = array( '#markup' => l(t('configure'), - 'admin/structure/block/configure/' . $block['module'] . '/' . $block['delta']), + 'admin/structure/block/configure/' . $block['block_id']), ); if ($block['module'] == 'block') { - $form[$key]['delete'] = array( + $form[$block['block_id']]['delete'] = array( '#markup' => l(t('delete'), - 'admin/structure/block/delete/' . $block['delta']), + 'admin/structure/block/delete/' . $block['block_id']), ); } } // Do not allow disabling the main system content block. - unset($form['system_main']['region']['#options'][BLOCK_REGION_NONE]); + unset($form[block_entity_id('system', 'main')]['region']['#options'][BLOCK_REGION_NONE]); $form['submit'] = array( '#type' => 'submit', @@ -112,20 +111,19 @@ function block_admin_display_form($form, &$form_state, $blocks, $theme) { * Process main blocks administration form submissions. */ function block_admin_display_form_submit($form, &$form_state) { - foreach ($form_state['values'] as $block) { - $block['status'] = (int) ($block['region'] != BLOCK_REGION_NONE); - $block['region'] = $block['status'] ? $block['region'] : ''; - db_update('block') - ->fields(array( - 'status' => $block['status'], - 'weight' => $block['weight'], - 'region' => $block['region'], - )) - ->condition('module', $block['module']) - ->condition('delta', $block['delta']) - ->condition('theme', $block['theme']) - ->execute(); + foreach ($form_state['values'] as $block_id => $instance) { + $new_status = (int) ($instance['region'] != BLOCK_REGION_NONE); + if ($new_status != $instance['status']) { + db_update('block') + ->fields(array('status' => $new_status)) + ->condition('block_id', $block_id) + ->execute(); + } + + $instance['region'] = $new_status ? $instance['region'] : BLOCK_REGION_NONE; + block_instance_save($instance); } + drupal_set_message(t('The block settings have been updated.')); cache_clear_all(); } @@ -159,11 +157,11 @@ function _block_compare($a, $b) { return $status; } // Sort by region (in the order defined by theme .info file). - if ((!empty($a['region']) && !empty($b['region'])) && ($place = ($regions[$a['region']] - $regions[$b['region']]))) { + if (((isset($a['instances'][$theme_key]) && !empty($a['instances'][$theme_key]['region'])) && (isset($b['instances'][$theme_key]) && !empty($b['instances'][$theme_key]['region']))) && ($place = ($regions[$a['instances'][$theme_key]['region']] - $regions[$b['instances'][$theme_key]['region']]))) { return $place; } // Sort by weight. - $weight = $a['weight'] - $b['weight']; + $weight = $a['instances'][$theme_key]['weight'] - $b['instances'][$theme_key]['weight']; if ($weight) { return $weight; } @@ -174,43 +172,44 @@ function _block_compare($a, $b) { /** * Menu callback; displays the block configuration form. */ -function block_admin_configure($form, &$form_state, $module = NULL, $delta = 0) { +function block_admin_configure($form, &$form_state, $block) { + $form['block_id'] = array( + '#type' => 'value', + '#value' => (isset($block->block_id)) ? $block->block_id : NULL, + ); $form['module'] = array( '#type' => 'value', - '#value' => $module, + '#value' => $block->module, ); $form['delta'] = array( '#type' => 'value', - '#value' => $delta, + '#value' => isset($block->delta) ? $block->delta : NULL, ); - $edit = db_query("SELECT pages, visibility, custom, title FROM {block} WHERE module = :module AND delta = :delta", array( - ':module' => $module, - ':delta' => $delta, - ))->fetchAssoc(); - $form['block_settings'] = array( '#type' => 'fieldset', '#title' => t('Block specific settings'), '#collapsible' => TRUE, + '#weight' => field_attach_extra_weight($block->block_machine_name, 'block_settings'), ); $form['block_settings']['title'] = array( '#type' => 'textfield', '#title' => t('Block title'), '#maxlength' => 64, - '#description' => $module == 'block' ? t('The title of the block as shown to the user.') : t('Override the default title for the block. Use <none> to display no title, or leave blank to use the default block title.'), - '#default_value' => $edit['title'], + '#description' => $block->module == 'block' ? t('The title of the block as shown to the user.') : t('Override the default title for the block. Use <none> to display no title, or leave blank to use the default block title.'), + '#default_value' => isset($block->title) ? $block->title : '', '#weight' => -18, ); // Allow the user to define this block's region directly - $form['regions'] = array( + $form['instances'] = array( '#type' => 'fieldset', '#title' => t('Region settings'), '#collapsible' => TRUE, '#collapsed' => TRUE, '#description' => t('Specify in which region this block is displayed.'), '#tree' => TRUE, + '#weight' => field_attach_extra_weight($block->block_machine_name, 'instances'), ); $theme_default = variable_get('theme_default', 'garland'); @@ -219,13 +218,9 @@ function block_admin_configure($form, &$form_state, $module = NULL, $delta = 0) foreach (list_themes() as $key => $theme) { // Only display enabled themes if ($theme->status) { - $region = db_query("SELECT region FROM {block} WHERE module = :module AND delta = :delta AND theme = :theme", array( - ':module' => $module, - ':delta' => $delta, - ':theme' => $key, - ))->fetchField(); - - $form['regions'][$key] = array( + $region = (isset($block->instances[$key]['region'])) ? $block->instances[$key]['region'] : NULL; + + $form['instances'][$key]['region'] = array( '#type' => 'select', '#title' => t('!theme region', array('!theme' => $theme->info['name'])), '#default_value' => (!empty($region) ? $region : BLOCK_REGION_NONE), @@ -233,20 +228,38 @@ function block_admin_configure($form, &$form_state, $module = NULL, $delta = 0) '#expandable' => ($key !== $theme_default), '#weight' => ($key == $theme_default ? 9 : 10), ); + $form['instances'][$key]['block_instance_id'] = array( + '#type' => 'value', + '#value' => (isset($block->instances[$key]['block_instance_id'])) ? $block->instances[$key]['block_instance_id'] : NULL, + ); + $form['instances'][$key]['theme'] = array( + '#type' => 'value', + '#value' => $key, + ); + $form['instances'][$key]['weight'] = array( + '#type' => 'value', + '#value' => (isset($block->instances[$key]['weight'])) ? $block->instances[$key]['weight'] : 0, + ); } } // Module-specific block configurations. - if ($settings = module_invoke($module, 'block_configure', $delta)) { + if ($settings = module_invoke($block->module, 'block_configure', $block->delta)) { foreach ($settings as $k => $v) { $form['block_settings'][$k] = $v; } } // Get the block subject for the page title. - $info = module_invoke($module, 'block_info'); - if (isset($info[$delta])) { - drupal_set_title(t("'%name' block", array('%name' => $info[$delta]['info'])), PASS_THROUGH); + $info = module_invoke($block->module, 'block_info'); + if (isset($info[$block->delta])) { + drupal_set_title(t("'%name' block", array('%name' => $info[$block->delta]['info'])), PASS_THROUGH); + } + + // Attach fields. + if (isset($block->block_id)) { + //field_attach_load('block', array($block->block_id => $block)); + field_attach_form('block', $block, $form, $form_state); } $form['page_vis_settings'] = array( @@ -254,13 +267,17 @@ function block_admin_configure($form, &$form_state, $module = NULL, $delta = 0) '#title' => t('Page specific visibility settings'), '#collapsible' => TRUE, '#collapsed' => TRUE, + '#weight' => field_attach_extra_weight($block->block_machine_name, 'page_vis_settings'), ); $access = user_access('use PHP for settings'); - if ($edit['visibility'] == 2 && !$access) { + if (isset($block->visibility) && $block->visibility == 2 && !$access) { $form['page_vis_settings'] = array(); $form['page_vis_settings']['visibility'] = array('#type' => 'value', '#value' => 2); - $form['page_vis_settings']['pages'] = array('#type' => 'value', '#value' => $edit['pages']); + $form['page_vis_settings']['pages'] = array( + '#type' => 'value', + '#value' => isset($block->pages) ? $block->pages : '', + ); } else { $options = array(t('Every page except those specified below.'), t('Only the pages specified below.')); @@ -274,27 +291,25 @@ function block_admin_configure($form, &$form_state, $module = NULL, $delta = 0) '#type' => 'radios', '#title' => t('Show block on specific pages'), '#options' => $options, - '#default_value' => $edit['visibility'], + '#default_value' => isset($block->visibility) ? $block->visibility : NULL, ); $form['page_vis_settings']['pages'] = array( '#type' => 'textarea', '#title' => t('Pages'), - '#default_value' => $edit['pages'], + '#default_value' => isset($block->pages) ? $block->pages : '', '#description' => $description, ); } // Role-based visibility settings. - $default_role_options = db_query("SELECT rid FROM {block_role} WHERE module = :module AND delta = :delta", array( - ':module' => $module, - ':delta' => $delta, - ))->fetchCol(); + $default_role_options = (isset($block->roles)) ? $block->roles : array(); $role_options = db_query('SELECT rid, name FROM {role} ORDER BY name')->fetchAllKeyed(); $form['role_vis_settings'] = array( '#type' => 'fieldset', '#title' => t('Role specific visibility settings'), '#collapsible' => TRUE, '#collapsed' => TRUE, + '#weight' => field_attach_extra_weight($block->block_machine_name, 'role_vis_settings'), ); $form['role_vis_settings']['roles'] = array( '#type' => 'checkboxes', @@ -305,15 +320,13 @@ function block_admin_configure($form, &$form_state, $module = NULL, $delta = 0) ); // Content type specific configuration. - $default_type_options = db_query("SELECT type FROM {block_node_type} WHERE module = :module AND delta = :delta", array( - ':module' => $module, - ':delta' => $delta, - ))->fetchCol(); + $default_type_options = (isset($block->node_types)) ? $block->node_types : array(); $form['content_type_vis_settings'] = array( '#type' => 'fieldset', '#title' => t('Content type specific visibility settings'), '#collapsible' => TRUE, '#collapsed' => TRUE, + '#weight' => field_attach_extra_weight($block->block_machine_name, 'content_type_vis_settings'), ); $form['content_type_vis_settings']['types'] = array( '#type' => 'checkboxes', @@ -329,6 +342,7 @@ function block_admin_configure($form, &$form_state, $module = NULL, $delta = 0) '#title' => t('User specific visibility settings'), '#collapsible' => TRUE, '#collapsed' => TRUE, + '#weight' => field_attach_extra_weight($block->block_machine_name, 'user_vis_settings'), ); $form['user_vis_settings']['custom'] = array( '#type' => 'radios', @@ -339,12 +353,13 @@ function block_admin_configure($form, &$form_state, $module = NULL, $delta = 0) t('Hide this block by default but let individual users show it.') ), '#description' => t('Allow individual users to customize the visibility of this block in their account settings.'), - '#default_value' => $edit['custom'], + '#default_value' => isset($block->custom) ? $block->custom : NULL, ); $form['submit'] = array( '#type' => 'submit', '#value' => t('Save block'), + '#weight' => 99, ); return $form; @@ -352,70 +367,67 @@ function block_admin_configure($form, &$form_state, $module = NULL, $delta = 0) function block_admin_configure_validate($form, &$form_state) { if ($form_state['values']['module'] == 'block') { - $custom_block_exists = (bool) db_query_range('SELECT 1 FROM {block_custom} WHERE bid <> :bid AND info = :info', 0, 1, array( - ':bid' => $form_state['values']['delta'], + $custom_block_exists = (bool) db_query_range('SELECT 1 FROM {block_custom} WHERE block_id <> :block_id AND info = :info', 0, 1, array( + ':block_id' => $form_state['values']['block_id'], ':info' => $form_state['values']['info'], ))->fetchField(); if (empty($form_state['values']['info']) || $custom_block_exists) { form_set_error('info', t('Please ensure that each block description is unique.')); } } + + $block = (object) $form_state['values']; + $block->block_machine_name = $block->module . ':' . $block->delta; + field_attach_form_validate('block', $block, $form, $form_state); } function block_admin_configure_submit($form, &$form_state) { if (!form_get_errors()) { - db_update('block') - ->fields(array( - 'visibility' => (int) $form_state['values']['visibility'], - 'pages' => trim($form_state['values']['pages']), - 'custom' => (int) $form_state['values']['custom'], - 'title' => $form_state['values']['title'], - )) - ->condition('module', $form_state['values']['module']) - ->condition('delta', $form_state['values']['delta']) - ->execute(); + $block = $form_state['values']; + + $block['block_machine_name'] = $block['module'] . ':' . $block['delta']; + $block = (object) $block; + field_attach_submit('block', $block, $form, $form_state); + field_attach_presave('block', $block); + $block = (array) $block; + + // Make sure status = 1 any theme has the block in a valid region. + foreach ($block['instances'] as $instance) { + if ($instance['region'] != BLOCK_REGION_NONE) { + $block['status'] = 1; + break; + } + } + + block_save($block); db_delete('block_role') - ->condition('module', $form_state['values']['module']) - ->condition('delta', $form_state['values']['delta']) + ->condition('block_id', $block['block_id']) ->execute(); - $query = db_insert('block_role')->fields(array('rid', 'module', 'delta')); + $query = db_insert('block_role')->fields(array('block_id', 'rid')); foreach (array_filter($form_state['values']['roles']) as $rid) { $query->values(array( 'rid' => $rid, - 'module' => $form_state['values']['module'], - 'delta' => $form_state['values']['delta'], + 'block_id' => $block['block_id'], )); } $query->execute(); db_delete('block_node_type') - ->condition('module', $form_state['values']['module']) - ->condition('delta', $form_state['values']['delta']) + ->condition('block_id', $block['block_id']) ->execute(); - $query = db_insert('block_node_type')->fields(array('type', 'module', 'delta')); + $query = db_insert('block_node_type')->fields(array('type', 'block_id')); foreach (array_filter($form_state['values']['types']) as $type) { $query->values(array( 'type' => $type, - 'module' => $form_state['values']['module'], - 'delta' => $form_state['values']['delta'], + 'block_id' => $block['block_id'], )); } $query->execute(); - // Store regions per theme for this block - foreach ($form_state['values']['regions'] as $theme => $region) { - db_merge('block') - ->key(array('theme' => $theme, 'delta' => $form_state['values']['delta'], 'module' => $form_state['values']['module'])) - ->fields(array( - 'region' => $region, - 'pages' => trim($form_state['values']['pages']), - 'status' => (int) ($region != BLOCK_REGION_NONE), - )) - ->execute(); - } + field_attach_update('block', (object) $block); - module_invoke($form_state['values']['module'], 'block_save', $form_state['values']['delta'], $form_state['values']); + module_invoke($form_state['values']['module'], 'block_save', $form_state['values']['delta'], $block); drupal_set_message(t('The block configuration has been saved.')); cache_clear_all(); $form_state['redirect'] = 'admin/structure/block'; @@ -425,8 +437,25 @@ function block_admin_configure_submit($form, &$form_state) { /** * Menu callback: display the custom block addition form. */ -function block_add_block_form($form, &$form_state) { - return block_admin_configure($form, $form_state, 'block', NULL); +function block_add_block_form(&$form_state) { + $block = array( + 'block_machine_name' => 'block_0', + 'module' => 'block', + 'delta' => NULL, + ); + + $form = block_admin_configure(array(), $form_state, (object) $block); + // When creating a new custom block the Field API bundle for the block and + // block_body field have not been created yet. Collect input for the + // block_body field here and place it in the new Field API field once it has + // been created. + $form['block_body'] = array( + '#type' => 'textarea', + '#title' => t('Block body'), + '#text_format' => filter_default_format(), + '#required' => TRUE, + ); + return $form; } function block_add_block_form_validate($form, &$form_state) { @@ -441,65 +470,81 @@ function block_add_block_form_validate($form, &$form_state) { * Save the new custom block. */ function block_add_block_form_submit($form, &$form_state) { - $delta = db_insert('block_custom') + $block = $form_state['values']; + block_save($block); + + // Update the {block}.delta to match the block_id. + db_update('block') + ->fields(array('delta' => $block['block_id'])) + ->condition('block_id', $block['block_id']) + ->execute(); + + db_insert('block_custom') ->fields(array( - 'body' => $form_state['values']['body'], + 'block_id' => $block['block_id'], 'info' => $form_state['values']['info'], - 'format' => $form_state['values']['body_format'], )) ->execute(); - $query = db_insert('block')->fields(array('visibility', 'pages', 'custom', 'title', 'module', 'theme', 'status', 'weight', 'delta', 'cache')); - foreach (list_themes() as $key => $theme) { - if ($theme->status) { - $query->values(array( - 'visibility' => (int) $form_state['values']['visibility'], - 'pages' => trim($form_state['values']['pages']), - 'custom' => (int) $form_state['values']['custom'], - 'title' => $form_state['values']['title'], - 'module' => $form_state['values']['module'], - 'theme' => $theme->name, - 'status' => 0, - 'weight' => 0, - 'delta' => $delta, - 'cache' => DRUPAL_NO_CACHE, - )); - } - } - $query->execute(); - - $query = db_insert('block_role')->fields(array('rid', 'module', 'delta')); + $query = db_insert('block_role')->fields(array('rid', 'block_id')); foreach (array_filter($form_state['values']['roles']) as $rid) { $query->values(array( 'rid' => $rid, - 'module' => $form_state['values']['module'], - 'delta' => $delta, + 'block_id' => $block['block_id'], )); } $query->execute(); - $query = db_insert('block_node_type')->fields(array('type', 'module', 'delta')); + $query = db_insert('block_node_type')->fields(array('type', 'block_id')); foreach (array_filter($form_state['values']['types']) as $type) { $query->values(array( 'type' => $type, - 'module' => $form_state['values']['module'], - 'delta' => $delta, + 'block_id' => $form_state['block_id'], )); } $query->execute(); - - // Store regions per theme for this block - foreach ($form_state['values']['regions'] as $theme => $region) { - db_merge('block') - ->key(array('theme' => $theme, 'delta' => $delta, 'module' => $form_state['values']['module'])) - ->fields(array( - 'region' => $region, - 'pages' => trim($form_state['values']['pages']), - 'status' => (int) ($region != BLOCK_REGION_NONE), - )) - ->execute(); + + // Create the 'block_body' field if it doesn't already exist. + $field = field_info_field('block_body'); + if (empty($field)) { + $field = array( + 'field_name' => 'block_body', + 'type' => 'text_long', + ); + field_create_field($field); } + // Attach an instance of the 'block_body' field to the new block bundle. + $instance = array( + 'field_name' => 'block_body', + 'bundle' => 'block:' . $block['block_id'], + 'label' => t('Block body'), + 'description' => t('The content of the block as shown to the user.'), + 'required' => TRUE, + 'settings' => array('text_processing' => 1), + 'widget' => array( + 'type' => 'text_textarea', + 'label' => t('Block body'), + ), + 'display' => array( + 'full' => array('label' => 'hidden'), + ), + ); + field_create_instance($instance); + + // Move the content from the temporary block_body field to the new Field API + // block_body field. + $block['block_machine_name'] = 'block:' . $block['block_id']; + $block['block_body'] = array( + FIELD_LANGUAGE_NONE => array( + 0 => array( + 'value' => $form_state['values']['block_body'], + 'format' => $form_state['values']['block_body_format'], + ), + ), + ); + field_attach_insert('block', (object) $block); + drupal_set_message(t('The block has been created.')); cache_clear_all(); $form_state['redirect'] = 'admin/structure/block'; @@ -508,12 +553,16 @@ function block_add_block_form_submit($form, &$form_state) { /** * Menu callback; confirm deletion of custom blocks. */ -function block_custom_block_delete($form, &$form_state, $bid = 0) { - $custom_block = block_custom_block_get($bid); - $form['info'] = array('#type' => 'hidden', '#value' => $custom_block['info'] ? $custom_block['info'] : $custom_block['title']); - $form['bid'] = array('#type' => 'hidden', '#value' => $bid); +function block_custom_block_delete($form, &$form_state, $block) { + if ($block->module != 'block') { + drupal_set_message(t('The block %block can not be deleted. To remove this block disable the %module module', array('%block' => $block->info, '%module' => $block->module)), 'warning'); + drupal_goto('admin/structure/block'); + } + + $form['info'] = array('#type' => 'hidden', '#value' => $block->info ? $block->info : $block->info); + $form['block_id'] = array('#type' => 'hidden', '#value' => $block->block_id); - return confirm_form($form, t('Are you sure you want to delete the block %name?', array('%name' => $custom_block['info'])), 'admin/structure/block', '', t('Delete'), t('Cancel')); + return confirm_form($form, t('Are you sure you want to delete the block %name?', array('%name' => $block->info)), 'admin/structure/block', '', t('Delete'), t('Cancel')); } /** @@ -521,11 +570,10 @@ function block_custom_block_delete($form, &$form_state, $bid = 0) { */ function block_custom_block_delete_submit($form, &$form_state) { db_delete('block_custom') - ->condition('bid', $form_state['values']['bid']) + ->condition('block_id', $form_state['values']['block_id']) ->execute(); db_delete('block') - ->condition('module', 'block') - ->condition('delta', $form_state['values']['bid']) + ->condition('block_id', $form_state['values']['block_id']) ->execute(); drupal_set_message(t('The block %name has been removed.', array('%name' => $form_state['values']['info']))); cache_clear_all(); diff --git a/modules/block/block.install b/modules/block/block.install index 5565e05..70d0c2f 100644 --- a/modules/block/block.install +++ b/modules/block/block.install @@ -11,9 +11,9 @@ */ function block_schema() { $schema['block'] = array( - 'description' => 'Stores block settings, such as region and visibility settings.', + 'description' => 'Stores block settings, such as visibility settings.', 'fields' => array( - 'bid' => array( + 'block_id' => array( 'type' => 'serial', 'not null' => TRUE, 'description' => 'Primary Key: Unique block ID.', @@ -32,13 +32,6 @@ function block_schema() { 'default' => '0', 'description' => 'Unique ID for block within a module.', ), - 'theme' => array( - 'type' => 'varchar', - 'length' => 64, - 'not null' => TRUE, - 'default' => '', - 'description' => 'The theme under which the block settings apply.', - ), 'status' => array( 'type' => 'int', 'not null' => TRUE, @@ -46,20 +39,6 @@ function block_schema() { 'size' => 'tiny', 'description' => 'Block enabled status. (1 = enabled, 0 = disabled)', ), - 'weight' => array( - 'type' => 'int', - 'not null' => TRUE, - 'default' => 0, - 'size' => 'tiny', - 'description' => 'Block weight within region.', - ), - 'region' => array( - 'type' => 'varchar', - 'length' => 64, - 'not null' => TRUE, - 'default' => '', - 'description' => 'Theme region within which the block is set.', - ), 'custom' => array( 'type' => 'int', 'not null' => TRUE, @@ -94,29 +73,66 @@ function block_schema() { 'description' => 'Binary flag to indicate block cache mode. (-1: Do not cache, 1: Cache per role, 2: Cache per user, 4: Cache per page, 8: Block cache global) See BLOCK_CACHE_* constants in block.module for more detailed information.', ), ), - 'primary key' => array('bid'), + 'primary key' => array('block_id'), 'unique keys' => array( - 'tmd' => array('theme', 'module', 'delta'), + 'bmd' => array('block_id', 'module', 'delta'), ), 'indexes' => array( - 'list' => array('theme', 'status', 'region', 'weight', 'module'), + 'list' => array('status', 'module'), ), ); - $schema['block_role'] = array( - 'description' => 'Sets up access permissions for blocks based on user roles', + $schema['block_instance'] = array( + 'description' => 'Stores block intance information for themes and regions.', 'fields' => array( - 'module' => array( + 'block_instance_id' => array( + 'type' => 'serial', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'description' => 'Primary key: Unique block instance ID', + ), + 'block_id' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'description' => 'Block ID from {blocks}', + ), + 'theme' => array( 'type' => 'varchar', 'length' => 64, 'not null' => TRUE, - 'description' => "The block's origin module, from {block}.module.", + 'default' => '', + 'description' => 'The theme under which the block settings apply.', ), - 'delta' => array( + 'region' => array( 'type' => 'varchar', - 'length' => 32, + 'length' => 64, 'not null' => TRUE, - 'description' => "The block's unique delta within module, from {block}.delta.", + 'default' => '', + 'description' => 'Theme region within which the block is set.', + ), + 'weight' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + 'size' => 'tiny', + 'description' => 'Block weight within region.', + ), + ), + 'primary key' => array('block_instance_id'), + 'indexes' => array( + 'list' => array('theme', 'region', 'weight'), + ), + ); + + $schema['block_role'] = array( + 'description' => 'Sets up access permissions for blocks based on user roles', + 'fields' => array( + 'block_id' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'description' => 'Block ID from {blocks}', ), 'rid' => array( 'type' => 'int', @@ -125,7 +141,7 @@ function block_schema() { 'description' => "The user's role ID from {users_roles}.rid.", ), ), - 'primary key' => array('module', 'delta', 'rid'), + 'primary key' => array('block_id', 'rid'), 'indexes' => array( 'rid' => array('rid'), ), @@ -134,17 +150,11 @@ function block_schema() { $schema['block_node_type'] = array( 'description' => 'Sets up display criteria for blocks based on content types', 'fields' => array( - 'module' => array( - 'type' => 'varchar', - 'length' => 64, - 'not null' => TRUE, - 'description' => "The block's origin module, from {block}.module.", - ), - 'delta' => array( - 'type' => 'varchar', - 'length' => 32, + 'block_id' => array( + 'type' => 'int', + 'unsigned' => TRUE, 'not null' => TRUE, - 'description' => "The block's unique delta within module, from {block}.delta.", + 'description' => 'Block ID from {blocks}', ), 'type' => array( 'type' => 'varchar', @@ -153,7 +163,7 @@ function block_schema() { 'description' => "The machine-readable name of this type from {node_type}.type.", ), ), - 'primary key' => array('module', 'delta', 'type'), + 'primary key' => array('block_id', 'type'), 'indexes' => array( 'type' => array('type'), ), @@ -162,18 +172,12 @@ function block_schema() { $schema['block_custom'] = array( 'description' => 'Stores contents of custom-made blocks.', 'fields' => array( - 'bid' => array( - 'type' => 'serial', + 'block_id' => array( + 'type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'description' => "The block's {block}.bid.", ), - 'body' => array( - 'type' => 'text', - 'not null' => FALSE, - 'size' => 'big', - 'description' => 'Block contents.', - ), 'info' => array( 'type' => 'varchar', 'length' => 128, @@ -181,18 +185,10 @@ function block_schema() { 'default' => '', 'description' => 'Block description.', ), - 'format' => array( - 'type' => 'int', - 'size' => 'small', - 'not null' => TRUE, - 'default' => 0, - 'description' => "Block body's {filter_format}.format; for example, 1 = Filtered HTML.", - ) ), 'unique keys' => array( - 'info' => array('info'), + 'bi' => array('block_id', 'info'), ), - 'primary key' => array('bid'), ); $schema['cache_block'] = drupal_get_schema_unprocessed('system', 'cache'); @@ -264,3 +260,216 @@ function block_update_7001() { db_create_table('block_node_type', $schema['block_node_type']); } + +/** + * Refactor {block} table to {block} and {block_instance} and add a unique ID + * for every block. + */ +function block_update_7002() { + // Create the new {block_instance} table. + $schema['block_instance'] = array( + 'description' => 'Stores block intance information for themes and regions.', + 'fields' => array( + 'block_instance_id' => array( + 'type' => 'serial', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'description' => 'Primary key: Unique block instance ID', + ), + 'block_id' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'description' => 'Block ID from {blocks}', + ), + 'theme' => array( + 'type' => 'varchar', + 'length' => 64, + 'not null' => TRUE, + 'default' => '', + 'description' => 'The theme under which the block settings apply.', + ), + 'region' => array( + 'type' => 'varchar', + 'length' => 64, + 'not null' => TRUE, + 'default' => '', + 'description' => 'Theme region within which the block is set.', + ), + 'weight' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + 'size' => 'tiny', + 'description' => 'Block weight within region.', + ), + ), + 'primary key' => array('block_instance_id'), + 'unique keys' => array( + 'btr' => array('block_id', 'theme', 'region'), + ), + 'indexes' => array( + 'list' => array('theme', 'region', 'weight'), + ), + ); + db_create_table('block_instance', $schema['block_instance']); + + // Rename {block}.bid to {block}.block_id and adjust keys and indexes. + db_drop_unique_key('block', 'tmd'); + db_drop_index('block', 'list'); + db_change_field('block', 'bid', 'block_id', + array( + 'type' => 'serial', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'description' => 'Primary Key: Unique block ID.' + ), + array( + 'unique keys' => array( + 'bmd' => array('block_id', 'module', 'delta'), + ), + 'indexes' => array( + 'list' => array('status', 'module'), + ), + ) + ); + + $result = db_query('SELECT * FROM {block}'); + $blocks = array(); + // Copy data from {block} table to new {block_instance} table. Re-use + // block_id for blocks with the same module/delta and keep track of used + // block_id so we can remove the old rows. + foreach ($result as $block) { + if (isset($blocks[$block->module . ':' . $block->delta])) { + $block->block_id = $blocks[$block->module . ':' . $block->delta]; + } + db_query("INSERT INTO {block_instance} (block_id, theme, region, weight) VALUES (" . $block->block_id . ", '" . $block->theme . "', '" . $block->region . "', " . $block->weight . ")"); + $blocks[$block->module . ':' . $block->delta] = $block->block_id; + } + + // Replace module/delta columns with block_id column on {block_role} and + // {block_node_type} tables. + db_drop_primary_key('block_role'); + db_add_field('block_role', 'block_id', + array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'description' => 'Block ID from {blocks}', + 'initial' => 0 + ), + array( + 'primary key' => array('block_id', 'rid'), + ) + ); + db_drop_primary_key('block_node_type'); + db_add_field('block_node_type', 'block_id', + array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'description' => 'Block ID from {blocks}', + 'initial' => 0 + ), + array( + 'primary key' => array('block_id', 'type'), + ) + ); + foreach ($blocks as $key => $block_id) { + list($module, $delta) = explode(':', $key); + db_query("UPDATE {block_role} SET block_id = " . $block_id . " WHERE module = '" . $module . "' AND delta = '" . $delta . "'"); + db_query("UPDATE {block_node_type} SET block_id = " . $block_id . " WHERE module = '" . $module . "' AND delta = '" . $delta . "'"); + } + + // Remove unused columns from {block_role} and {block_node_type}. + db_drop_field('block_role', 'module'); + db_drop_field('block_role', 'delta'); + db_drop_field('block_node_type', 'module'); + db_drop_field('block_node_type', 'delta'); + + // Replace {block_custom}.bid with {block_custom}.block_id. + db_add_field('block_custom', 'block_id', + array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'description' => "The block's {block}.bid.", + 'default' => 0, + ) + ); + $result = db_query("SELECT * FROM {block} WHERE module='block'"); + foreach ($result as $block) { + if ($blocks[$block->module . ':' . $block->delta] == $block->block_id) { + db_query("UPDATE {block_custom} SET block_id = " . $block->block_id . " WHERE bid = " . $block->delta); + db_query("UPDATE {block} SET delta = " . $block->block_id . " WHERE block_id = " . $block->block_id); + } + } + db_drop_field('block_custom', 'bid'); + + // Any block that is not in $blocks is now a duplicate and can be removed. + db_delete('block') + ->condition('block_id', $blocks, 'NOT IN') + ->execute(); + + // Remove columns from {block} that are now in {block_instance}. + db_drop_field('block', 'theme'); + db_drop_field('block', 'region'); + db_drop_field('block', 'weight'); +} + +/** + * Convert custom block body to a field. + */ +function block_update_7003() { + // Create the 'block_body' field if it doesn't already exist. + $field = field_info_field('block_body'); + if (empty($field)) { + $field = array( + 'field_name' => 'block_body', + 'type' => 'text_long', + ); + field_create_field($field); + } + + // Attach an instance of the 'block_body' field to all custom blocks. + $custom_blocks = db_query('SELECT * FROM {block_custom}'); + foreach ($custom_blocks as $block) { + // Create a new bundle for the block + field_attach_create_bundle('block:' . $block->block_id); + + $instance = array( + 'field_name' => 'block_body', + 'bundle' => 'block:' . $block->block_id, + 'label' => t('Block body'), + 'description' => t('The content of the block as shown to the user.'), + 'required' => TRUE, + 'settings' => array('text_processing' => 1), + 'widget' => array( + 'type' => 'text_textarea', + 'label' => t('Block body'), + ), + 'display' => array( + 'full' => array('label' => 'hidden'), + ), + ); + field_create_instance($instance); + + // Move the block body to the new 'block_body' field. + $block->block_machine_name = 'block:' . $block->block_id; + $block->block_body = array( + FIELD_LANGUAGE_NONE => array( + 0 => array( + 'value' => $block->body, + 'format' => $block->format, + ), + ), + ); + // This is a core update and no contrib modules are enabled yet, so we can + // assume default field storage for a faster update. + field_sql_storage_field_storage_write('block', $block, FIELD_STORAGE_INSERT, array()); + } + + // Remove the now-obsolete body info from block_custom. + db_drop_field('block_custom', 'body'); + db_drop_field('block_custom', 'format'); +} diff --git a/modules/block/block.module b/modules/block/block.module index 2cf4749..f188886 100644 --- a/modules/block/block.module +++ b/modules/block/block.module @@ -95,18 +95,18 @@ function block_menu() { 'type' => MENU_CALLBACK, 'file' => 'block.admin.inc', ); - $items['admin/structure/block/configure'] = array( + $items['admin/structure/block/configure/%block'] = array( 'title' => 'Configure block', 'page callback' => 'drupal_get_form', - 'page arguments' => array('block_admin_configure'), + 'page arguments' => array('block_admin_configure', 4), 'access arguments' => array('administer blocks'), 'type' => MENU_CALLBACK, 'file' => 'block.admin.inc', ); - $items['admin/structure/block/delete'] = array( + $items['admin/structure/block/delete/%block'] = array( 'title' => 'Delete block', 'page callback' => 'drupal_get_form', - 'page arguments' => array('block_custom_block_delete'), + 'page arguments' => array('block_custom_block_delete', 4), 'access arguments' => array('administer blocks'), 'type' => MENU_CALLBACK, 'file' => 'block.admin.inc', @@ -168,16 +168,116 @@ function _block_custom_theme($theme = NULL) { } /** + * Implement hook_entity_info(). + */ +function block_entity_info() { + $return = array( + 'block' => array( + 'label' => t('Block'), + 'controller class' => 'BlockController', + 'base table' => 'block', + 'fieldable' => TRUE, + 'object keys' => array( + 'id' => 'block_id', + 'bundle' => 'block_machine_name', + ), + 'bundle keys' => array( + 'bundle' => 'block_machine_name', + ), + 'bundles' => array(), + ), + ); + + $blocks = db_query('SELECT block_id, module, delta FROM {block}'); + foreach ($blocks as $block) { + $return['block']['bundles'][$block->module . ':' . $block->delta] = array( + 'label' => $block->module . '_' . $block->delta, + 'admin' => array( + 'path' => 'admin/structure/block/configure/%block', + 'real path' => 'admin/structure/block/configure/' . $block->block_id, + 'access arguments' => array('administer blocks'), + 'bundle argument' => 4, + ), + ); + } + + return $return; +} + +/** + * Implement hook_field_build_modes(). + */ +function block_field_build_modules($obj_type) { + $modes = array(); + if ($obj_type == 'block') { + $modes = array( + 'full' => t('Block'), + ); + } + return $modes; +} + +/** + * Implement hook_field_extra_fields(). + */ +function block_field_extra_fields($bundle) { + $args = explode(':', $bundle); + $module = (isset($args[0])) ? $args[0] : NULL; + $delta = (isset($args[1])) ? $args[1] : NULL; + $is_block = block_entity_id($module, $delta); + + if ($is_block) { + $extra = array(); + $extra['block_settings'] = array( + 'label' => t('Block settings'), + 'description' => t('Block specific settings'), + 'weight' => -10, + ); + $extra['module_content'] = array( + 'label' => t('Module content'), + 'description' => t('Any content provided by the implementing module'), + 'weight' => 0, + ); + $extra['instances'] = array( + 'label' => t('Region settings'), + 'description' => t('Specify in which region this block is displayed.'), + 'weight' => 30, + ); + $extra['page_vis_settings'] = array( + 'label' => t('Page visibility settings'), + 'description' => t('Page specific visibility settings'), + 'weight' => 31, + ); + $extra['role_vis_settings'] = array( + 'label' => t('Role visibility settings'), + 'description' => t('Role specific visibility settings'), + 'weight' => 32, + ); + $extra['content_type_vis_settings'] = array( + 'label' => t('Content type visibility settings'), + 'description' => t('Show block for specific content types'), + 'weight' => 33, + ); + $extra['user_vis_settings'] = array( + 'label' => t('User visibility settings'), + 'description' => t('User specific visibility settings'), + 'weight' => 34, + ); + return $extra; + } +} + +/** * Implement hook_block_info(). */ function block_block_info() { $blocks = array(); - $result = db_query('SELECT bid, info FROM {block_custom} ORDER BY info'); + $result = db_query('SELECT block_id, info FROM {block_custom} ORDER BY info'); foreach ($result as $block) { - $blocks[$block->bid]['info'] = $block->info; + $blocks[$block->block_id]['info'] = $block->info; // Not worth caching. - $blocks[$block->bid]['cache'] = DRUPAL_NO_CACHE; + $blocks[$block->block_id]['cache'] = DRUPAL_NO_CACHE; } return $blocks; } @@ -186,7 +286,7 @@ function block_block_info() { * Implement hook_block_configure(). */ function block_block_configure($delta = 0) { - $custom_block = array('format' => filter_default_format()); + $custom_block = array(); if ($delta) { $custom_block = block_custom_block_get($delta); } @@ -194,6 +294,17 @@ function block_block_configure($delta = 0) { } /** + * Implement hook_block_load(). + */ +function block_block_load($blocks) { + foreach ($blocks as $block_id => $block) { + if ($block->module == 'block' && $custom_block = block_custom_block_get($block_id)) { + $blocks[$block_id]->info = $custom_block['info']; + } + } +} + +/** * Implement hook_block_save(). */ function block_block_save($delta = 0, $edit = array()) { @@ -202,13 +313,11 @@ function block_block_save($delta = 0, $edit = array()) { /** * Implement hook_block_view(). - * - * Generates the administrator-defined blocks for display. */ -function block_block_view($delta = 0, $edit = array()) { - $block = db_query('SELECT body, format FROM {block_custom} WHERE bid = :bid', array(':bid' => $delta))->fetchObject(); - $data['content'] = check_markup($block->body, $block->format, '', TRUE); - return $data; +function block_block_view($delta = 0) { + // All custom block content is handled by the field API, so return an empty + // array here. + return array('content' => array()); } /** @@ -253,6 +362,21 @@ function block_page_build(&$page) { } /** + * Implement hook_modules_enabled(). + */ +function block_modules_enabled($modules) { + // When a new module that provides a block is enabled we need to make sure + // that the block is saved to the database and given a block_id. + foreach ($modules as $module) { + if (function_exists($module . '_block_info')) { + _block_rehash(); + drupal_flush_all_caches(); + break; + } + } +} + +/** * Get a renderable array of a region containing all enabled blocks. * * @param $region @@ -310,14 +434,18 @@ function _block_rehash($theme = NULL) { $theme = $theme_key; } + $block_ids = db_select('block', 'b') + ->fields('b', array('block_id')) + ->execute() + ->fetchCol(); + $existing_blocks = block_load_multiple($block_ids); + $old_blocks = array(); - $result = db_query("SELECT * FROM {block} WHERE theme = :theme", array(':theme' => $theme)); - foreach ($result as $old_block) { - $old_block = is_object($old_block) ? get_object_vars($old_block) : $old_block; - $old_blocks[$old_block['module']][$old_block['delta']] = $old_block; + foreach ($existing_blocks as $block) { + $old_blocks[$block->module][$block->delta] = $block; } - $blocks = array(); + // Valid region names for the theme. $regions = system_region_list($theme); @@ -328,39 +456,54 @@ function _block_rehash($theme = NULL) { if (empty($old_blocks[$module][$delta])) { // If it's a new block, add identifiers. $block['module'] = $module; - $block['delta'] = $delta; - $block['theme'] = $theme; + $block['delta'] = $delta; + $block['instances'] = array( + $theme => array( + 'theme' => $theme, + 'region' => isset($block['region']) ? $block['region'] : BLOCK_REGION_NONE, + 'weight' => isset($block['weight']) ? $block['weight'] : NULL, + ) + ); if (!isset($block['pages'])) { // {block}.pages is type 'text', so it cannot have a // default value, and not null, so we need to provide // value if the module did not. $block['pages'] = ''; } - // Add defaults and save it into the database. - drupal_write_record('block', $block); - // Set region to none if not enabled. - $block['region'] = $block['status'] ? $block['region'] : BLOCK_REGION_NONE; + + block_save($block); + // Add to the list of blocks we return. - $blocks[] = $block; + $blocks[$block['block_id']] = $block; } else { // If it's an existing block, database settings should overwrite - // the code. But aside from 'info' everything that's definable in - // code is stored in the database and we do not store 'info', so we - // do not need to update the database here. + // the code. + // If the block does not have an instance for the selected theme create one now. + if (!isset($old_blocks[$module][$delta]->instances[$theme])) { + $instance = array( + 'block_id' => $old_blocks[$module][$delta]->block_id, + 'theme' => $theme, + 'region' => BLOCK_REGION_NONE, + ); + drupal_write_record('block_instance', $instance); + $old_blocks[$module][$delta]->instances[$theme] = $instance; + } // Add 'info' to this block. - $old_blocks[$module][$delta]['info'] = $block['info']; + $old_blocks[$module][$delta]->info = $block['info']; // If the region name does not exist, disable the block and assign it to none. - if (!empty($old_blocks[$module][$delta]['region']) && !isset($regions[$old_blocks[$module][$delta]['region']])) { - drupal_set_message(t('The block %info was assigned to the invalid region %region and has been disabled.', array('%info' => $old_blocks[$module][$delta]['info'], '%region' => $old_blocks[$module][$delta]['region'])), 'warning'); - $old_blocks[$module][$delta]['status'] = 0; - $old_blocks[$module][$delta]['region'] = BLOCK_REGION_NONE; + if ($old_blocks[$module][$delta]->instances[$theme]['region'] != BLOCK_REGION_NONE && !isset($regions[$old_blocks[$module][$delta]->instances[$theme]['region']])) { + drupal_set_message(t('The block %info was assigned to the invalid region %region and has been disabled.', array('%info' => $old_blocks[$module][$delta]->info, '%region' => $old_blocks[$module][$delta]->instances[$theme]['region'])), 'warning'); + $old_blocks[$module][$delta]->status = 0; + $old_blocks[$module][$delta]->instances[$theme]['region'] = BLOCK_REGION_NONE; + $block = (array) $old_blocks[$module][$delta]; + block_save($block); } else { - $old_blocks[$module][$delta]['region'] = $old_blocks[$module][$delta]['status'] ? $old_blocks[$module][$delta]['region'] : BLOCK_REGION_NONE; + $old_blocks[$module][$delta]->instances[$theme]['region'] = $old_blocks[$module][$delta]->instances[$theme]['region'] ? $old_blocks[$module][$delta]->instances[$theme]['region'] : BLOCK_REGION_NONE; } // Add this block to the list of blocks we return. - $blocks[] = $old_blocks[$module][$delta]; + $blocks[$old_blocks[$module][$delta]->block_id] = (array) $old_blocks[$module][$delta]; // Remove this block from the list of blocks to be deleted. unset($old_blocks[$module][$delta]); } @@ -372,9 +515,7 @@ function _block_rehash($theme = NULL) { foreach ($old_blocks as $module => $old_module_blocks) { foreach ($old_module_blocks as $delta => $block) { db_delete('block') - ->condition('module', $module) - ->condition('delta', $delta) - ->condition('theme', $theme) + ->condition('block_id', $block->block_id) ->execute(); } } @@ -394,39 +535,23 @@ function _block_rehash($theme = NULL) { * - body: Block contents. * - format: Filter ID of the filter format for the body. */ -function block_custom_block_get($bid) { - return db_query("SELECT * FROM {block_custom} WHERE bid = :bid", array(':bid' => $bid))->fetchAssoc(); +function block_custom_block_get($block_id) { + return db_query("SELECT * FROM {block_custom} WHERE block_id = :block_id", array(':block_id' => $block_id))->fetchAssoc(); } /** * Define the custom block form. */ function block_custom_block_form($edit = array()) { - $edit += array( - 'info' => '', - 'body' => '', - ); $form['info'] = array( '#type' => 'textfield', '#title' => t('Block description'), - '#default_value' => $edit['info'], + '#default_value' => isset($edit['info']) ? $edit['info'] : '', '#maxlength' => 64, '#description' => t('A brief description of your block. Used on the blocks administration page.', array('@overview' => url('admin/structure/block'))), '#required' => TRUE, '#weight' => -19, ); - $form['body_field']['#weight'] = -17; - $form['body_field']['body'] = array( - '#type' => 'textarea', - '#title' => t('Block body'), - '#default_value' => $edit['body'], - '#text_format' => isset($edit['format']) ? $edit['format'] : filter_default_format(), - '#rows' => 15, - '#description' => t('The content of the block as shown to the user.'), - '#required' => TRUE, - '#weight' => -17, - '#access' => filter_access(filter_format_load($edit['format'])), - ); return $form; } @@ -447,11 +572,9 @@ function block_custom_block_form($edit = array()) { function block_custom_block_save($edit, $delta) { db_update('block_custom') ->fields(array( - 'body' => $edit['body'], 'info' => $edit['info'], - 'format' => $edit['body_format'], )) - ->condition('bid', $delta) + ->condition('block_id', $edit['block_id']) ->execute(); return TRUE; } @@ -463,16 +586,16 @@ function block_form_user_profile_form_alter(&$form, &$form_state) { if ($form['#user_category'] == 'account') { $account = $form['#user']; $rids = array_keys($account->roles); - $result = db_query("SELECT DISTINCT b.* FROM {block} b LEFT JOIN {block_role} r ON b.module = r.module AND b.delta = r.delta WHERE b.status = 1 AND b.custom <> 0 AND (r.rid IN (:rids) OR r.rid IS NULL) ORDER BY b.weight, b.module", array(':rids' => $rids)); + $block_ids = db_query('SELECT b.block_id FROM {block} b LEFT JOIN {block_role} r ON b.block_id = r.block_id WHERE b.status = 1 AND b.custom <> 0 AND (r.rid IN (:rids) OR r.rid IS NULL) ORDER BY b.module', array(':rids' => $rids))->fetchAssoc(); + $blocks = block_load_multiple($block_ids); - $blocks = array(); - foreach ($result as $block) { + foreach ($blocks as $block) { $data = module_invoke($block->module, 'block_info'); if ($data[$block->delta]['info']) { - $blocks[$block->module][$block->delta] = array( + $form['block'][$block->block_id] = array( '#type' => 'checkbox', '#title' => check_plain($data[$block->delta]['info']), - '#default_value' => isset($account->block[$block->module][$block->delta]) ? $account->block[$block->module][$block->delta] : ($block->custom == 1), + '#default_value' => isset($account->block[$block->block_id]) ? $account->block[$block->block_id] : ($block->custom == 1), ); } } @@ -515,7 +638,7 @@ function block_system_themes_form_submit(&$form, &$form_state) { } if ($form_state['values']['admin_theme'] && $form_state['values']['admin_theme'] !== variable_get('admin_theme', 0)) { // If we're changing themes, make sure the theme has its blocks initialized. - $has_blocks = (bool) db_query_range('SELECT 1 FROM {block} WHERE theme = :theme', 0, 1, array(':theme' => $form_state['values']['admin_theme']))->fetchField(); + $has_blocks = (bool) db_query_range('SELECT 1 FROM {block_instance} WHERE theme = :theme', 0, 1, array(':theme' => $form_state['values']['admin_theme']))->fetchField(); if (!$has_blocks) { block_theme_initialize($form_state['values']['admin_theme']); } @@ -536,19 +659,23 @@ function block_system_themes_form_submit(&$form, &$form_state) { */ function block_theme_initialize($theme) { // Initialize theme's blocks if none already registered. - $has_blocks = (bool) db_query_range('SELECT 1 FROM {block} WHERE theme = :theme', 0, 1, array(':theme' => $theme))->fetchField(); + $has_blocks = (bool) db_query_range('SELECT 1 FROM {block_instance} WHERE theme = :theme', 0, 1, array(':theme' => $theme))->fetchField(); if (!$has_blocks) { $default_theme = variable_get('theme_default', 'garland'); $regions = system_region_list($theme); - $result = db_query("SELECT * FROM {block} WHERE theme = :theme", array(':theme' => $default_theme), array('fetch' => PDO::FETCH_ASSOC)); - foreach ($result as $block) { + $block_ids = db_select('block', 'b') + ->fields('b', array('block_id')) + ->execute() + ->fetchCol(); + $blocks = block_load_multiple($block_ids); + foreach ($blocks as $block) { // If the region isn't supported by the theme, assign the block to the theme's default region. - if (!array_key_exists($block['region'], $regions)) { - $block['region'] = system_default_region($theme); + if (!array_key_exists($block->instances[$default_theme]['region'], $regions)) { + $block->instances[$theme]['region'] = system_default_region($theme); } - $block['theme'] = $theme; - unset($block['bid']); - drupal_write_record('block', $block); + $block->instances[$theme]['theme'] = $theme; + $block = (array) $block; + block_save($block); } } } @@ -587,30 +714,207 @@ function block_list($region) { } /** + * Given a module and delta return the unique ID for a block. + * + * @param $module + * Name of the module that implements the block. + * @param $delta + * Unique ID of the block within the defining module. + * + * @return + * The numeric entity ID of the block or FALSE if there is no matching block. + * Resuts are statically cached. + */ +function block_entity_id($module, $delta) { + $block_ids = &drupal_static(__FUNCTION__); + + if (empty($block_ids)) { + $block_ids = array(); + $blocks = db_query('SELECT block_id, module, delta FROM {block}'); + foreach ($blocks as $block) { + $block_ids[$block->module . ':' . $block->delta] = $block->block_id; + } + } + + return (isset($block_ids[$module . ':' . $delta])) ? $block_ids[$module . ':' . $delta] : NULL; +} + +/** + * Load a block object from the database. + * + * @param $block_id + * The block_id of the block to load. + * @param $reset + * Whether to reset the block_load_multiple cache. + * @return + * A block object. + */ +function block_load($block_id, $reset = FALSE) { + $block = block_load_multiple(array($block_id), array(), $reset); + return $block ? $block[$block_id] : FALSE; +} + +/** + * Load block entities from the database. + * + * This function should be used whenever you need to load more than one block + * from the database. Blocks are loaded into memory and will not require + * database access if loaded again during the same page request. + * + * @see entity_load() + * + * @param $block_ids + * An array of block IDs. + * @param $conditions + * An array of conditions on the {block} table in the form 'field' => $value. + * @param $reset + * Whether to reset the internal block_load cache. + * + * @return + * An array of block objects indexed by block_id. + */ +function block_load_multiple($block_ids = array(), $conditions = array(), $reset = FALSE) { + return entity_load('block', $block_ids, $conditions, $reset); +} + +/** + * Save changes to a block or add a new block and related instances. + * + * @param $block + * The $block object to be saved. If $block->block_id is omitted a new node + * will be added. + */ +function block_save(&$block) { + // Check for an existing block with the same module and delta. + $block['block_id'] = isset($block['block_id']) ? $block['block_id'] : block_entity_id($block['module'], $block['delta']); + + // Add defaults and save it into the database. + $update = (isset($block['block_id'])) ? 'block_id' : FALSE; + drupal_write_record('block', $block, $update); + + // Add defaults and save each instance into the database. + if (isset($block['instances']) && is_array($block['instances'])) { + foreach ($block['instances'] as $key => $instance) { + $instance['block_id'] = $block['block_id']; + block_instance_save($instance); + $block['instances'][$key] = $instance; + } + } + + // Create a Field API bundle for the block. + if (!$update) { + field_attach_create_bundle($block['module'] . ':' . $block['delta']); + } + + // Clear the page and block caches. + cache_clear_all(); + // Flush the static cache mapping module:delta to a block_id. + drupal_static_reset('block_entity_id'); +} + +/** + * Save changes to an instances or add a new one. + * + * @param $instances + * A block instance array. + */ +function block_instance_save(&$instance) { + $instance['theme'] = (isset($instance['theme'])) ? $instance['theme'] : variable_get('theme_default', 'garland'); + $instance['region'] = (isset($instance['region'])) ? $instance['region'] : BLOCK_REGION_NONE; + $update = (isset($instance['block_instance_id'])) ? 'block_instance_id' : NULL; + drupal_write_record('block_instance', $instance, $update); +} + +/** + * Controller class for blocks. + * + * This extends the DrupalDefaultEntityController class, adding required + * special handling for block objects. + */ +class BlockController extends DrupalDefaultEntityController { + protected function buildQuery() { + parent::buildQuery(); + $this->query->leftJoin('block_role', 'br', 'base.block_id = br.block_id'); + $this->query->addField('br', 'rid'); + $this->query->leftJoin('block_node_type', 'bnt', 'base.block_id = bnt.block_id'); + $this->query->addField('bnt', 'type'); + } + + protected function attachLoad(&$records) { + // Combine multiple rows that represent the same block from the leftJoin + // into a single record. + foreach ($records as $record) { + if (isset($record->rid)) { + $roles[$record->block_id][$record->rid] = $record->rid; + unset($record->rid); + $record->roles = $roles[$record->block_id]; + } + elseif (!isset($record->roles)) { + $record->roles = array(); + } + + if (isset($record->type)) { + $node_types[$record->block_id][$record->type] = $record->type; + unset($record->type); + $record->node_types = $node_types[$record->block_id]; + } + elseif (!isset($record->nodes)) { + $record->node_types = array(); + } + $queried_blocks[$record->block_id] = $record; + } + $records = $queried_blocks; + + // Load instance information for each block and assign a + // block_machine_name. + foreach ($records as $record) { + $instances = db_query('SELECT block_instance_id, theme, region, weight FROM {block_instance} WHERE block_id = :block_id', array(':block_id' => $record->block_id), array('fetch' => PDO::FETCH_ASSOC)); + if ($instances) { + foreach ($instances as $instance) { + $record->instances[$instance['theme']] = $instance; + } + } + $record->block_machine_name = $record->module . ':' . $record->delta; + } + + parent::attachLoad($records); + } +} + +/** * Load blocks information from the database. */ function _block_load_blocks() { global $theme_key; $query = db_select('block', 'b'); + $query->join('block_instance', 'bi', 'bi.block_id = b.block_id'); $result = $query ->fields('b') - ->condition('b.theme', $theme_key) + ->fields('bi') + ->condition('bi.theme', $theme_key) ->condition('b.status', 1) - ->orderBy('b.region') - ->orderBy('b.weight') + ->orderBy('bi.region') + ->orderBy('bi.weight') ->orderBy('b.module') ->addTag('block_load') - ->execute(); + ->execute() + ->fetchCol(); + + $block_info = block_load_multiple($result); - $block_info = $result->fetchAllAssoc('bid'); // Allow modules to modify the block list. drupal_alter('block_info', $block_info); $blocks = array(); foreach ($block_info as $block) { + // Simplify the block structure for theming. + $block->theme = $block->instances[$theme_key]['theme']; + $block->region = $block->instances[$theme_key]['region']; + $block->weight = $block->instances[$theme_key]['weight']; $blocks[$block->region]["{$block->module}_{$block->delta}"] = $block; } + return $blocks; } @@ -623,22 +927,8 @@ function _block_load_blocks() { function block_block_info_alter(&$blocks) { global $user, $theme_key; - // Build an array of roles for each block. - $block_roles = array(); - $result = db_query('SELECT module, delta, rid FROM {block_role}'); - foreach ($result as $record) { - $block_roles[$record->module][$record->delta][] = $record->rid; - } - - // Build an array of node types for each block. - $block_node_types = array(); - $result = db_query('SELECT module, delta, type FROM {block_node_type}'); - foreach ($result as $record) { - $block_node_types[$record->module][$record->delta][] = $record->type; - } - foreach ($blocks as $key => $block) { - if ($block->theme != $theme_key || $block->status != 1) { + if (!isset($block->instances[$theme_key]) || $block->status != 1) { // This block was added by a contrib module, leave it in the list. continue; } @@ -646,7 +936,7 @@ function block_block_info_alter(&$blocks) { // If a block has no roles associated, it is displayed for every role. // For blocks with roles associated, if none of the user's roles matches // the settings from this block, remove it from the block list. - if (isset($block_roles[$block->module][$block->delta]) && !array_intersect($block_roles[$block->module][$block->delta], array_keys($user->roles))) { + if (count($block->roles) && !array_intersect($block->roles, array_keys($user->roles))) { // No match. unset($blocks[$key]); continue; @@ -655,11 +945,11 @@ function block_block_info_alter(&$blocks) { // If a block has no node types associated, it is displayed for every type. // For blocks with node types associated, if the node type does not match // the settings from this block, remove it from the block list. - if (isset($block_node_types[$block->module][$block->delta])) { + if (count($block->node_types)) { $node = menu_get_object(); if (!empty($node)) { // This is a node or node edit page. - if (!in_array($node->type, $block_node_types[$block->module][$block->delta])) { + if (!in_array($node->type, $block->node_types)) { // This block should not be displayed for this node type. unset($blocks[$key]); continue; @@ -667,7 +957,7 @@ function block_block_info_alter(&$blocks) { } elseif (arg(0) == 'node' && arg(1) == 'add' && in_array(arg(2), array_keys(node_type_get_types()))) { // This is a node creation page - if (!in_array(arg(2), $block_node_types[$block->module][$block->delta])) { + if (!in_array(arg(2), $block->node_types)) { // This block should not be displayed for this node type. unset($blocks[$key]); continue; @@ -682,8 +972,8 @@ function block_block_info_alter(&$blocks) { // Use the user's block visibility setting, if necessary. if ($block->custom != 0) { - if ($user->uid && isset($user->block[$block->module][$block->delta])) { - $enabled = $user->block[$block->module][$block->delta]; + if ($user->uid && isset($user->block[$block->block_id])) { + $enabled = $user->block[$block->block_id]; } else { $enabled = ($block->custom == 1); @@ -762,11 +1052,25 @@ function _block_render_blocks($region_blocks) { $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); - } + + // Normalize to the drupal_render() structure. + if (!empty($block->content) && is_string($block->content)) { + $block->content = array('#markup' => $block->content); + } + else if (!empty($block->content) && is_array($block->content)) { + // If a module provides the conent in a drupal_render() structure move + // the module provided content into a child array so it does not get + // overwritten when we attach the Fields API content. + $block->content = array('module_content' => $block->content); + $block->content['module_content']['#weight'] = field_attach_extra_weight($block->block_machine_name, 'module_content'); + } + else { + $block->content = array(); + } + + $block->content += field_attach_view('block', $block, 'full'); + + if (!empty($block->content)) { // Override default block title if a custom display title is present. if ($block->title) { // Check plain here to allow module generated titles to keep any @@ -872,26 +1176,18 @@ function block_user_role_delete($role) { } /** - * Implement hook_filter_format_delete(). - */ -function block_filter_format_delete($format, $fallback) { - db_update('block_custom') - ->fields(array('format' => $fallback->format)) - ->condition('format', $format->format) - ->execute(); -} - -/** * Implement hook_menu_delete(). */ function block_menu_delete($menu) { + $block_id = block_entity_id('menu', $menu['menu_name']); db_delete('block') - ->condition('module', 'menu') - ->condition('delta', $menu['menu_name']) + ->condition('block_id', $block_id) + ->execute(); + db_delete('block_instance') + ->condition('block_id', $block_id) ->execute(); db_delete('block_role') - ->condition('module', 'menu') - ->condition('delta', $menu['menu_name']) + ->condition('block_id', $block_id) ->execute(); } diff --git a/modules/block/block.test b/modules/block/block.test index 3b5d8bd..728a9ea 100644 --- a/modules/block/block.test +++ b/modules/block/block.test @@ -41,19 +41,20 @@ class BlockTestCase extends DrupalWebTestCase { $custom_block = array(); $custom_block['info'] = $this->randomName(8); $custom_block['title'] = $this->randomName(8); - $custom_block['body'] = $this->randomName(32); + $custom_block['block_body'] = $this->randomName(32); $this->drupalPost('admin/structure/block/add', $custom_block, t('Save block')); // Confirm that the custom block has been created, and then query the created bid. $this->assertText(t('The block has been created.'), t('Custom block successfully created.')); - $bid = db_query("SELECT bid FROM {block_custom} WHERE info = :info", array(':info' => $custom_block['info']))->fetchField(); + $block_id = db_query("SELECT block_id FROM {block_custom} WHERE info = :info", array(':info' => $custom_block['info']))->fetchField(); // Check to see if the custom block was created by checking that it's in the database.. - $this->assertNotNull($bid, t('Custom block found in database')); + $this->assertNotNull($block_id, t('Custom block found in database')); // Check if the block can be moved to all availble regions. + $custom_block['block_id'] = $block_id; $custom_block['module'] = 'block'; - $custom_block['delta'] = $bid; + $custom_block['delta'] = $block_id; foreach ($this->regions as $region) { $this->moveBlockToRegion($custom_block, $region); } @@ -61,7 +62,7 @@ class BlockTestCase extends DrupalWebTestCase { // Delete the created custom block & verify that it's been deleted and no longer appearing on the page. $this->drupalGet('admin/structure/block'); $this->clickLink(t('delete')); - $this->drupalPost('admin/structure/block/delete/' . $bid, array(), t('Delete')); + $this->drupalPost('admin/structure/block/delete/' . $block_id, array(), t('Delete')); $this->assertRaw(t('The block %title has been removed.', array('%title' => $custom_block['info'])), t('Custom block successfully deleted.')); $this->assertNoText(t($custom_block['title']), t('Custom block no longer appears on page.')); } @@ -74,14 +75,14 @@ class BlockTestCase extends DrupalWebTestCase { $custom_block = array(); $custom_block['info'] = $this->randomName(8); $custom_block['title'] = $this->randomName(8); - $custom_block['body'] = '

Full HTML

'; - $custom_block['body_format'] = 2; + $custom_block['block_body'] = '

Full HTML

'; + $custom_block['block_body_format'] = 2; $this->drupalPost('admin/structure/block/add', $custom_block, t('Save block')); // Set the created custom block to a specific region. - $bid = db_query("SELECT bid FROM {block_custom} WHERE info = :info", array(':info' => $custom_block['info']))->fetchField(); + $block_id = db_query("SELECT block_id FROM {block_custom} WHERE info = :info", array(':info' => $custom_block['info']))->fetchField(); $edit = array(); - $edit['block_' . $bid . '[region]'] = $this->regions[1]['name']; + $edit[$block_id . '[region]'] = $this->regions[1]['name']; $this->drupalPost('admin/structure/block', $edit, t('Save blocks')); // Confirm that the custom block is being displayed using configured text format. @@ -92,9 +93,9 @@ class BlockTestCase extends DrupalWebTestCase { // but can still submit the form without errors. $block_admin = $this->drupalCreateUser(array('administer blocks')); $this->drupalLogin($block_admin); - $this->drupalGet('admin/structure/block/configure/block/' . $bid); + $this->drupalGet('admin/structure/block/configure/block/' . $block_id); $this->assertNoText(t('Block body')); - $this->drupalPost('admin/structure/block/configure/block/' . $bid, array(), t('Save block')); + $this->drupalPost('admin/structure/block/configure/block/' . $block_id, array(), t('Save block')); $this->assertNoText(t('Please ensure that each block description is unique.')); // Confirm that the custom block is still being displayed using configured text format. @@ -107,20 +108,20 @@ class BlockTestCase extends DrupalWebTestCase { */ function testBlockVisibility() { $block = array(); - + // Create a random title for the block $title = $this->randomName(8); - + // Create the custom block $custom_block = array(); $custom_block['info'] = $this->randomName(8); $custom_block['title'] = $title; - $custom_block['body'] = $this->randomName(32); + $custom_block['block_body'] = $this->randomName(32); $this->drupalPost('admin/structure/block/add', $custom_block, t('Save block')); - - $bid = db_query("SELECT bid FROM {block_custom} WHERE info = :info", array(':info' => $custom_block['info']))->fetchField(); + + $block_id = db_query("SELECT block_id FROM {block_custom} WHERE info = :info", array(':info' => $custom_block['info']))->fetchField(); $block['module'] = 'block'; - $block['delta'] = $bid; + $block['delta'] = $block_id; $block['title'] = $title; // Set the block to be hidden on any user path, and to be shown only to @@ -128,9 +129,10 @@ class BlockTestCase extends DrupalWebTestCase { $edit = array(); $edit['pages'] = 'user*'; $edit['roles[2]'] = TRUE; - $this->drupalPost('admin/structure/block/configure/' . $block['module'] . '/' . $block['delta'], $edit, t('Save block')); + $this->drupalPost('admin/structure/block/configure/' . $block_id, $edit, t('Save block')); // Move block to the first sidebar. + $block['block_id'] = $block_id; $this->moveBlockToRegion($block, $this->regions[1]); $this->drupalGet(''); @@ -156,24 +158,26 @@ class BlockTestCase extends DrupalWebTestCase { $block['title'] = $this->randomName(8); // Set block title to confirm that interface works and override any custom titles. - $this->drupalPost('admin/structure/block/configure/' . $block['module'] . '/' . $block['delta'], array('title' => $block['title']), t('Save block')); + $block_id = block_entity_id($block['module'], $block['delta']); + $this->drupalPost('admin/structure/block/configure/' . $block_id, array('title' => $block['title']), t('Save block')); $this->assertText(t('The block configuration has been saved.'), t('Block title set.')); - $bid = db_query("SELECT bid FROM {block} WHERE module = :module AND delta = :delta", array( + $block_id = db_query("SELECT block_id FROM {block} WHERE module = :module AND delta = :delta", array( ':module' => $block['module'], ':delta' => $block['delta'], ))->fetchField(); // Check to see if the block was created by checking that it's in the database. - $this->assertNotNull($bid, t('Block found in database')); + $this->assertNotNull($block_id, t('Block found in database')); // Check if the block can be moved to all availble regions. + $block['block_id'] = $block_id; foreach ($this->regions as $region) { $this->moveBlockToRegion($block, $region); } // Set the block to the disabled region. $edit = array(); - $edit[$block['module'] . '_' . $block['delta'] . '[region]'] = '-1'; + $edit[$block_id . '[region]'] = '-1'; $this->drupalPost('admin/structure/block', $edit, t('Save blocks')); // Confirm that the block was moved to the proper region. @@ -181,16 +185,16 @@ class BlockTestCase extends DrupalWebTestCase { $this->assertNoText(t($block['title']), t('Block no longer appears on page.')); // Confirm that the regions xpath is not availble - $xpath = '//div[@id="block-block-' . $bid . '"]/*'; + $xpath = '//div[@id="block-block-' . $block_id . '"]/*'; $this->assertNoFieldByXPath($xpath, FALSE, t('Custom block found in no regions.')); // For convenience of developers, put the navigation block back. $edit = array(); - $edit[$block['module'] . '_' . $block['delta'] . '[region]'] = $this->regions[1]['name']; + $edit[$block_id . '[region]'] = $this->regions[1]['name']; $this->drupalPost('admin/structure/block', $edit, t('Save blocks')); $this->assertText(t('The block settings have been updated.'), t('Block successfully move to first sidebar region.')); - $this->drupalPost('admin/structure/block/configure/' . $block['module'] . '/' . $block['delta'], array('title' => 'Navigation'), t('Save block')); + $this->drupalPost('admin/structure/block/configure/' . $block_id, array('title' => 'Navigation'), t('Save block')); $this->assertText(t('The block configuration has been saved.'), t('Block title set.')); } @@ -202,7 +206,7 @@ class BlockTestCase extends DrupalWebTestCase { // Set the created block to a specific region. $edit = array(); - $edit[$block['module'] . '_' . $block['delta'] . '[region]'] = $region['name']; + $edit[$block['block_id'] . '[region]'] = $region['name']; $this->drupalPost('admin/structure/block', $edit, t('Save blocks')); // Confirm that the block was moved to the proper region. @@ -259,25 +263,22 @@ class NewDefaultThemeBlocks extends DrupalWebTestCase { $this->drupalLogin($admin_user); // Ensure no other theme's blocks are in the block table yet. - $count = db_query_range("SELECT 1 FROM {block} WHERE theme NOT IN ('garland', 'seven')", 0, 1)->fetchField(); + $count = db_query_range("SELECT 1 FROM {block_instance} WHERE theme NOT IN ('garland', 'seven')", 0, 1)->fetchField(); $this->assertFalse($count, t('Only Garland and Seven have blocks.')); // Populate list of all blocks for matching against new theme. $blocks = array(); - $result = db_query("SELECT * FROM {block} WHERE theme = 'garland'"); + $result = db_query("SELECT b.* FROM {block} b INNER JOIN {block_instance} bi ON bi.block_id = b.block_id WHERE bi.theme = 'garland'"); foreach ($result as $block) { - // $block->theme and $block->bid will not match, so remove them. - unset($block->theme, $block->bid); - $blocks[$block->module][$block->delta] = $block; + $blocks[$block->block_id] = $block; } // Turn on the Stark theme and ensure that it contains all of the blocks // that Garland did. $this->drupalPost('admin/appearance', array('theme_default' => 'stark'), t('Save configuration')); - $result = db_query("SELECT * FROM {block} WHERE theme='stark'"); + $result = db_query("SELECT b.* FROM {block} b INNER JOIN {block_instance} bi ON bi.block_id = b.block_id WHERE bi.theme='stark'"); foreach ($result as $block) { - unset($block->theme, $block->bid); - $this->assertEqual($blocks[$block->module][$block->delta], $block, t('Block %name matched', array('%name' => $block->module . '-' . $block->delta))); + $this->assertEqual($blocks[$block->block_id], $block, t('Block %name matched', array('%name' => $block->module . '-' . $block->delta))); } } } diff --git a/modules/blog/blog.test b/modules/blog/blog.test index 7d2aa41..f2cec08 100644 --- a/modules/blog/blog.test +++ b/modules/blog/blog.test @@ -62,7 +62,8 @@ class BlogTestCase extends DrupalWebTestCase { $this->drupalLogin($this->big_user); // Enable the recent blog block. $edit = array(); - $edit['blog_recent[region]'] = 'sidebar_second'; + $block_id = block_entity_id('blog', 'recent'); + $edit[$block_id . '[region]'] = 'sidebar_second'; $this->drupalPost('admin/structure/block', $edit, t('Save blocks')); $this->assertResponse(200); diff --git a/modules/book/book.test b/modules/book/book.test index 861c04c..366b27f 100644 --- a/modules/book/book.test +++ b/modules/book/book.test @@ -215,12 +215,13 @@ class BookBlockTestCase extends DrupalWebTestCase { function testBookNavigationBlock() { // Set block title to confirm that the interface is availble. - $this->drupalPost('admin/structure/block/configure/book/navigation', array('title' => $this->randomName(8)), t('Save block')); + $block_id = block_entity_id('book', 'navigation'); + $this->drupalPost('admin/structure/block/configure/' . $block_id, array('title' => $this->randomName(8)), t('Save block')); $this->assertText(t('The block configuration has been saved.'), t('Block configuration set.')); // Set the block to a region to confirm block is availble. $edit = array(); - $edit['book_navigation[region]'] = 'footer'; + $edit[$block_id . '[region]'] = 'footer'; $this->drupalPost('admin/structure/block', $edit, t('Save blocks')); $this->assertText(t('The block settings have been updated.'), t('Block successfully move to footer region.')); } diff --git a/modules/comment/comment.test b/modules/comment/comment.test index aed0043..da89041 100644 --- a/modules/comment/comment.test +++ b/modules/comment/comment.test @@ -720,8 +720,9 @@ class CommentBlockFunctionalTest extends CommentHelperCase { $this->drupalLogin($this->admin_user); // Set the block to a region to confirm block is available. + $block_id = block_entity_id('comment', 'recent'); $edit = array( - 'comment_recent[region]' => 'sidebar_first', + $block_id . '[region]' => 'sidebar_first', ); $this->drupalPost('admin/structure/block', $edit, t('Save blocks')); $this->assertText(t('The block settings have been updated.'), t('Block saved to first sidebar region.')); @@ -731,7 +732,7 @@ class CommentBlockFunctionalTest extends CommentHelperCase { 'title' => $this->randomName(), 'comment_block_count' => 2, ); - $this->drupalPost('admin/structure/block/configure/comment/recent', $block, t('Save block')); + $this->drupalPost('admin/structure/block/configure/' . $block_id, $block, t('Save block')); $this->assertText(t('The block configuration has been saved.'), t('Block saved.')); // Add some test comments, one without a subject. @@ -760,7 +761,7 @@ class CommentBlockFunctionalTest extends CommentHelperCase { $block = array( 'comment_block_count' => 10, ); - $this->drupalPost('admin/structure/block/configure/comment/recent', $block, t('Save block')); + $this->drupalPost('admin/structure/block/configure/' . $block_id, $block, t('Save block')); $this->assertText(t('The block configuration has been saved.'), t('Block saved.')); // Post an additional comment. diff --git a/modules/dashboard/dashboard.module b/modules/dashboard/dashboard.module index f4c6495..452fdf0 100644 --- a/modules/dashboard/dashboard.module +++ b/modules/dashboard/dashboard.module @@ -49,8 +49,10 @@ function dashboard_menu() { 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]); + foreach($block->instances as $block_instance) { + if (in_array($block_instance['region'], dashboard_regions())) { + unset($blocks[$key]); + } } } } diff --git a/modules/filter/filter.test b/modules/filter/filter.test index d72a144..a49cdfc 100644 --- a/modules/filter/filter.test +++ b/modules/filter/filter.test @@ -1130,8 +1130,8 @@ class FilterHooksTestCase extends DrupalWebTestCase { } function setUp() { - parent::setUp('block', 'filter_test'); - $admin_user = $this->drupalCreateUser(array('administer filters', 'administer blocks')); + parent::setUp('filter_test'); + $admin_user = $this->drupalCreateUser(array('administer filters', 'administer nodes', 'administer comments')); $this->drupalLogin($admin_user); } @@ -1157,29 +1157,30 @@ class FilterHooksTestCase extends DrupalWebTestCase { $this->assertRaw(t('The text format %format has been updated.', array('%format' => $name)), t('Format successfully updated.')); $this->assertText('hook_filter_format_update invoked.', t('hook_filter_format_update() was invoked.')); - // Add a new custom block. - $custom_block = array(); - $custom_block['info'] = $this->randomName(8); - $custom_block['title'] = $this->randomName(8); - $custom_block['body'] = $this->randomName(32); - // Use the format created. - $custom_block['body_format'] = $format_id; - $this->drupalPost('admin/structure/block/add', $custom_block, t('Save block')); - $this->assertText(t('The block has been created.'), t('New block successfully created.')); + // Create a node and post a comment to it using the format created. + $node = $this->drupalCreateNode(array('type' => 'article', 'promote' => 1, 'comment' => COMMENT_NODE_OPEN)); + $this->assertTrue($node, t('Article node created.')); + $edit = array(); + $edit['subject'] = $this->randomName(8); + $edit['comment'] = $this->randomName(32); + $edit['comment_format'] = $format; + $this->drupalPost('node/' . $node->nid, $edit, t('Save')); + $this->assertText($edit['subject'], t('Comment created.')); + + // Verify the new comment is in the database. + $cid = db_query("SELECT cid FROM {comment} WHERE nid = :nid AND subject = :subject", array(':nid' => $node->nid, ':subject' => $edit['subject']))->fetchField(); + $this->assertNotNull($cid, t('New comment found in database')); - // Verify the new block is in the database. - $bid = db_query("SELECT bid FROM {block_custom} WHERE info = :info", array(':info' => $custom_block['info']))->fetchField(); - $this->assertNotNull($bid, t('New block found in database')); // Delete the text format. $this->drupalPost('admin/config/content/formats/' . $format_id . '/delete', array(), t('Delete')); $this->assertRaw(t('Deleted text format %format.', array('%format' => $name)), t('Format successfully deleted.')); $this->assertText('hook_filter_format_delete invoked.', t('hook_filter_format_delete() was invoked.')); - // Verify that the deleted format was replaced with the fallback format. - $current_format = db_select('block_custom', 'b') - ->fields('b', array('format')) - ->condition('bid', $bid) + // Verify that the deleted format was replaced with the default format. + $current_format = db_select('comment', 'c') + ->fields('c', array('format')) + ->condition('cid', $cid) ->execute() ->fetchField(); $this->assertEqual($current_format, filter_fallback_format(), t('Deleted text format replaced with the fallback format.')); diff --git a/modules/forum/forum.test b/modules/forum/forum.test index 1e3f082..3d55b90 100644 --- a/modules/forum/forum.test +++ b/modules/forum/forum.test @@ -81,14 +81,14 @@ class ForumTestCase extends DrupalWebTestCase { // Enable the active forum block. $edit = array(); - $edit['forum_active[region]'] = 'sidebar_second'; + $edit[block_entity_id('forum', 'active') . '[region]'] = 'sidebar_second'; $this->drupalPost('admin/structure/block', $edit, t('Save blocks')); $this->assertResponse(200); $this->assertText(t('The block settings have been updated.'), t('Active forum topics forum block was enabled')); // Enable the new forum block. $edit = array(); - $edit['forum_new[region]'] = 'sidebar_second'; + $edit[block_entity_id('forum', 'new') . '[region]'] = 'sidebar_second'; $this->drupalPost('admin/structure/block', $edit, t('Save blocks')); $this->assertResponse(200); $this->assertText(t('The block settings have been updated.'), t('[New forum topics] Forum block was enabled')); diff --git a/modules/locale/locale.test b/modules/locale/locale.test index ade91bc..b254328 100644 --- a/modules/locale/locale.test +++ b/modules/locale/locale.test @@ -1069,7 +1069,7 @@ class LanguageSwitchingFunctionalTest extends DrupalWebTestCase { function testLanguageBlock() { // Enable the language switching block. $edit = array( - 'locale_language[region]' => 'sidebar_first', + block_entity_id('locale', 'language') . '[region]' => 'sidebar_first', ); $this->drupalPost('admin/structure/block', $edit, t('Save blocks')); diff --git a/modules/menu/menu.test b/modules/menu/menu.test index 6bcf56e..182e7aa 100644 --- a/modules/menu/menu.test +++ b/modules/menu/menu.test @@ -154,10 +154,18 @@ class MenuTestCase extends DrupalWebTestCase { $this->drupalGet('admin/structure/menu'); $this->assertText($title, 'Menu created'); + // After creating a custom menu there is also a new block provided by menu + // that has not been saved to the {block} table yet. Save the new block and + // flush the block_entity_id cache so that the remainder of the tests can + // use the new block. + _block_rehash(); + drupal_static_reset('block_entity_id'); + // Enable the custom menu block. $menu_name = 'menu-' . $menu_name; // Drupal prepends the name with 'menu-'. $edit = array(); - $edit['menu_' . $menu_name . '[region]'] = 'sidebar_first'; + $block_id = block_entity_id('menu', $menu_name); + $edit[$block_id . '[region]'] = 'sidebar_first'; $this->drupalPost('admin/structure/block', $edit, t('Save blocks')); $this->assertResponse(200); $this->assertText(t('The block settings have been updated.'), t('Custom menu block was enabled')); diff --git a/modules/node/node.test b/modules/node/node.test index 26e9e09..3c3cb54 100644 --- a/modules/node/node.test +++ b/modules/node/node.test @@ -490,12 +490,13 @@ class NodeBlockTestCase extends DrupalWebTestCase { function testSearchFormBlock() { // Set block title to confirm that the interface is availble. - $this->drupalPost('admin/structure/block/configure/node/syndicate', array('title' => $this->randomName(8)), t('Save block')); + $block_id = block_entity_id('node', 'syndicate'); + $this->drupalPost('admin/structure/block/configure/' . $block_id, array('title' => $this->randomName(8)), t('Save block')); $this->assertText(t('The block configuration has been saved.'), t('Block configuration set.')); // Set the block to a region to confirm block is availble. $edit = array(); - $edit['node_syndicate[region]'] = 'footer'; + $edit[$block_id . '[region]'] = 'footer'; $this->drupalPost('admin/structure/block', $edit, t('Save blocks')); $this->assertText(t('The block settings have been updated.'), t('Block successfully move to footer region.')); } diff --git a/modules/poll/poll.test b/modules/poll/poll.test index afe10db..c68160f 100644 --- a/modules/poll/poll.test +++ b/modules/poll/poll.test @@ -260,12 +260,13 @@ class PollBlockTestCase extends PollTestCase { function testRecentBlock() { // Set block title to confirm that the interface is available. - $this->drupalPost('admin/structure/block/configure/poll/recent', array('title' => $this->randomName(8)), t('Save block')); + $block_id = block_entity_id('poll', 'recent'); + $this->drupalPost('admin/structure/block/configure/' . $block_id, array('title' => $this->randomName(8)), t('Save block')); $this->assertText(t('The block configuration has been saved.'), t('Block configuration set.')); // Set the block to a region to confirm block is available. $edit = array(); - $edit['poll_recent[region]'] = 'footer'; + $edit[$block_id . '[region]'] = 'footer'; $this->drupalPost('admin/structure/block', $edit, t('Save blocks')); $this->assertText(t('The block settings have been updated.'), t('Block successfully move to footer region.')); diff --git a/modules/profile/profile.test b/modules/profile/profile.test index 0dae49a..dcb9b8b 100644 --- a/modules/profile/profile.test +++ b/modules/profile/profile.test @@ -314,12 +314,13 @@ class ProfileBlockTestCase extends DrupalWebTestCase { function testAuthorInformationBlock() { // Set block title to confirm that the interface is availble. - $this->drupalPost('admin/structure/block/configure/profile/author-information', array('title' => $this->randomName(8)), t('Save block')); + $block_id = block_entity_id('profile', 'author-information'); + $this->drupalPost('admin/structure/block/configure/' . $block_id, array('title' => $this->randomName(8)), t('Save block')); $this->assertText(t('The block configuration has been saved.'), t('Block configuration set.')); // Set the block to a region to confirm block is availble. $edit = array(); - $edit['profile_author-information[region]'] = 'footer'; + $edit[$block_id . '[region]'] = 'footer'; $this->drupalPost('admin/structure/block', $edit, t('Save blocks')); $this->assertText(t('The block settings have been updated.'), t('Block successfully move to footer region.')); } diff --git a/modules/search/search.test b/modules/search/search.test index 2ded22f..b7b7aab 100644 --- a/modules/search/search.test +++ b/modules/search/search.test @@ -404,12 +404,13 @@ class SearchBlockTestCase extends DrupalWebTestCase { function testSearchFormBlock() { // Set block title to confirm that the interface is availble. - $this->drupalPost('admin/structure/block/configure/search/form', array('title' => $this->randomName(8)), t('Save block')); + $block_id = block_entity_id('search', 'form'); + $this->drupalPost('admin/structure/block/configure/' . $block_id, array('title' => $this->randomName(8)), t('Save block')); $this->assertText(t('The block configuration has been saved.'), t('Block configuration set.')); // Set the block to a region to confirm block is availble. $edit = array(); - $edit['search_form[region]'] = 'footer'; + $edit[$block_id . '[region]'] = 'footer'; $this->drupalPost('admin/structure/block', $edit, t('Save blocks')); $this->assertText(t('The block settings have been updated.'), t('Block successfully move to footer region.')); } @@ -420,7 +421,8 @@ class SearchBlockTestCase extends DrupalWebTestCase { function testBlock() { // Enable the block, and place it in the 'content' region so that it isn't // hidden on 404 pages. - $edit = array('search_form[region]' => 'content'); + $block_id = block_entity_id('search', 'form'); + $edit = array($block_id . '[region]' => 'content'); $this->drupalPost('admin/structure/block', $edit, t('Save blocks')); // Test a normal search via the block form, from the front page. @@ -434,7 +436,7 @@ class SearchBlockTestCase extends DrupalWebTestCase { // Test a search from the block when it doesn't appear on the search page. $edit = array('pages' => 'search'); - $this->drupalPost('admin/structure/block/configure/search/form', $edit, t('Save block')); + $this->drupalPost('admin/structure/block/configure/' . $block_id, $edit, t('Save block')); $this->drupalPost('node', $terms, t('Search')); $this->assertText('Your search yielded no results'); } diff --git a/modules/system/system.test b/modules/system/system.test index e7c75b8..170c75e 100644 --- a/modules/system/system.test +++ b/modules/system/system.test @@ -562,7 +562,7 @@ class AccessDeniedTestCase extends DrupalWebTestCase { // Log back in, set the custom 403 page to /user and remove the block $this->drupalLogin($this->admin_user); variable_set('site_403', 'user'); - $this->drupalPost('admin/structure/block', array('user_login[region]' => '-1'), t('Save blocks')); + $this->drupalPost('admin/structure/block', array(block_entity_id('user', 'login') . '[region]' => '-1'), t('Save blocks')); // Check that we can log in from the 403 page. $this->drupalLogout(); @@ -972,12 +972,13 @@ class SystemBlockTestCase extends DrupalWebTestCase { */ function testPoweredByBlock() { // Set block title and some settings to confirm that the interface is availble. - $this->drupalPost('admin/structure/block/configure/system/powered-by', array('title' => $this->randomName(8), 'color' => 'powered-black', 'size' => '135x42'), t('Save block')); + $block_id = block_entity_id('system', 'powered-by'); + $this->drupalPost('admin/structure/block/configure/' . $block_id, array('title' => $this->randomName(8), 'color' => 'powered-black', 'size' => '135x42'), t('Save block')); $this->assertText(t('The block configuration has been saved.'), t('Block configuration set.')); // Set the powered-by block to the footer region. $edit = array(); - $edit['system_powered-by[region]'] = 'footer'; + $edit[$block_id . '[region]'] = 'footer'; $this->drupalPost('admin/structure/block', $edit, t('Save blocks')); $this->assertText(t('The block settings have been updated.'), t('Block successfully moved to footer region.')); @@ -987,7 +988,7 @@ class SystemBlockTestCase extends DrupalWebTestCase { // Set the block to the disabled region. $edit = array(); - $edit['system_powered-by[region]'] = '-1'; + $edit[$block_id . '[region]'] = '-1'; $this->drupalPost('admin/structure/block', $edit, t('Save blocks')); // Confirm that the block is hidden. @@ -995,9 +996,9 @@ class SystemBlockTestCase extends DrupalWebTestCase { // For convenience of developers, set the block to it's default settings. $edit = array(); - $edit['system_powered-by[region]'] = 'footer'; + $edit[$block_id . '[region]'] = 'footer'; $this->drupalPost('admin/structure/block', $edit, t('Save blocks')); - $this->drupalPost('admin/structure/block/configure/system/powered-by', array('title' => '', 'color' => 'powered-blue', 'size' => '80x15'), t('Save block')); + $this->drupalPost('admin/structure/block/configure/' . $block_id, array('title' => '', 'color' => 'powered-blue', 'size' => '80x15'), t('Save block')); } } diff --git a/profiles/default/default.install b/profiles/default/default.install index 59cc545..2a51b1f 100644 --- a/profiles/default/default.install +++ b/profiles/default/default.install @@ -9,133 +9,142 @@ function default_install() { // Enable some standard blocks. - $values = array( + $blocks = array( array( 'module' => 'system', 'delta' => 'main', - 'theme' => 'garland', 'status' => 1, - 'weight' => 0, - 'region' => 'content', - 'pages' => '', - 'cache' => -1, - ), - array( - 'module' => 'search', - 'delta' => 'form', - 'theme' => 'garland', - 'status' => 1, - 'weight' => -1, - 'region' => 'sidebar_first', 'pages' => '', 'cache' => -1, + 'instances' => array( + array( + 'theme' => 'garland', + 'region' => 'content', + 'weight' => 0, + ), + array( + 'theme' => 'seven', + 'region' => 'content', + 'weight' => 0, + ), + ), ), array( 'module' => 'user', 'delta' => 'login', - 'theme' => 'garland', 'status' => 1, - 'weight' => 0, - 'region' => 'sidebar_first', 'pages' => '', 'cache' => -1, + 'instances' => array( + array( + 'theme' => 'garland', + 'region' => 'sidebar_first', + 'weight' => 0, + ), + array( + 'theme' => 'seven', + 'region' => 'content', + 'weight' => 10, + ), + ), ), array( 'module' => 'system', 'delta' => 'navigation', - 'theme' => 'garland', 'status' => 1, - 'weight' => 0, - 'region' => 'sidebar_first', 'pages' => '', 'cache' => -1, + 'instances' => array( + array( + 'theme' => 'garland', + 'region' => 'sidebar_first', + 'weight' => 0, + ), + ), ), array( 'module' => 'system', 'delta' => 'management', - 'theme' => 'garland', - 'status' => 1, - 'weight' => 1, - 'region' => 'sidebar_first', - 'pages' => '', - 'cache' => -1, - ), - array( - 'module' => 'system', - 'delta' => 'powered-by', - 'theme' => 'garland', 'status' => 1, - 'weight' => 10, - 'region' => 'footer', 'pages' => '', 'cache' => -1, + 'instances' => array( + array( + 'theme' => 'garland', + 'region' => 'sidebar_first', + 'weight' => 1, + ), + array( + 'theme' => 'seven', + 'region' => 'dashboard_main', + 'weight' => 0, + ), + ), ), array( - 'module' => 'system', - 'delta' => 'help', - 'theme' => 'garland', + 'module' => 'search', + 'delta' => 'form', 'status' => 1, - 'weight' => 0, - 'region' => 'help', 'pages' => '', 'cache' => -1, + 'instances' => array( + array( + 'theme' => 'garland', + 'region' => 'sidebar_first', + 'weight' => -1, + ) + ), ), array( 'module' => 'system', - 'delta' => 'main', - 'theme' => 'seven', + 'delta' => 'powered-by', 'status' => 1, - 'weight' => 0, - 'region' => 'content', 'pages' => '', 'cache' => -1, + 'instances' => array( + array( + 'theme' => 'garland', + 'region' => 'footer', + 'weight' => 10, + ), + ), ), array( 'module' => 'system', 'delta' => 'help', - 'theme' => 'seven', 'status' => 1, - 'weight' => 0, - 'region' => 'help', - 'pages' => '', - 'cache' => -1, - ), - array( - 'module' => 'user', - 'delta' => 'login', - 'theme' => 'seven', - 'status' => 1, - 'weight' => 10, - 'region' => 'content', - 'pages' => '', - 'cache' => -1, - ), - array( - 'module' => 'system', - 'delta' => 'management', - 'theme' => 'seven', - 'status' => 1, - 'weight' => 0, - 'region' => 'dashboard_main', 'pages' => '', 'cache' => -1, + 'instances' => array( + array( + 'theme' => 'garland', + 'region' => 'help', + 'weight' => 0, + ), + array( + 'theme' => 'seven', + 'region' => 'help', + 'weight' => 0, + ), + ), ), array( 'module' => 'user', 'delta' => 'new', - 'theme' => 'seven', 'status' => 1, - 'weight' => 0, - 'region' => 'dashboard_sidebar', 'pages' => '', 'cache' => -1, + 'instances' => array( + 'theme' => 'seven', + 'region' => 'dashboard_sidebar', + 'weight' => 0, + ), ), ); - $query = db_insert('block')->fields(array('module', 'delta', 'theme', 'status', 'weight', 'region', 'pages', 'cache')); - foreach ($values as $record) { - $query->values($record); + + foreach ($blocks as $block) { + block_save($block); } - $query->execute(); // Insert default user-defined node types into the database. For a complete // list of available node type attributes, refer to the node type API