TRUE
(experts only)'));
- $title = t('Pages or PHP code');
- $description .= ' ' . t('If the PHP option is chosen, enter PHP code between %php. Note that executing incorrect PHP code can break your Drupal site.', array('%php' => ''));
- }
- else {
- $title = t('Pages');
- }
- $form['visibility']['path']['visibility'] = array(
- '#type' => 'radios',
- '#title' => t('Show block on specific pages'),
- '#options' => $options,
- '#default_value' => isset($block->visibility) ? $block->visibility : BLOCK_VISIBILITY_NOTLISTED,
- );
- $form['visibility']['path']['pages'] = array(
- '#type' => 'textarea',
- '#title' => '' . $title . '',
- '#default_value' => isset($block->pages) ? $block->pages : '',
- '#description' => $description,
- );
- }
-
- // Configure the block visibility per language.
- if (module_exists('language') && language_multilingual()) {
- $configurable_language_types = language_types_get_configurable();
- $existing_language_settings = db_query("SELECT type, langcode FROM {block_language} WHERE module = :module AND delta = :delta", array(
- ':module' => $form['module']['#value'],
- ':delta' => $form['delta']['#value'],
- ))->fetchAll();
- $default_langcode_options = array();
- $default_language_type = $configurable_language_types[0];
- foreach ($existing_language_settings as $setting) {
- $default_langcode_options[] = $setting->langcode;
- // Overwrite default language type if we have it set. Although this
- // theoretically would allow per language type association, our UI
- // only allows language type association overall for a block, so we
- // only need a single value.
- $default_language_type = $setting->type;
- }
-
- // Fetch languages.
- $languages = language_list(LANGUAGE_ALL);
- foreach ($languages as $language) {
- // @TODO $language->name is not wrapped with t(), it should be replaced
- // by CMI translation implementation.
- $langcodes_options[$language->langcode] = $language->name;
- }
- $form['visibility']['language'] = array(
- '#type' => 'fieldset',
- '#title' => t('Languages'),
- '#collapsible' => TRUE,
- '#collapsed' => TRUE,
- '#group' => 'visibility',
- '#weight' => 5,
- );
- // If there are multiple configurable language types, let the user pick
- // which one should be applied to this visibility setting. This way users
- // can limit blocks by interface language or content language for exmaple.
- $language_types = language_types_info();
- $language_type_options = array();
- foreach ($configurable_language_types as $type_key) {
- $language_type_options[$type_key] = $language_types[$type_key]['name'];
- }
- $form['visibility']['language']['language_type'] = array(
- '#type' => 'radios',
- '#title' => t('Language type'),
- '#options' => $language_type_options,
- '#default_value' => $default_language_type,
- '#access' => count($language_type_options) > 1,
- );
- $form['visibility']['language']['langcodes'] = array(
- '#type' => 'checkboxes',
- '#title' => t('Show this block only for specific languages'),
- '#default_value' => $default_langcode_options,
- '#options' => $langcodes_options,
- '#description' => t('Show this block only for the selected language(s). If you select no languages, the block will be visibile in all languages.'),
- );
+ elseif (!$theme && isset($config['config_id'])) {
+ list(, , , $theme) = explode('.', $config['config_id']);
}
-
- // Per-role visibility.
- $default_role_options = db_query("SELECT rid FROM {block_role} WHERE module = :module AND delta = :delta", array(
- ':module' => $block->module,
- ':delta' => $block->delta,
- ))->fetchCol();
- $role_options = array_map('check_plain', user_roles());
- $form['visibility']['role'] = array(
- '#type' => 'fieldset',
- '#title' => t('Roles'),
- '#collapsible' => TRUE,
- '#collapsed' => TRUE,
- '#group' => 'visibility',
- '#weight' => 10,
- );
- $form['visibility']['role']['roles'] = array(
- '#type' => 'checkboxes',
- '#title' => t('Show block for specific roles'),
- '#default_value' => $default_role_options,
- '#options' => $role_options,
- '#description' => t('Show this block only for the selected role(s). If you select no roles, the block will be visible to all users.'),
- );
-
- // Per-user visibility.
- $form['visibility']['user'] = array(
- '#type' => 'fieldset',
- '#title' => t('Users'),
- '#collapsible' => TRUE,
- '#collapsed' => TRUE,
- '#group' => 'visibility',
- '#weight' => 20,
- );
- $form['visibility']['user']['custom'] = array(
- '#type' => 'radios',
- '#title' => t('Customizable per user'),
- '#options' => array(
- BLOCK_CUSTOM_FIXED => t('Not customizable'),
- BLOCK_CUSTOM_ENABLED => t('Customizable, visible by default'),
- BLOCK_CUSTOM_DISABLED => t('Customizable, hidden by default'),
- ),
- '#description' => t('Allow individual users to customize the visibility of this block in their account settings.'),
- '#default_value' => isset($block->custom) ? $block->custom : BLOCK_CUSTOM_FIXED,
- );
-
- $form['actions'] = array('#type' => 'actions');
- $form['actions']['submit'] = array(
- '#type' => 'submit',
- '#value' => t('Save block'),
+ $form['theme'] = array(
+ '#type' => 'value',
+ '#value' => $theme,
);
-
+ $form += $instance->configureWrapper($form, $form_state);
return $form;
}
@@ -519,15 +275,7 @@ function block_admin_configure($form, &$form_state, $module, $delta) {
* @see block_admin_configure_submit()
*/
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'],
- ':info' => $form_state['values']['info'],
- ))->fetchField();
- if (empty($form_state['values']['info']) || $custom_block_exists) {
- form_set_error('info', t('Ensure that each block description is unique.'));
- }
- }
+ $form['#instance']->configureValidateWrapper($form, $form_state);
}
/**
@@ -537,186 +285,16 @@ function block_admin_configure_validate($form, &$form_state) {
* @see block_admin_configure_validate()
*/
function block_admin_configure_submit($form, &$form_state) {
- if (!form_get_errors()) {
- $transaction = db_transaction();
- try {
- 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();
-
- db_delete('block_role')
- ->condition('module', $form_state['values']['module'])
- ->condition('delta', $form_state['values']['delta'])
- ->execute();
- $query = db_insert('block_role')->fields(array('rid', 'module', 'delta'));
- foreach (array_filter($form_state['values']['roles']) as $rid) {
- $query->values(array(
- 'rid' => $rid,
- 'module' => $form_state['values']['module'],
- 'delta' => $form_state['values']['delta'],
- ));
- }
- $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 == BLOCK_REGION_NONE ? '' : $region),
- 'pages' => trim($form_state['values']['pages']),
- 'status' => (int) ($region != BLOCK_REGION_NONE),
- ))
- ->execute();
- }
-
- // Update the block visibility settings if we have settings to store
- // for the existing languages.
- if (module_exists('language') && isset($form_state['values']['langcodes'])) {
- db_delete('block_language')
- ->condition('module', $form_state['values']['module'])
- ->condition('delta', $form_state['values']['delta'])
- ->execute();
- $query = db_insert('block_language')->fields(array(
- 'type', 'langcode', 'module', 'delta'
- ));
- foreach (array_filter($form_state['values']['langcodes']) as $langcode) {
- $query->values(array(
- 'type' => $form_state['values']['language_type'],
- 'langcode' => $langcode,
- 'module' => $form_state['values']['module'],
- 'delta' => $form_state['values']['delta'],
- ));
- }
- $query->execute();
- }
-
- module_invoke($form_state['values']['module'], 'block_save', $form_state['values']['delta'], $form_state['values']);
- }
- catch (Exception $e) {
- $transaction->rollback();
- watchdog_exception('block', $e);
- throw $e;
- }
- drupal_set_message(t('The block configuration has been saved.'));
- cache_invalidate(array('content' => TRUE));
- $form_state['redirect'] = 'admin/structure/block';
+ $form['#instance']->configureSubmitWrapper($form, $form_state);
+ $config_values = $form['#instance']->getConfig();
+ $machine_name = 'plugin.core.block.' . $form_state['values']['theme'] . '.' . $form_state['values']['machine_name'];
+ $config = config($machine_name);
+ $config->set('id', $form['#instance']->getPluginId());
+ foreach ($config_values as $key => $value) {
+ $config->set($key, $value);
}
-}
-
-/**
- * Form constructor for the add block form.
- *
- * @see block_menu()
- * @see block_add_block_form_validate()
- * @see block_add_block_form_submit()
- * @ingroup forms
- */
-function block_add_block_form($form, &$form_state) {
- return block_admin_configure($form, $form_state, 'block', NULL);
-}
-
-/**
- * Form validation handler for block_add_block_form().
- *
- * @see block_add_block_form()
- * @see block_add_block_form_submit()
- */
-function block_add_block_form_validate($form, &$form_state) {
- $custom_block_exists = (bool) db_query_range('SELECT 1 FROM {block_custom} WHERE info = :info', 0, 1, array(':info' => $form_state['values']['info']))->fetchField();
-
- if (empty($form_state['values']['info']) || $custom_block_exists) {
- form_set_error('info', t('Ensure that each block description is unique.'));
- }
-}
-
-/**
- * Form submission handler for block_add_block_form().
- *
- * Saves the new custom block.
- *
- * @see block_add_block_form()
- * @see block_add_block_form_validate()
- */
-function block_add_block_form_submit($form, &$form_state) {
- $delta = db_insert('block_custom')
- ->fields(array(
- 'body' => $form_state['values']['body']['value'],
- 'info' => $form_state['values']['info'],
- 'format' => $form_state['values']['body']['format'],
- ))
- ->execute();
- // Store block delta to allow other modules to work with new block.
- $form_state['values']['delta'] = $delta;
-
- $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'));
- foreach (array_filter($form_state['values']['roles']) as $rid) {
- $query->values(array(
- 'rid' => $rid,
- 'module' => $form_state['values']['module'],
- 'delta' => $delta,
- ));
- }
- $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 == BLOCK_REGION_NONE ? '' : $region),
- 'pages' => trim($form_state['values']['pages']),
- 'status' => (int) ($region != BLOCK_REGION_NONE),
- ))
- ->execute();
- }
-
- // Update the block visibility settings if we have settings to store
- // for the existing languages.
- if (module_exists('language') && isset($form_state['values']['langcodes'])) {
- $query = db_insert('block_language')->fields(array(
- 'type', 'langcode', 'module', 'delta'
- ));
- foreach (array_filter($form_state['values']['langcodes']) as $langcode) {
- $query->values(array(
- 'type' => $form_state['values']['language_type'],
- 'langcode' => $langcode,
- 'module' => $form_state['values']['module'],
- 'delta' => $form_state['values']['delta'],
- ));
- }
- $query->execute();
- }
-
- drupal_set_message(t('The block has been created.'));
- cache_invalidate(array('content' => TRUE));
- $form_state['redirect'] = 'admin/structure/block';
+ $config->save();
+ $form_state['redirect'] = 'admin/structure/block/list/block_plugin_ui:' . $form_state['values']['theme'];
}
/**
@@ -729,43 +307,30 @@ function block_add_block_form_submit($form, &$form_state) {
* The unique ID of the block within the context of $module.
*
* @see block_menu()
- * @see block_custom_block_delete_submit()
+ * @see block_admin_block_delete_submit()
*/
-function block_custom_block_delete($form, &$form_state, $module, $delta) {
- $block = block_load($module, $delta);
- $custom_block = block_custom_block_get($block->delta);
- $form['info'] = array('#type' => 'hidden', '#value' => $custom_block['info'] ? $custom_block['info'] : $custom_block['title']);
- $form['bid'] = array('#type' => 'hidden', '#value' => $block->delta);
-
- 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'));
+function block_admin_block_delete($form, &$form_state, $plugin_id, $theme) {
+ $block = block_load($plugin_id);
+ $form['id'] = array('#type' => 'value', '#value' => $plugin_id);
+ $form['theme'] = array('#type' => 'value', '#value' => $theme);
+ $definition = $block->getDefinition();
+ $config = $block->getConfig();
+ $subject = empty($config['subject']) ? $definition['subject'] : $config['subject'];
+ $form['subject'] = array('#type' => 'value', '#value' => $subject);
+
+ return confirm_form($form, t('Are you sure you want to delete the block %name?', array('%name' => $subject)), 'admin/structure/block', '', t('Delete'), t('Cancel'));
}
/**
- * Form submission handler for block_custom_block_delete().
+ * Form submission handler for block_admin_block_delete().
*
- * @see block_custom_block_delete()
+ * @see block_admin_block_delete()
*/
-function block_custom_block_delete_submit($form, &$form_state) {
- db_delete('block_custom')
- ->condition('bid', $form_state['values']['bid'])
- ->execute();
- db_delete('block')
- ->condition('module', 'block')
- ->condition('delta', $form_state['values']['bid'])
- ->execute();
- db_delete('block_role')
- ->condition('module', 'block')
- ->condition('delta', $form_state['values']['bid'])
- ->execute();
- db_delete('block_language')
- ->condition('module', 'block')
- ->condition('delta', $form_state['values']['bid'])
- ->execute();
-
- drupal_set_message(t('The block %name has been removed.', array('%name' => $form_state['values']['info'])));
- cache_invalidate(array('content' => TRUE));
- $form_state['redirect'] = 'admin/structure/block';
- return;
+function block_admin_block_delete_submit($form, &$form_state) {
+ $config = config($form_state['values']['id']);
+ $config->delete();
+ drupal_set_message(t('The block %name has been removed.', array('%name' => $form_state['values']['subject'])));
+ $form_state['redirect'] = 'admin/structure/block/list/' . $form_state['values']['theme'];
}
/**
diff --git a/core/modules/block/block.install b/core/modules/block/block.install
index ee72e0f..520689b 100644
--- a/core/modules/block/block.install
+++ b/core/modules/block/block.install
@@ -136,42 +136,6 @@ function block_schema() {
),
);
- $schema['block_custom'] = array(
- 'description' => 'Stores contents of custom-made blocks.',
- 'fields' => array(
- 'bid' => array(
- 'type' => 'serial',
- 'unsigned' => TRUE,
- 'not null' => TRUE,
- 'description' => "The block's {block}.bid.",
- ),
- 'body' => array(
- 'type' => 'text',
- 'not null' => FALSE,
- 'size' => 'big',
- 'description' => 'Block contents.',
- 'translatable' => TRUE,
- ),
- 'info' => array(
- 'type' => 'varchar',
- 'length' => 128,
- 'not null' => TRUE,
- 'default' => '',
- 'description' => 'Block description.',
- ),
- 'format' => array(
- 'type' => 'varchar',
- 'length' => 255,
- 'not null' => FALSE,
- 'description' => 'The {filter_format}.format of the block body.',
- ),
- ),
- 'unique keys' => array(
- 'info' => array('info'),
- ),
- 'primary key' => array('bid'),
- );
-
$schema['block_language'] = array(
'description' => 'Sets up display criteria for blocks based on langcode',
'fields' => array(
diff --git a/core/modules/block/block.module b/core/modules/block/block.module
index 2978146..321e4b9 100644
--- a/core/modules/block/block.module
+++ b/core/modules/block/block.module
@@ -54,16 +54,21 @@ function block_help($path, $arg) {
$output .= '' . t('Use this page to create a new custom block.') . '
'; } - if ($arg[0] == 'admin' && $arg[1] == 'structure' && $arg['2'] == 'block' && (empty($arg[3]) || $arg[3] == 'list')) { - $demo_theme = !empty($arg[4]) ? $arg[4] : variable_get('theme_default', 'stark'); + if ($arg[0] == 'admin' && $arg[1] == 'structure' && $arg['2'] == 'block' && (empty($arg[3]) || $arg[3] == 'list') && empty($arg[5])) { + if (!empty($arg[4])) { + list(, $demo_theme) = explode(':', $arg[4]); + } + else { + $demo_theme = variable_get('theme_default', 'stark'); + } $themes = list_themes(); $output = '' . t('This page provides a drag-and-drop interface for assigning a block to a region, and for controlling the order of blocks within regions. Since not all themes implement the same regions, or display regions in the same way, blocks are positioned on a per-theme basis. Remember that your changes will not be saved until you click the Save blocks button at the bottom of the page. Click the configure link next to each block to configure its specific title and visibility settings.') . '
'; $output .= '' . l(t('Demonstrate block regions (@theme)', array('@theme' => $themes[$demo_theme]->info['name'])), 'admin/structure/block/demo/' . $demo_theme) . '
'; @@ -127,51 +132,40 @@ function block_menu() { $items['admin/structure/block/manage/%/%/delete'] = array( 'title' => 'Delete block', 'page callback' => 'drupal_get_form', - 'page arguments' => array('block_custom_block_delete', 4, 5), + 'page arguments' => array('block_admin_block_delete', 4, 5), 'access arguments' => array('administer blocks'), 'type' => MENU_LOCAL_TASK, 'context' => MENU_CONTEXT_NONE, 'file' => 'block.admin.inc', ); - $items['admin/structure/block/add'] = array( - 'title' => 'Add block', - 'page callback' => 'drupal_get_form', - 'page arguments' => array('block_add_block_form'), - 'access arguments' => array('administer blocks'), - 'type' => MENU_LOCAL_ACTION, - 'file' => 'block.admin.inc', - ); - foreach (list_themes() as $key => $theme) { - $items['admin/structure/block/list/' . $key] = array( - 'title' => check_plain($theme->info['name']), - 'page arguments' => array($key), - 'type' => $key == $default_theme ? MENU_DEFAULT_LOCAL_TASK : MENU_LOCAL_TASK, - 'weight' => $key == $default_theme ? -10 : 0, - 'access callback' => '_block_themes_access', - 'access arguments' => array($key), - 'file' => 'block.admin.inc', - ); - if ($key != $default_theme) { - $items['admin/structure/block/list/' . $key . '/add'] = array( - 'title' => 'Add block', - 'page callback' => 'drupal_get_form', - 'page arguments' => array('block_add_block_form'), - 'access arguments' => array('administer blocks'), - 'type' => MENU_LOCAL_ACTION, + // Block administration is actually tied to theme and plugin definition so + // that the plugin can appropriately attach to this url structure. + $themes = list_themes(); + foreach (system_plugin_manager('plugin_ui')->getDefinitions() as $plugin_id => $plugin) { + list($plugin_base, $key) = explode(':', $plugin_id); + if ($plugin_base == 'block_plugin_ui') { + $theme = $themes[$key]; + $items['admin/structure/block/list/' . $plugin_id] = array( + 'title' => check_plain($theme->info['name']), + 'page arguments' => array($key), + 'type' => $key == $default_theme ? MENU_DEFAULT_LOCAL_TASK : MENU_LOCAL_TASK, + 'weight' => $key == $default_theme ? -10 : 0, + 'access callback' => '_block_themes_access', + 'access arguments' => array($key), + 'file' => 'block.admin.inc', + ); + $items['admin/structure/block/demo/' . $key] = array( + 'title' => check_plain($theme->info['name']), + 'page callback' => 'block_admin_demo', + 'page arguments' => array($key), + 'type' => MENU_CALLBACK, + 'access callback' => '_block_themes_access', + 'access arguments' => array($key), + 'theme callback' => '_block_custom_theme', + 'theme arguments' => array($key), 'file' => 'block.admin.inc', ); } - $items['admin/structure/block/demo/' . $key] = array( - 'title' => check_plain($theme->info['name']), - 'page callback' => 'block_admin_demo', - 'page arguments' => array($key), - 'type' => MENU_CALLBACK, - 'access callback' => '_block_themes_access', - 'access arguments' => array($key), - 'theme callback' => '_block_custom_theme', - 'theme arguments' => array($key), - 'file' => 'block.admin.inc', - ); } return $items; } @@ -212,53 +206,6 @@ function _block_custom_theme($theme = NULL) { } /** - * Implements hook_block_info(). - */ -function block_block_info() { - $blocks = array(); - - $result = db_query('SELECT bid, info FROM {block_custom} ORDER BY info'); - foreach ($result as $block) { - $blocks[$block->bid]['info'] = $block->info; - // Not worth caching. - $blocks[$block->bid]['cache'] = DRUPAL_NO_CACHE; - } - return $blocks; -} - -/** - * Implements hook_block_configure(). - */ -function block_block_configure($delta = 0) { - if ($delta) { - $custom_block = block_custom_block_get($delta); - } - else { - $custom_block = array(); - } - return block_custom_block_form($custom_block); -} - -/** - * Implements hook_block_save(). - */ -function block_block_save($delta = 0, $edit = array()) { - block_custom_block_save($edit, $delta); -} - -/** - * Implements hook_block_view(). - * - * Generates the administrator-defined blocks for display. - */ -function block_block_view($delta = '') { - $block = db_query('SELECT body, format FROM {block_custom} WHERE bid = :bid', array(':bid' => $delta))->fetchObject(); - $data['subject'] = NULL; - $data['content'] = check_markup($block->body, $block->format, '', TRUE); - return $data; -} - -/** * Implements hook_page_build(). * * Renders blocks into their regions. @@ -343,7 +290,6 @@ function block_get_blocks_by_region($region) { * A renderable array. */ function _block_get_renderable_region($list = array()) { - $weight = 0; $build = array(); // Block caching is not compatible with node_access modules. We also // preserve the submission of forms in blocks, by fetching from cache @@ -356,23 +302,27 @@ function _block_get_renderable_region($list = array()) { !in_array($_SERVER['REQUEST_METHOD'], array('GET', 'HEAD')); foreach ($list as $key => $block) { + $config = $block->getConfig(); + $definition = $block->getDefinition(); $build[$key] = array( '#block' => $block, - '#weight' => ++$weight, + '#weight' => (int) $config['weight'], '#theme_wrappers' => array('block'), ); - if ($not_cacheable || in_array($block->cache, array(DRUPAL_NO_CACHE, DRUPAL_CACHE_CUSTOM))) { + if ($not_cacheable || in_array($config['cache'], array(DRUPAL_NO_CACHE, DRUPAL_CACHE_CUSTOM))) { // Non-cached blocks get built immediately. Provides more content // that can be easily manipulated during hook_page_alter(). $build[$key] = _block_get_renderable_block($build[$key]); } else { + $key_components = explode('.', $key); + $id = array_pop($key_components); $build[$key] += array( '#pre_render' => array('_block_get_renderable_block'), '#cache' => array( - 'keys' => array($block->module, $block->delta), - 'granularity' => $block->cache, + 'keys' => array($id, $config['module']), + 'granularity' => $config['cache'], 'bin' => 'block', 'tags' => array('content' => TRUE), ), @@ -384,16 +334,16 @@ function _block_get_renderable_region($list = array()) { // skip the help block, since we assume that most users do not need or want // to perform contextual actions on the help block, and the links needlessly // draw attention on it. - if ($key != 'system_main' && $key != 'system_help') { - $build[$key]['#contextual_links']['block'] = array('admin/structure/block/manage', array($block->module, $block->delta)); + if ($definition['class'] != 'Drupal\\system\\Plugin\\block\\block\\SystemHelpBlock' && $definition['class'] != 'Drupal\\system\\Plugin\\block\\block\\SystemMainBlock') { + global $theme; + $build[$key]['#contextual_links']['block'] = array('admin/structure/block/manage', array($key, $theme)); } } - $build['#sorted'] = TRUE; return $build; } /** - * Updates the 'block' DB table with the blocks currently exported by modules. + * Returns an array of block class instances by theme. * * @param $theme * The theme to rehash blocks for. If not provided, defaults to the currently @@ -403,251 +353,17 @@ function _block_get_renderable_region($list = array()) { * Blocks currently exported by modules. */ function _block_rehash($theme = NULL) { - global $theme_key; - - drupal_theme_initialize(); - if (!isset($theme)) { - // If theme is not specifically set, rehash for the current theme. - $theme = $theme_key; - } - $regions = system_region_list($theme); - - // These are the blocks the function will return. $blocks = array(); - // These are the blocks defined by code and modified by the database. - $current_blocks = array(); - // These are {block}.bid values to be kept. - $bids = array(); - $or = db_or(); - // Gather the blocks defined by modules. - foreach (module_implements('block_info') as $module) { - $module_blocks = module_invoke($module, 'block_info'); - foreach ($module_blocks as $delta => $block) { - // Compile a condition to retrieve this block from the database. - $condition = db_and() - ->condition('module', $module) - ->condition('delta', $delta); - $or->condition($condition); - // Add identifiers. - $block['module'] = $module; - $block['delta'] = $delta; - $block['theme'] = $theme; - $current_blocks[$module][$delta] = $block; - } - } - // Save the blocks defined in code for alter context. - $code_blocks = $current_blocks; - $database_blocks = db_select('block', 'b') - ->fields('b') - ->condition($or) - ->condition('theme', $theme) - ->execute(); - foreach ($database_blocks as $block) { - // Preserve info which is not in the database. - $block->info = $current_blocks[$block->module][$block->delta]['info']; - // The cache mode can only by set from hook_block_info(), so that has - // precedence over the database's value. - if (isset($current_blocks[$block->module][$block->delta]['cache'])) { - $block->cache = $current_blocks[$block->module][$block->delta]['cache']; - } - // Blocks stored in the database override the blocks defined in code. - $current_blocks[$block->module][$block->delta] = get_object_vars($block); - // Preserve this block. - $bids[$block->bid] = $block->bid; - } - drupal_alter('block_info', $current_blocks, $theme, $code_blocks); - foreach ($current_blocks as $module => $module_blocks) { - foreach ($module_blocks as $delta => $block) { - 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'] = ''; - } - // Make sure weight is set. - if (!isset($block['weight'])) { - $block['weight'] = 0; - } - if (!empty($block['region']) && $block['region'] != BLOCK_REGION_NONE && !isset($regions[$block['region']]) && $block['status'] == 1) { - drupal_set_message(t('The block %info was assigned to the invalid region %region and has been disabled.', array('%info' => $block['info'], '%region' => $block['region'])), 'warning'); - // Disabled modules are moved into the BLOCK_REGION_NONE later so no - // need to move the block to another region. - $block['status'] = 0; - } - // Set region to none if not enabled and make sure status is set. - if (empty($block['status'])) { - $block['status'] = 0; - $block['region'] = BLOCK_REGION_NONE; - } - // There is no point saving disabled blocks. Still, we need to save them - // because the 'title' attribute is saved to the {blocks} table. - if (isset($block['bid'])) { - // If the block has a bid property, it comes from the database and - // the record needs to be updated, so set the primary key to 'bid' - // before passing to drupal_write_record(). - $primary_keys = array('bid'); - // Remove a block from the list of blocks to keep if it became disabled. - unset($bids[$block['bid']]); - } - else { - $primary_keys = array(); - } - drupal_write_record('block', $block, $primary_keys); - // Add to the list of blocks we return. - $blocks[] = $block; - } - } - if ($bids) { - // Remove disabled that are no longer defined by the code from the - // database. - db_delete('block') - ->condition('bid', $bids, 'NOT IN') - ->condition('theme', $theme) - ->execute(); + $instances = array(); + $theme = $theme ? $theme : variable_get('theme_default', 'stark'); + $block_configs = config_get_storage_names_with_prefix('plugin.core.block.' . $theme); + foreach ($block_configs as $config) { + $blocks[$config] = block_load($config); } return $blocks; } /** - * Returns information from database about a user-created (custom) block. - * - * @param $bid - * ID of the block to get information for. - * - * @return - * Associative array of information stored in the database for this block. - * Array keys: - * - bid: Block ID. - * - info: Block description. - * - 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(); -} - -/** - * Form constructor for the custom block form. - * - * @param $edit - * (optional) An associative array of information retrieved by - * block_custom_get_block() if an existing block is being edited, or an empty - * array otherwise. Defaults to array(). - * - * @ingroup forms - */ -function block_custom_block_form($edit = array()) { - $edit += array( - 'info' => '', - 'body' => '', - ); - $form['info'] = array( - '#type' => 'textfield', - '#title' => t('Block description'), - '#default_value' => $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' => -18, - ); - $form['body_field']['#weight'] = -17; - $form['body_field']['body'] = array( - '#type' => 'text_format', - '#title' => t('Block body'), - '#default_value' => $edit['body'], - '#format' => isset($edit['format']) ? $edit['format'] : NULL, - '#rows' => 15, - '#description' => t('The content of the block as shown to the user.'), - '#required' => TRUE, - '#weight' => -17, - ); - - return $form; -} - -/** - * Saves a user-created block in the database. - * - * @param $edit - * Associative array of fields to save. Array keys: - * - info: Block description. - * - body: Associative array of body value and format. Array keys: - * - value: Block contents. - * - format: Filter ID of the filter format for the body. - * @param $delta - * Block ID of the block to save. - * - * @return - * Always returns TRUE. - */ -function block_custom_block_save($edit, $delta) { - db_update('block_custom') - ->fields(array( - 'body' => $edit['body']['value'], - 'info' => $edit['info'], - 'format' => $edit['body']['format'], - )) - ->condition('bid', $delta) - ->execute(); - return TRUE; -} - -/** - * Implements hook_form_FORM_ID_alter() for user_profile_form(). - */ -function block_form_user_profile_form_alter(&$form, &$form_state) { - $account = $form_state['controller']->getEntity($form_state); - $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)); - - $blocks = array(); - foreach ($result as $block) { - $data = module_invoke($block->module, 'block_info'); - if ($data[$block->delta]['info']) { - $blocks[$block->module][$block->delta] = array( - '#type' => 'checkbox', - '#title' => check_plain($data[$block->delta]['info']), - '#default_value' => isset($account->data['block'][$block->module][$block->delta]) ? $account->data['block'][$block->module][$block->delta] : ($block->custom == 1), - ); - } - } - // Only display the fieldset if there are any personalizable blocks. - if ($blocks) { - $form['block'] = array( - '#type' => 'fieldset', - '#title' => t('Personalize blocks'), - '#description' => t('Blocks consist of content or information that complements the main content of the page. Enable or disable optional blocks using the checkboxes below.'), - '#weight' => 3, - '#collapsible' => TRUE, - '#tree' => TRUE, - ); - $form['block'] += $blocks; - } -} - -/** - * Implements hook_field_extra_fields(). - */ -function block_field_extra_fields() { - $extra['user']['user']['form']['block'] = array( - 'label' => t('Personalize blocks'), - 'description' => t('Block module form element.'), - 'weight' => 3, - ); - - return $extra; -} - -/** - * Implements hook_user_presave(). - */ -function block_user_presave($account) { - if (isset($account->block)) { - $account->data['block'] = $account->block; - } -} - -/** * Initializes blocks for enabled themes. * * @param $theme_list @@ -672,20 +388,28 @@ function block_themes_enabled($theme_list) { */ 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 = config_get_storage_names_with_prefix('plugin.core.block.' . $theme); if (!$has_blocks) { $default_theme = variable_get('theme_default', 'stark'); // Apply only to new theme's visible regions. $regions = system_region_list($theme, REGIONS_VISIBLE); - $result = db_query("SELECT * FROM {block} WHERE theme = :theme", array(':theme' => $default_theme), array('fetch' => PDO::FETCH_ASSOC)); - foreach ($result as $block) { - // If the region isn't supported by the theme, assign the block to the theme's default region. - if ($block['status'] && !isset($regions[$block['region']])) { - $block['region'] = system_default_region($theme); + $default_theme_blocks = config_get_storage_names_with_prefix('plugin.core.block.' . $default_theme); + foreach ($default_theme_blocks as $config_id) { + $block_config = config($config_id)->get(); + $machine_name = explode('.', $config_id); + $machine_name = array_pop($machine_name); + $new_config_id = 'plugin.core.block.' . $theme . '.' . $machine_name; + $new_block = config($new_config_id); + // If the region isn't supported by the theme, assign the block to the + // theme's default region. + if (!isset($regions[$block_config['region']])) { + $new_block->set('region', system_default_region($theme)); + unset($block_config['region']); } - $block['theme'] = $theme; - unset($block['bid']); - drupal_write_record('block', $block); + foreach ($block_config as $key => $value) { + $new_block->set($key, $value); + } + $new_block->save(); } } } @@ -697,14 +421,11 @@ function block_theme_initialize($theme) { * The name of a region. * * @return - * An array of block objects, indexed with the module name and block delta - * concatenated with an underscore, thus: MODULE_DELTA. If you are displaying - * your blocks in one or two sidebars, you may check whether this array is - * empty to see how many columns are going to be displayed. + * An array of block objects, indexed with the CMI key that represents the + * configuration. If you are displaying your blocks in one or two sidebars, + * you may check whether this array is empty to see how many columns are + * going to be displayed. * - * @todo - * Now that the block table has a primary key, we should use that as the - * array key instead of MODULE_DELTA. */ function block_list($region) { $blocks = &drupal_static(__FUNCTION__); @@ -722,176 +443,53 @@ function block_list($region) { } /** + * Returns the block plugin manager from the dependency injection container. + */ +function block_manager() { + return drupal_container()->get('plugin.manager.block'); +} + +/** * Loads a block object from the database. * - * @param $module - * Name of the module that implements the block to load. - * @param $delta - * Unique ID of the block within the context of $module. Pass NULL to return - * an empty block object for $module. + * @param $plugin_id + * The plugin id to load. + * @param $conf + * An optional configuration array for creating a block instance from php + * instead of relying on configuration xml. * * @return * A block object. */ -function block_load($module, $delta) { - if (isset($delta)) { - $block = db_query('SELECT * FROM {block} WHERE module = :module AND delta = :delta', array(':module' => $module, ':delta' => $delta))->fetchObject(); +function block_load($plugin_id, $conf = array()) { + try { + $block = block_manager()->getInstance(array('config' => $plugin_id)); } - - // If the block does not exist in the database yet return a stub block - // object. - if (empty($block)) { - $block = new stdClass(); - $block->module = $module; - $block->delta = $delta; + catch (Drupal\Component\Plugin\Exception\PluginException $e) { + $block = block_manager()->createInstance($plugin_id, $conf); } - return $block; } /** - * Loads blocks' information from the database. + * Loads blocks' information from the configuration management system. * * @return * An array of blocks grouped by region. */ function _block_load_blocks() { - global $theme_key; - - $query = db_select('block', 'b'); - $query->addField('b', 'title', 'subject'); - $result = $query - ->fields('b') - ->condition('b.theme', $theme_key) - ->condition('b.status', 1) - ->orderBy('b.region') - ->orderBy('b.weight') - ->orderBy('b.module') - ->addTag('block_load') - ->addTag('translatable') - ->execute(); - - $block_info = $result->fetchAllAssoc('bid'); - // Allow modules to modify the block list. - drupal_alter('block_list', $block_info); - + global $theme; $blocks = array(); - foreach ($block_info as $block) { - $blocks[$block->region]["{$block->module}_{$block->delta}"] = $block; + $instances = config_get_storage_names_with_prefix('plugin.core.block.' . $theme); + foreach ($instances as $plugin_id) { + $block = block_manager()->getInstance(array('config' => $plugin_id)); + $config = $block->getConfig(); + $blocks[$config['region']]["$plugin_id"] = $block; } return $blocks; } /** - * Implements hook_block_list_alter(). - * - * Checks the page, user role, and user-specific visibility settings. - * Removes the block if the visibility conditions are not met. - */ -function block_block_list_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 langcodes allowed per block. - $result = db_query('SELECT module, delta, type, langcode FROM {block_language}'); - $block_langcodes = array(); - foreach ($result as $record) { - $block_langcodes[$record->module][$record->delta][$record->type][$record->langcode] = TRUE; - } - - foreach ($blocks as $key => $block) { - if (!isset($block->theme) || !isset($block->status) || $block->theme != $theme_key || $block->status != 1) { - // This block was added by a contrib module, leave it in the list. - continue; - } - - // 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))) { - // No match. - unset($blocks[$key]); - continue; - } - - // Use the user's block visibility setting, if necessary. - if ($block->custom != BLOCK_CUSTOM_FIXED) { - if ($user->uid && isset($user->data['block'][$block->module][$block->delta])) { - $enabled = $user->data['block'][$block->module][$block->delta]; - } - else { - $enabled = ($block->custom == BLOCK_CUSTOM_ENABLED); - } - } - else { - $enabled = TRUE; - } - - // Limited visibility blocks must list at least one page. - if ($block->visibility == BLOCK_VISIBILITY_LISTED && empty($block->pages)) { - $enabled = FALSE; - } - - if (!$enabled) { - unset($blocks[$key]); - continue; - } - - // Match path if necessary. - if ($block->pages) { - // Convert path to lowercase. This allows comparison of the same path - // with different case. Ex: /Page, /page, /PAGE. - $pages = drupal_strtolower($block->pages); - if ($block->visibility < BLOCK_VISIBILITY_PHP) { - // Compare the lowercase path alias (if any) and internal path. - $path = current_path(); - $path_alias = drupal_strtolower(drupal_get_path_alias($path)); - $page_match = drupal_match_path($path_alias, $pages) || (($path != $path_alias) && drupal_match_path($path, $pages)); - // When $block->visibility has a value of 0 (BLOCK_VISIBILITY_NOTLISTED), - // the block is displayed on all pages except those listed in $block->pages. - // When set to 1 (BLOCK_VISIBILITY_LISTED), it is displayed only on those - // pages listed in $block->pages. - $page_match = !($block->visibility xor $page_match); - } - elseif (module_exists('php')) { - $page_match = php_eval($block->pages); - } - else { - $page_match = FALSE; - } - } - else { - $page_match = TRUE; - } - if (!$page_match) { - unset($blocks[$key]); - continue; - } - - // Language visibility settings. - // No language setting for this block, leave it in the list. - if (!isset($block_langcodes[$block->module][$block->delta])) { - continue; - } - foreach ($block_langcodes[$block->module][$block->delta] as $language_type => $langcodes) { - if (isset($langcodes[language($language_type)->langcode])) { - // Found a language type - langcode combination in the configuration - // that is applicable to the current request. - continue 2; - } - } - // Had language configuration but none matched. - unset($blocks[$key]); - } -} - -/** * Builds the content and subject for a block. * * For cacheable blocks, this is called during #pre_render. @@ -904,45 +502,18 @@ function block_block_list_alter(&$blocks) { */ function _block_get_renderable_block($element) { $block = $element['#block']; - - // Render the block content if it has not been created already. - if (!isset($block->content)) { - $array = module_invoke($block->module, 'block_view', $block->delta); - - // Allow modules to modify the block before it is viewed, via either - // hook_block_view_alter() or hook_block_view_MODULE_DELTA_alter(). - drupal_alter(array('block_view', "block_view_{$block->module}_{$block->delta}"), $array, $block); - - if (empty($array['content'])) { - // Blocks without content should emit no markup at all. - $element += array( - '#access' => FALSE, - '#printed' => TRUE, - ); - } - elseif (isset($array) && is_array($array)) { - foreach ($array as $k => $v) { - $block->$k = $v; + // Don't bother to build blocks that aren't accessible. + if ($element['#access'] = $block->accessWrapper()) { + $build = $block->build(); + if ($build) { + if (isset($build['#title'])) { + $element['#title'] = $build['#title']; } + $element += $build; } - } - - if (isset($block->content) && $block->content) { - // Normalize to the drupal_render() structure. - if (is_string($block->content)) { - $block->content = array('#markup' => $block->content); - } - // Override default block title if a custom display title is present. - if ($block->title) { - // Check plain here to allow module generated titles to keep any - // markup. - $block->subject = $block->title == 'TRUE
(experts only)'));
+ $title = t('Pages or PHP code');
+ $description .= ' ' . t('If the PHP option is chosen, enter PHP code between %php. Note that executing incorrect PHP code can break your Drupal site.', array('%php' => ''));
+ }
+ else {
+ $title = t('Pages');
+ }
+ $form['visibility']['path']['visibility'] = array(
+ '#type' => 'radios',
+ '#title' => t('Show block on specific pages'),
+ '#options' => $options,
+ '#default_value' => !empty($this->configuration['visibility']['path']['visibility']) ? $this->configuration['visibility']['path']['visibility'] : BLOCK_VISIBILITY_NOTLISTED,
+ );
+ $form['visibility']['path']['pages'] = array(
+ '#type' => 'textarea',
+ '#title' => '' . $title . '',
+ '#default_value' => !empty($this->configuration['visibility']['path']['pages']) ? $this->configuration['visibility']['path']['pages'] : '',
+ '#description' => $description,
+ );
+ }
+
+ // Configure the block visibility per language.
+ if (module_exists('language') && language_multilingual()) {
+ $configurable_language_types = language_types_get_configurable();
+
+ // Fetch languages.
+ $languages = language_list(LANGUAGE_ALL);
+ foreach ($languages as $language) {
+ // @TODO $language->name is not wrapped with t(), it should be replaced
+ // by CMI translation implementation.
+ $langcodes_options[$language->langcode] = $language->name;
+ }
+ $form['visibility']['language'] = array(
+ '#type' => 'fieldset',
+ '#title' => t('Languages'),
+ '#collapsible' => TRUE,
+ '#collapsed' => TRUE,
+ '#group' => 'visibility',
+ '#weight' => 5,
+ );
+ // If there are multiple configurable language types, let the user pick
+ // which one should be applied to this visibility setting. This way users
+ // can limit blocks by interface language or content language for exmaple.
+ $language_types = language_types_info();
+ $language_type_options = array();
+ foreach ($configurable_language_types as $type_key) {
+ $language_type_options[$type_key] = $language_types[$type_key]['name'];
+ }
+ $form['visibility']['language']['language_type'] = array(
+ '#type' => 'radios',
+ '#title' => t('Language type'),
+ '#options' => $language_type_options,
+ '#default_value' => !empty($this->configuration['visibility']['language']['language_type']) ? $this->configuration['visibility']['language']['language_type'] : $configurable_language_types[0],
+ '#access' => count($language_type_options) > 1,
+ );
+ $form['visibility']['language']['langcodes'] = array(
+ '#type' => 'checkboxes',
+ '#title' => t('Show this block only for specific languages'),
+ '#default_value' => !empty($this->configuration['visibility']['language']['langcodes']) ? $this->configuration['visibility']['language']['langcodes'] : array(),
+ '#options' => $langcodes_options,
+ '#description' => t('Show this block only for the selected language(s). If you select no languages, the block will be visibile in all languages.'),
+ );
+ }
+
+ // Per-role visibility.
+ $role_options = array_map('check_plain', user_roles());
+ $form['visibility']['role'] = array(
+ '#type' => 'fieldset',
+ '#title' => t('Roles'),
+ '#collapsible' => TRUE,
+ '#collapsed' => TRUE,
+ '#group' => 'visibility',
+ '#weight' => 10,
+ );
+ $form['visibility']['role']['roles'] = array(
+ '#type' => 'checkboxes',
+ '#title' => t('Show block for specific roles'),
+ '#default_value' => !empty($this->configuration['visibility']['role']['roles']) ? $this->configuration['visibility']['role']['roles'] : array(),
+ '#options' => $role_options,
+ '#description' => t('Show this block only for the selected role(s). If you select no roles, the block will be visible to all users.'),
+ );
+
+ $form += $this->configure($form, $form_state);
+
+ $form['actions'] = array('#type' => 'actions');
+ $form['actions']['submit'] = array(
+ '#type' => 'submit',
+ '#value' => t('Save block'),
+ );
+
+ return $form;
+ }
+
+ /**
+ * Implements BlockInterface::configure().
+ */
+ public function configure($form, &$form_state) {
+ return array();
+ }
+
+ /**
+ * Implements BlockInterface::configureValidateWrapper().
+ */
+ public function configureValidateWrapper($form, &$form_state) {
+ if (empty($form['settings']['machine_name']['#disabled'])) {
+ if (preg_match('/[^a-zA-Z0-9_]/', $form_state['values']['machine_name'])) {
+ form_set_error('machine_name', t('Block name must be alphanumeric or underscores only.'));
+ }
+ if (in_array('plugin.core.block.' . $form_state['values']['machine_name'], config_get_storage_names_with_prefix('plugin.core.block'))) {
+ form_set_error('machine_name', t('Block name must be unique.'));
+ }
+ }
+ else {
+ $config_id = explode('.', $form_state['values']['machine_name']);
+ $form_state['values']['machine_name'] = array_pop($config_id);
+ }
+ 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'],
+ ':info' => $form_state['values']['info'],
+ ))->fetchField();
+ if (empty($form_state['values']['info']) || $custom_block_exists) {
+ form_set_error('info', t('Ensure that each block description is unique.'));
+ }
+ }
+ $form_state['values']['visibility']['role']['roles'] = array_filter($form_state['values']['visibility']['role']['roles']);
+ $this->configureValidate($form, &$form_state);
+ }
+
+ /**
+ * Implements BlockInterface::configureValidate().
+ */
+ public function configureValidate($form, &$form_state) {}
+
+ /**
+ * Implements BlockInterface::configureSubmitWrapper().
+ */
+ public function configureSubmitWrapper($form, &$form_state) {
+ if (!form_get_errors()) {
+ $transaction = db_transaction();
+ try {
+ $keys = array(
+ 'visibility' => 'visibility',
+ 'pages' => 'pages',
+ 'custom' => 'custom',
+ 'title' => 'subject',
+ 'module' => 'module',
+ 'region' => 'region',
+ );
+ foreach ($keys as $key => $new_key) {
+ if (isset($form_state['values'][$key])) {
+ $this->configuration[$new_key] = $form_state['values'][$key];
+ }
+ }
+ }
+ catch (Exception $e) {
+ $transaction->rollback();
+ watchdog_exception('block', $e);
+ throw $e;
+ }
+ if (empty($this->configuration['weight'])) {
+ $this->configuration['weight'] = 0;
+ }
+ drupal_set_message(t('The block configuration has been saved.'));
+ drupal_flush_all_caches();
+ $this->configureSubmit($form, &$form_state);
+ }
+ }
+
+ /**
+ * Implements BlockInterface::configureSubmit().
+ */
+ public function configureSubmit($form, &$form_state) {}
+}
diff --git a/core/modules/block/lib/Drupal/block/BlockBundle.php b/core/modules/block/lib/Drupal/block/BlockBundle.php
new file mode 100644
index 0000000..5ea31a3
--- /dev/null
+++ b/core/modules/block/lib/Drupal/block/BlockBundle.php
@@ -0,0 +1,24 @@
+register('plugin.manager.block', 'Drupal\block\Plugin\Type\BlockManager');
+ }
+}
\ No newline at end of file
diff --git a/core/modules/block/lib/Drupal/block/BlockInterface.php b/core/modules/block/lib/Drupal/block/BlockInterface.php
new file mode 100644
index 0000000..601034a
--- /dev/null
+++ b/core/modules/block/lib/Drupal/block/BlockInterface.php
@@ -0,0 +1,72 @@
+derivatives) && !empty($this->derivatives[$derivative_id])) {
+ return $this->derivatives[$derivative_id];
+ }
+ $this->getDerivativeDefinitions($base_plugin_definition);
+ return $this->derivatives[$derivative_id];
+ }
+
+ /**
+ * Implements DerivativeInterface::getDerivativeDefinitions().
+ */
+ public function getDerivativeDefinitions(array $base_plugin_definition) {
+ foreach (list_themes() as $key => $theme) {
+ $this->derivatives[$key] = $base_plugin_definition;
+ }
+ return $this->derivatives;
+ }
+}
\ No newline at end of file
diff --git a/core/modules/block/lib/Drupal/block/Plugin/Type/BlockManager.php b/core/modules/block/lib/Drupal/block/Plugin/Type/BlockManager.php
new file mode 100644
index 0000000..6ffb31f
--- /dev/null
+++ b/core/modules/block/lib/Drupal/block/Plugin/Type/BlockManager.php
@@ -0,0 +1,18 @@
+discovery = new AlterDecorator(new DerivativeDiscoveryDecorator(new AnnotatedClassDiscovery('block', 'block')), 'block');
+ $this->factory = new DefaultFactory($this);
+ $this->mapper = new ConfigMapper($this);
+ }
+}
\ No newline at end of file
diff --git a/core/modules/block/lib/Drupal/block/Plugin/system/plugin_ui/BlockPluginUI.php b/core/modules/block/lib/Drupal/block/Plugin/system/plugin_ui/BlockPluginUI.php
new file mode 100644
index 0000000..edf0351
--- /dev/null
+++ b/core/modules/block/lib/Drupal/block/Plugin/system/plugin_ui/BlockPluginUI.php
@@ -0,0 +1,67 @@
+ 'value',
+ '#value' => $theme,
+ );
+ return parent::form($form, $form_state, $plugin, $facet);
+ }
+
+ public function excludeDefinitions($definitions) {
+ foreach ($definitions as $key => $definition) {
+ if (!empty($definition['custom'])) {
+ unset($definitions[$key]);
+ }
+ }
+ return $definitions;
+ }
+
+ public function formSubmit($form, &$form_state) {
+ $form_state['redirect'] = 'admin/structure/block/manage/' . $form_state['values']['block'] . '/' . $form_state['values']['theme'];
+ }
+
+ public function access($plugin_id) {
+ list($plugin, $theme) = explode(':', $plugin_id);
+ return _block_themes_access($theme);
+ }
+
+ public function tableHeader() {
+ return array(t('Subject'), t('Operations'));
+ }
+
+ public function row($plugin, $plugin_definition, $plugin_id, $config) {
+ list($plugin, $theme) = explode(':', $plugin);
+ return array($config['subject'], l($plugin_definition['link_title'], $plugin_definition['config_path'] . '/' . $plugin_id . '/' . $theme));
+ }
+}
\ No newline at end of file
diff --git a/core/modules/block/lib/Drupal/block/Tests/BlockCacheTest.php b/core/modules/block/lib/Drupal/block/Tests/BlockCacheTest.php
index 3a5776c..ebaa4c0 100644
--- a/core/modules/block/lib/Drupal/block/Tests/BlockCacheTest.php
+++ b/core/modules/block/lib/Drupal/block/Tests/BlockCacheTest.php
@@ -49,8 +49,13 @@ function setUp() {
$this->normal_user_alt->save();
// Enable our test block.
- $edit['blocks[block_test_test_cache][region]'] = 'sidebar_first';
- $this->drupalPost('admin/structure/block', $edit, t('Save blocks'));
+ $block = array();
+ $block['id'] = 'test_cache';
+ $block['machine_name'] = $this->randomName(8);
+ $block['theme'] = variable_get('theme_default', 'stark');
+ $block['region'] = 'sidebar_first';
+ $this->block = $block;
+ $this->drupalPost('admin/structure/block/manage/' . $block['id'] . '/' . $block['theme'], array('machine_name' => $block['machine_name'], 'region' => $block['region']), t('Save block'));
}
/**
@@ -192,14 +197,17 @@ function testCachePerPage() {
* Private helper method to set the test block's cache mode.
*/
private function setCacheMode($cache_mode) {
- db_update('block')
- ->fields(array('cache' => $cache_mode))
- ->condition('module', 'block_test')
- ->execute();
-
- $current_mode = db_query("SELECT cache FROM {block} WHERE module = 'block_test'")->fetchField();
- if ($current_mode != $cache_mode) {
- $this->fail(t('Unable to set cache mode to %mode. Current mode: %current_mode', array('%mode' => $cache_mode, '%current_mode' => $current_mode)));
+ $block = $this->block;
+ $block['config_id'] = 'plugin.core.block.' . $block['theme'] . '.' . $block['machine_name'];
+ $block_config = config($block['config_id']);
+ $block_config->set('cache', $cache_mode);
+ $block_config->save();
+
+ $instance = block_load($block['config_id']);
+ $config = $instance->getConfig();
+ if ($config['cache'] != $cache_mode) {
+ $this->fail(t('Unable to set cache mode to %mode. Current mode: %current_mode', array('%mode' => $cache_mode, '%current_mode' => $config['cache'])));
}
+ $this->assertEqual($config['cache'], $cache_mode, t("Test block's database entry updated to DRUPAL_NO_CACHE."));
}
}
diff --git a/core/modules/block/lib/Drupal/block/Tests/BlockHtmlIdTest.php b/core/modules/block/lib/Drupal/block/Tests/BlockHtmlIdTest.php
index 705bc76..c537eec 100644
--- a/core/modules/block/lib/Drupal/block/Tests/BlockHtmlIdTest.php
+++ b/core/modules/block/lib/Drupal/block/Tests/BlockHtmlIdTest.php
@@ -37,12 +37,19 @@ function setUp() {
$this->drupalLogin($this->admin_user);
// Enable our test block.
- $edit['blocks[block_test_test_html_id][region]'] = 'sidebar_first';
- $this->drupalPost('admin/structure/block', $edit, t('Save blocks'));
+ $block = array();
+ $block['id'] = 'test_html_id';
+ $block['machine_name'] = $this->randomName(8);
+ $block['theme'] = variable_get('theme_default', 'stark');
+ $block['region'] = 'sidebar_first';
+ $this->block = $block;
+ $this->drupalPost('admin/structure/block/manage/' . $block['id'] . '/' . $block['theme'], array('machine_name' => $block['machine_name'], 'region' => $block['region']), t('Save block'));
// Make sure the block has some content so it will appear
$current_content = $this->randomName();
variable_set('block_test_content', $current_content);
+ // TODO: This block is being cached. Should it be?
+ $this->resetAll();
}
/**
diff --git a/core/modules/block/lib/Drupal/block/Tests/BlockTest.php b/core/modules/block/lib/Drupal/block/Tests/BlockTest.php
index 1b598e3..b35183b 100644
--- a/core/modules/block/lib/Drupal/block/Tests/BlockTest.php
+++ b/core/modules/block/lib/Drupal/block/Tests/BlockTest.php
@@ -320,22 +320,23 @@ function testBlockVisibilityPerUser() {
* Test configuring and moving a module-define block to specific regions.
*/
function testBlock() {
- // Select the Navigation block to be configured and moved.
+ // Select the 'Powered by Drupal' block to be configured and moved.
$block = array();
- $block['module'] = 'system';
- $block['delta'] = 'management';
+ $block['id'] = 'system_powered_by_block';
$block['title'] = $this->randomName(8);
+ $block['machine_name'] = $this->randomName(8);
+ $block['theme'] = variable_get('theme_default', 'stark');
+ $block['region'] = 'header';
// Set block title to confirm that interface works and override any custom titles.
- $this->drupalPost('admin/structure/block/manage/' . $block['module'] . '/' . $block['delta'] . '/configure', array('title' => $block['title']), t('Save block'));
+ $this->drupalPost('admin/structure/block/manage/' . $block['id'] . '/' . $block['theme'], array('title' => $block['title'], 'machine_name' => $block['machine_name'], 'region' => $block['region']), t('Save block'));
$this->assertText(t('The block configuration has been saved.'), 'Block title set.');
- $bid = db_query("SELECT bid 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 its configuration.
+ $block['config_id'] = 'plugin.core.block.' . $block['theme'] . '.' . $block['machine_name'];
+ $instance = block_load($block['config_id']);
+ $config = $instance->getConfig();
- // Check to see if the block was created by checking that it's in the database.
- $this->assertNotNull($bid, 'Block found in database');
+ $this->assertEqual($config['subject'], $block['title'], t('Stored block title found.'));
// Check whether the block can be moved to all available regions.
foreach ($this->regions as $region) {
@@ -344,31 +345,23 @@ function testBlock() {
// Set the block to the disabled region.
$edit = array();
- $edit['blocks[' . $block['module'] . '_' . $block['delta'] . '][region]'] = '-1';
+ $edit['blocks[0][region]'] = -1;
$this->drupalPost('admin/structure/block', $edit, t('Save blocks'));
// Confirm that the block was moved to the proper region.
$this->assertText(t('The block settings have been updated.'), 'Block successfully move to disabled region.');
+ $this->drupalGet('node');
$this->assertNoText(t($block['title']), 'Block no longer appears on page.');
// Confirm that the region's xpath is not available.
- $xpath = $this->buildXPathQuery('//div[@id=:id]/*', array(':id' => 'block-block-' . $bid));
- $this->assertNoFieldByXPath($xpath, FALSE, 'Custom block found in no regions.');
-
- // For convenience of developers, put the navigation block back.
- $edit = array();
- $edit['blocks[' . $block['module'] . '_' . $block['delta'] . '][region]'] = $this->regions[1];
- $this->drupalPost('admin/structure/block', $edit, t('Save blocks'));
- $this->assertText(t('The block settings have been updated.'), 'Block successfully move to first sidebar region.');
-
- $this->drupalPost('admin/structure/block/manage/' . $block['module'] . '/' . $block['delta'] . '/configure', array('title' => 'Navigation'), t('Save block'));
- $this->assertText(t('The block configuration has been saved.'), 'Block title set.');
+ $xpath = $this->buildXPathQuery('//div[@id=:id]/*', array(':id' => 'block-' . strtr(strtolower($block['machine_name']), '-', '_')));
+ $this->assertNoFieldByXPath($xpath, FALSE, t('Block found in no regions.'));
}
function moveBlockToRegion($block, $region) {
// Set the created block to a specific region.
$edit = array();
- $edit['blocks[' . $block['module'] . '_' . $block['delta'] . '][region]'] = $region;
+ $edit['blocks[0][region]'] = $region;
$this->drupalPost('admin/structure/block', $edit, t('Save blocks'));
// Confirm that the block was moved to the proper region.
@@ -380,10 +373,10 @@ function moveBlockToRegion($block, $region) {
// Confirm that the custom block was found at the proper region.
$xpath = $this->buildXPathQuery('//div[@class=:region-class]//div[@id=:block-id]/*', array(
- ':region-class' => 'region region-' . str_replace('_', '-', $region),
- ':block-id' => 'block-' . $block['module'] . '-' . $block['delta'],
+ ':region-class' => 'region region-' . drupal_html_class($region),
+ ':block-id' => 'block-' . strtr(strtolower($block['machine_name']), '-', '_'),
));
- $this->assertFieldByXPath($xpath, NULL, format_string('Custom block found in %region_name region.', array('%region_name' => $region)));
+ $this->assertFieldByXPath($xpath, NULL, t('Block found in %region_name region.', array('%region_name' => drupal_html_class($region))));
}
/**
@@ -393,19 +386,31 @@ function testBlockRehash() {
module_enable(array('block_test'));
$this->assertTrue(module_exists('block_test'), 'Test block module enabled.');
- // Our new block should be inserted in the database when we visit the
- // block management page.
- $this->drupalGet('admin/structure/block');
+ // Add a test block.
+ $plugin = block_manager()->getDefinition('test_cache');
+
+ $block = array();
+ $block['id'] = 'test_cache';
+ $block['machine_name'] = $this->randomName(8);
+ $block['theme'] = variable_get('theme_default', 'stark');
+ $block['region'] = 'header';
+ $this->drupalPost('admin/structure/block/manage/' . $block['id'] . '/' . $block['theme'], array('machine_name' => $block['machine_name'], 'region' => $block['region']), t('Save block'));
+
// Our test block's caching should default to DRUPAL_CACHE_PER_ROLE.
- $current_caching = db_query("SELECT cache FROM {block} WHERE module = 'block_test' AND delta = 'test_cache'")->fetchField();
- $this->assertEqual($current_caching, DRUPAL_CACHE_PER_ROLE, 'Test block cache mode defaults to DRUPAL_CACHE_PER_ROLE.');
+ $block['config_id'] = 'plugin.core.block.' . $block['theme'] . '.' . $block['machine_name'];
+ $instance = block_load($block['config_id']);
+ $config = $instance->getConfig();
+ $this->assertEqual($config['cache'], DRUPAL_CACHE_PER_ROLE, t('Test block cache mode defaults to DRUPAL_CACHE_PER_ROLE.'));
// Disable caching for this block.
- variable_set('block_test_caching', DRUPAL_NO_CACHE);
+ $block_config = config($block['config_id']);
+ $block_config->set('cache', DRUPAL_NO_CACHE);
+ $block_config->save();
// Flushing all caches should call _block_rehash().
$this->resetAll();
- // Verify that the database is updated with the new caching mode.
- $current_caching = db_query("SELECT cache FROM {block} WHERE module = 'block_test' AND delta = 'test_cache'")->fetchField();
- $this->assertEqual($current_caching, DRUPAL_NO_CACHE, "Test block's database entry updated to DRUPAL_NO_CACHE.");
+ // Verify that block is updated with the new caching mode.
+ $instance = block_load($block['config_id']);
+ $config = $instance->getConfig();
+ $this->assertEqual($config['cache'], DRUPAL_NO_CACHE, t("Test block's database entry updated to DRUPAL_NO_CACHE."));
}
}
diff --git a/core/modules/block/templates/block.tpl.php b/core/modules/block/templates/block.tpl.php
index 895b49f..b4405eb 100644
--- a/core/modules/block/templates/block.tpl.php
+++ b/core/modules/block/templates/block.tpl.php
@@ -41,7 +41,11 @@
* @ingroup themeable
*/
?>
+
' . format_plural($authenticated_count, 'There is currently 1 user online.', 'There are currently @count users online.') . '
', + ); + + // Display a list of currently online users. + $max_users = $this->configuration['max_list_count']; + if ($authenticated_count && $max_users) { + $build['#users'] = db_query_range('SELECT u.uid, u.name, MAX(s.timestamp) AS max_timestamp FROM {users} u INNER JOIN {sessions} s ON u.uid = s.uid WHERE s.timestamp >= :interval AND s.uid > 0 GROUP BY u.uid, u.name ORDER BY max_timestamp DESC', 0, $max_users, array(':interval' => $interval))->fetchAll(); + } + + return $build; + } +} diff --git a/core/modules/user/user.module b/core/modules/user/user.module index e42002b..0c31a9d 100644 --- a/core/modules/user/user.module +++ b/core/modules/user/user.module @@ -766,151 +766,26 @@ function user_login_block($form) { } /** - * Implements hook_block_info(). - */ -function user_block_info() { - global $user; - - $blocks['login']['info'] = t('User login'); - // Not worth caching. - $blocks['login']['cache'] = DRUPAL_NO_CACHE; - - $blocks['new']['info'] = t('Who\'s new'); - $blocks['new']['properties']['administrative'] = TRUE; - - // Too dynamic to cache. - $blocks['online']['info'] = t('Who\'s online'); - $blocks['online']['cache'] = DRUPAL_NO_CACHE; - $blocks['online']['properties']['administrative'] = TRUE; - - return $blocks; -} - -/** - * Implements hook_block_configure(). - */ -function user_block_configure($delta = '') { - global $user; - - switch ($delta) { - case 'new': - $form['user_block_whois_new_count'] = array( - '#type' => 'select', - '#title' => t('Number of users to display'), - '#default_value' => variable_get('user_block_whois_new_count', 5), - '#options' => drupal_map_assoc(array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)), - ); - return $form; - - case 'online': - $period = drupal_map_assoc(array(30, 60, 120, 180, 300, 600, 900, 1800, 2700, 3600, 5400, 7200, 10800, 21600, 43200, 86400), 'format_interval'); - $form['user_block_seconds_online'] = array('#type' => 'select', '#title' => t('User activity'), '#default_value' => variable_get('user_block_seconds_online', 900), '#options' => $period, '#description' => t('A user is considered online for this long after they have last viewed a page.')); - $form['user_block_max_list_count'] = array('#type' => 'select', '#title' => t('User list length'), '#default_value' => variable_get('user_block_max_list_count', 10), '#options' => drupal_map_assoc(array(0, 5, 10, 15, 20, 25, 30, 40, 50, 75, 100)), '#description' => t('Maximum number of currently online users to display.')); - return $form; - } -} - -/** - * Implements hook_block_save(). - */ -function user_block_save($delta = '', $edit = array()) { - global $user; - - switch ($delta) { - case 'new': - variable_set('user_block_whois_new_count', $edit['user_block_whois_new_count']); - break; - - case 'online': - variable_set('user_block_seconds_online', $edit['user_block_seconds_online']); - variable_set('user_block_max_list_count', $edit['user_block_max_list_count']); - break; - } -} - -/** - * Implements hook_block_view(). - */ -function user_block_view($delta = '') { - global $user; - - $block = array(); - - switch ($delta) { - case 'login': - // For usability's sake, avoid showing two login forms on one page. - if (!$user->uid && !(arg(0) == 'user' && !is_numeric(arg(1)))) { - - $block['subject'] = t('User login'); - $block['content']['user_login_form'] = drupal_get_form('user_login_block'); - } - return $block; - - case 'new': - if (user_access('access content')) { - // Retrieve a list of new users who have subsequently accessed the site successfully. - $items = db_query_range('SELECT uid, name FROM {users} WHERE status <> 0 AND access <> 0 ORDER BY created DESC', 0, variable_get('user_block_whois_new_count', 5))->fetchAll(); - - $block['subject'] = t('Who\'s new'); - $block['content'] = array( - '#theme' => 'item_list__user__new', - '#items' => array(), - ); - foreach ($items as $account) { - $block['content']['#items'][] = theme('username', array('account' => $account)); - } - } - return $block; - - case 'online': - if (user_access('access content')) { - // Count users active within the defined period. - $interval = REQUEST_TIME - variable_get('user_block_seconds_online', 900); - - // Perform database queries to gather online user lists. We use s.timestamp - // rather than u.access because it is much faster. - $authenticated_count = db_query("SELECT COUNT(DISTINCT s.uid) FROM {sessions} s WHERE s.timestamp >= :timestamp AND s.uid > 0", array(':timestamp' => $interval))->fetchField(); - - $block['subject'] = t('Who\'s online'); - $block['content'] = array( - '#theme' => 'item_list__user__online', - '#items' => array(), - '#prefix' => '' . format_plural($authenticated_count, 'There is currently 1 user online.', 'There are currently @count users online.') . '
', - ); - - // Display a list of currently online users. - $max_users = variable_get('user_block_max_list_count', 10); - if ($authenticated_count && $max_users) { - $items = db_query_range('SELECT u.uid, u.name, MAX(s.timestamp) AS max_timestamp FROM {users} u INNER JOIN {sessions} s ON u.uid = s.uid WHERE s.timestamp >= :interval AND s.uid > 0 GROUP BY u.uid, u.name ORDER BY max_timestamp DESC', 0, $max_users, array(':interval' => $interval))->fetchAll(); - - foreach ($items as $account) { - $block['content']['#items'][] = theme('username', array('account' => $account)); - } - } - } - return $block; - } -} - -/** * Implements hook_preprocess_HOOK() for block.tpl.php. */ function user_preprocess_block(&$variables) { if ($variables['block']->module == 'user') { - switch ($variables['block']->delta) { - case 'login': + switch ($variables['block']->id) { + case 'user_login_block': $variables['attributes']['role'] = 'form'; break; - case 'new': + case 'user_new_block': $variables['attributes']['role'] = 'complementary'; break; - case 'online': + case 'user_online_block': $variables['attributes']['role'] = 'complementary'; break; } } } + + /** * Format a username. *