diff --git a/core/modules/block/custom_block/config/custom_block.type.basic.yml b/core/modules/block/custom_block/config/custom_block.type.basic.yml new file mode 100644 index 0000000..e2bb99e --- /dev/null +++ b/core/modules/block/custom_block/config/custom_block.type.basic.yml @@ -0,0 +1,3 @@ +id: basic +label: Basic block +revision: '0' diff --git a/core/modules/block/custom_block/custom_block.admin.inc b/core/modules/block/custom_block/custom_block.admin.inc new file mode 100644 index 0000000..be1e32e --- /dev/null +++ b/core/modules/block/custom_block/custom_block.admin.inc @@ -0,0 +1,91 @@ +render(); +} + +/** + * Page callback: Presents the custom block type creation form. + * + * @return array + * A form array as expected by drupal_render(). + * + * @see custom_block_menu() + */ +function custom_block_type_add() { + drupal_set_title(t('Add custom block type')); + $block_type = entity_create('custom_block_type', array()); + return entity_get_form($block_type); +} + +/** + * Page callback: Presents the custom block type edit form. + * + * @param Drupal\custom_block\Plugin\Core\Entity\CustomBlockType $block_type + * The custom block type to edit. + * + * @return array + * A form array as expected by drupal_render(). + * + * @see custom_block_menu() + */ +function custom_block_type_edit(CustomBlockType $block_type) { + drupal_set_title(t('Edit %label custom block type', array('%label' => $block_type->label())), PASS_THROUGH); + return entity_get_form($block_type); +} + +/** + * Page callback: Form constructor for the custom block type deletion form. + * + * @param Drupal\custom_block\Plugin\Core\Entity\CustomBlockType $block_type + * The custom block type to be deleted. + * + * @see custom_block_menu() + * @see custom_block_type_delete_form_submit() + * + * @ingroup forms + */ +function custom_block_type_delete_form($form, &$form_state, CustomBlockType $block_type) { + $form_state['custom_block_type'] = $block_type; + $form['id'] = array( + '#type' => 'value', + '#value' => $block_type->id(), + ); + + return confirm_form( + $form, + t('Are you sure you want to delete %label?', array('%label' => $block_type->label())), + 'admin/structure/custom-blocks', + t('This action cannot be undone.'), + t('Delete') + ); +} + +/** + * Form submission handler for custom_block_type_delete_form(). + */ +function custom_block_type_delete_form_submit($form, &$form_state) { + $block_type = $form_state['custom_block_type']; + $block_type->delete(); + + drupal_set_message(t('Custom block type %label has been deleted.', array('%label' => $block_type->label()))); + watchdog('custom_block', 'Custom block type %label has been deleted.', array('%label' => $block_type->label()), WATCHDOG_NOTICE); + + $form_state['redirect'] = 'admin/structure/custom-blocks'; +} diff --git a/core/modules/block/custom_block/custom_block.install b/core/modules/block/custom_block/custom_block.install index 4a0e4b0..2f9ecd0 100644 --- a/core/modules/block/custom_block/custom_block.install +++ b/core/modules/block/custom_block/custom_block.install @@ -19,12 +19,11 @@ function custom_block_schema() { 'not null' => TRUE, 'description' => "The block's {block}.bid.", ), - 'body' => array( - 'type' => 'text', + 'uuid' => array( + 'description' => 'Unique Key: Universally unique identifier for this entity.', + 'type' => 'varchar', + 'length' => 128, 'not null' => FALSE, - 'size' => 'big', - 'description' => 'Block contents.', - 'translatable' => TRUE, ), 'info' => array( 'type' => 'varchar', @@ -33,17 +32,98 @@ function custom_block_schema() { 'default' => '', 'description' => 'Block description.', ), - 'format' => array( + 'machine_name' => array( 'type' => 'varchar', - 'length' => 255, + 'length' => 128, + 'not null' => TRUE, + 'default' => '', + 'description' => 'Machine name of block.', + ), + // Defaults to NULL in order to avoid a brief period of potential + // deadlocks on the index. + 'vid' => array( + 'description' => 'The current {block_custom_revision}.vid version identifier.', + 'type' => 'int', + 'unsigned' => TRUE, 'not null' => FALSE, - 'description' => 'The {filter_format}.format of the block body.', + 'default' => NULL, ), + 'type' => array( + 'description' => 'The type of this custom block.', + 'type' => 'varchar', + 'length' => 32, + 'not null' => TRUE, + 'default' => '', + ), + 'langcode' => array( + 'description' => 'The {language}.langcode of this node.', + 'type' => 'varchar', + 'length' => 12, + 'not null' => TRUE, + 'default' => '', + ), + 'tnid' => array( + 'description' => 'The translation set id for this node, which equals the node id of the source post in each set.', + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + ), + 'translate' => array( + 'description' => 'A boolean indicating whether this translation page needs to be updated.', + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + ), + ), + 'indexes' => array( + 'block_custom_type' => array(array('type', 4)), + 'tnid' => array('tnid'), + 'translate' => array('translate'), ), 'unique keys' => array( + 'vid' => array('vid'), + 'uuid' => array('uuid'), 'info' => array('info'), ), + 'foreign keys' => array( + 'block_custom_revision' => array( + 'table' => 'block_custom_revision', + 'columns' => array('vid' => 'vid'), + ), + ), 'primary key' => array('bid'), ); + + $schema['block_custom_revision'] = array( + 'description' => 'Stores contents of custom-made blocks.', + 'fields' => array( + 'bid' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + 'description' => "The block's {block}.bid.", + ), + // Defaults to NULL in order to avoid a brief period of potential + // deadlocks on the index. + 'vid' => array( + 'description' => 'The current version identifier.', + 'type' => 'serial', + 'unsigned' => TRUE, + 'not null' => TRUE, + ), + 'log' => array( + 'description' => 'The log entry explaining the changes in this version.', + 'type' => 'text', + 'not null' => TRUE, + 'size' => 'big', + ), + ), + 'unique keys' => array( + 'vid' => array('vid'), + ), + 'primary key' => array('vid'), + ); return $schema; } diff --git a/core/modules/block/custom_block/custom_block.js b/core/modules/block/custom_block/custom_block.js new file mode 100644 index 0000000..a35d438 --- /dev/null +++ b/core/modules/block/custom_block/custom_block.js @@ -0,0 +1,30 @@ +/** + * @file + * Defines Javascript behaviors for the custom_block module. + */ + +(function ($) { + +"use strict"; + +Drupal.behaviors.customBlockDetailsSummaries = { + attach: function (context) { + var $context = $(context); + $context.find('.custom-block-form-revision-information').drupalSetSummary(function (context) { + var $context = $(context); + var revisionCheckbox = $context.find('.form-item-revision input'); + + // Return 'New revision' if the 'Create new revision' checkbox is checked, + // or if the checkbox doesn't exist, but the revision log does. For users + // without the "Administer content" permission the checkbox won't appear, + // but the revision log will if the content type is set to auto-revision. + if (revisionCheckbox.is(':checked') || (!revisionCheckbox.length && $context.find('.form-item-log textarea').length)) { + return Drupal.t('New revision'); + } + + return Drupal.t('No revision'); + }); + } +}; + +})(jQuery); diff --git a/core/modules/block/custom_block/custom_block.module b/core/modules/block/custom_block/custom_block.module index d3e1588..42b77c7 100644 --- a/core/modules/block/custom_block/custom_block.module +++ b/core/modules/block/custom_block/custom_block.module @@ -5,6 +5,9 @@ * Allows the creaation of custom blocks through the user interface. */ +use Drupal\custom_block\Plugin\Core\Entity\CustomBlockType; +use Drupal\custom_block\Plugin\Core\Entity\CustomBlock; + /** * Implements hook_menu(). */ @@ -19,15 +22,92 @@ function custom_block_menu() { $items['admin/structure/block/list/' . $plugin_id . '/add/custom_blocks'] = array( 'title' => 'Add custom block', 'description' => 'Create a block with custom content and settings.', - 'page callback' => 'drupal_get_form', - 'page arguments' => array('block_admin_configure', 'custom_block:custom_block', $theme), - 'access callback' => TRUE, + 'page callback' => 'drupal_goto', + 'page arguments' => array( + 'block/add', + array('query' => array('theme' => $theme)) + ), + 'access arguments' => array('administer blocks'), 'type' => MENU_LOCAL_ACTION, - 'file' => 'block.admin.inc', - 'file path' => drupal_get_path('module', 'block'), ); } } + + $items['admin/structure/custom-blocks'] = array( + 'title' => 'Block types', + 'description' => 'Manage custom block types.', + 'page callback' => 'custom_block_type_list', + 'access arguments' => array('administer blocks'), + 'file' => 'custom_block.admin.inc', + ); + $items['admin/structure/custom-blocks/add'] = array( + 'title' => 'Add custom block type', + 'page callback' => 'custom_block_type_add', + 'access arguments' => array('administer blocks'), + 'type' => MENU_LOCAL_ACTION, + 'weight' => 1, + 'file' => 'custom_block.admin.inc', + ); + $items['admin/structure/custom-blocks/manage/%custom_block_type'] = array( + 'title' => 'Edit custom block type', + 'title callback' => 'entity_page_label', + 'title arguments' => array(4), + 'page callback' => 'custom_block_type_edit', + 'page arguments' => array(4), + 'access arguments' => array('administer blocks'), + 'file' => 'custom_block.admin.inc', + ); + $items['admin/structure/custom-blocks/manage/%custom_block_type/edit'] = array( + 'title' => 'Edit', + 'type' => MENU_DEFAULT_LOCAL_TASK, + 'weight' => -10, + ); + $items['admin/structure/custom-blocks/manage/%custom_block_type/delete'] = array( + 'title' => 'Delete', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('custom_block_type_delete_form', 4), + 'access arguments' => array('administer blocks'), + 'type' => MENU_LOCAL_TASK, + 'weight' => 10, + 'file' => 'custom_block.admin.inc', + ); + + $items['block/add'] = array( + 'title' => 'Add custom block', + 'page callback' => 'custom_block_add_page', + 'access arguments' => array('administer blocks'), + 'file' => 'custom_block.pages.inc', + ); + + $items['block/add/%custom_block_type'] = array( + 'title callback' => 'entity_page_label', + 'title arguments' => array(2), + 'page callback' => 'custom_block_add', + 'page arguments' => array(2), + 'access arguments' => array('administer blocks'), + 'description' => 'Add custom block', + 'file' => 'custom_block.pages.inc', + ); + $items['block/%custom_block_machine_name/edit'] = array( + 'title' => 'Edit', + 'page callback' => 'custom_block_edit', + 'page arguments' => array(1), + 'access arguments' => array('administer blocks'), + 'weight' => 0, + 'type' => MENU_LOCAL_TASK, + 'context' => MENU_CONTEXT_PAGE | MENU_CONTEXT_INLINE, + 'file' => 'custom_block.pages.inc', + ); + $items['block/%custom_block_machine_name/delete'] = array( + 'title' => 'Delete', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('custom_block_delete_confirm', 1), + 'access arguments' => array('administer blocks'), + 'weight' => 1, + 'type' => MENU_LOCAL_TASK, + 'context' => MENU_CONTEXT_INLINE, + 'file' => 'custom_block.pages.inc', + ); return $items; } @@ -53,3 +133,156 @@ function theme_custom_block_block($variables) { return check_markup($body, $format); } + +/** + * Loads a custom block type. + * + * @param int $id + * The ID of the custom block type to load. + * + * @return Drupal\custom_block\Plugin\Core\Entity\CustomBlockType|false + * A CustomBlockType object or FALSE if the requested $id does not exist. + */ +function custom_block_type_load($id) { + return entity_load('custom_block_type', $id); +} + +/** + * Loads a custom block. + * + * @param int $bid + * The bid of the custom block. + * + * @return Drupal\custom_block\Plugin\Core\Entity\CustomBlock|false + * A CustomBlock object or FALSE if the requested $id does not exist. + */ +function custom_block_load($bid) { + return entity_load('custom_block', $bid); +} + +/** + * Loads a custom block. + * + * @param string $machine_name + * The machine name of the custom block. + * + * @return Drupal\custom_block\Plugin\Core\Entity\CustomBlock|false + * A CustomBlock object or FALSE if the requested $id does not exist. + */ +function custom_block_machine_name_load($machine_name) { + $block_ids = entity_query('custom_block')->condition('machine_name', $machine_name)->execute(); + if (!empty($block_ids) && ($block_id = reset($block_ids))) { + return entity_load('custom_block', $block_id); + } + return FALSE; +} + +/** + * Entity URI callback. + * + * @param Drupal\custom_block\Plugin\Core\Entity\CustomBlockType $block_type + * A custom block type entity. + * + * @return array + * An array with 'path' as the key and the path to the custom block type as + * the value. + */ +function custom_block_type_uri(CustomBlockType $block_type) { + return array( + 'path' => 'admin/structure/custom-blocks/manage/' . $block_type->id(), + ); +} + +/** + * Implements hook_entity_info(). + */ +function custom_block_entity_info_alter(&$types) { + foreach (config_get_storage_names_with_prefix('custom_block.type.') as $config_name) { + $config = config($config_name); + $types['custom_block']['bundles'][$config->get('id')] = array( + 'label' => $config->get('label'), + 'admin' => array( + 'path' => 'admin/structure/custom-blocks/manage/%', + 'real path' => 'admin/structure/custom-blocks/manage/' . $config->get('id'), + 'bundle argument' => 4, + 'access arguments' => array('administer blocks'), + ), + ); + } +} + +/** + * Adds the default body field to a custom block type. + * + * @param string $block_type_id + * Id of the block type. + * @param string $label + * (optional) The label for the body instance. Defaults to 'Block body' + * + * @return array() + * Body field instance. + */ +function custom_block_add_body_field($block_type_id, $label = 'Block body') { + // Add or remove the body field, as needed. + $field = field_info_field('block_body'); + $instance = field_info_instance('custom_block', 'block_body', $block_type_id); + if (empty($field)) { + $field = array( + 'field_name' => 'block_body', + 'type' => 'text_with_summary', + 'entity_types' => array('custom_block'), + ); + $field = field_create_field($field); + } + if (empty($instance)) { + $instance = array( + 'field_name' => 'block_body', + 'entity_type' => 'custom_block', + 'bundle' => $block_type_id, + 'label' => $label, + 'widget' => array('type' => 'text_textarea_with_summary'), + 'settings' => array('display_summary' => FALSE), + ); + $instance = field_create_instance($instance); + + // Assign display settings for the 'full'. + entity_get_display('custom_block', $block_type_id, 'full') + ->setComponent($field['field_name'], array( + 'label' => 'hidden', + 'type' => 'text_default', + )) + ->save(); + } + + return $instance; +} + +/** + * Implements hook_form_FORM_ID_alter() for block_plugin_ui(). + */ +function custom_block_form_block_plugin_ui_alter(&$form, $form_state) { + foreach ($form['left']['plugin_library']['#rows'] as $plugin_id => &$row) { + @list($base, $derivative) = explode(':', $plugin_id); + if ($base !== 'custom_block') { + continue; + } + $row['1']['data']['#links']['edit'] = array( + 'title' => t('Edit'), + 'href' => 'block/' . $derivative . '/edit' + ); + } +} + +/** + * Implements hook_admin_paths(). + */ +function custom_block_admin_paths() { + $paths = array( + 'block/add' => TRUE, + 'block/add/*' => TRUE, + 'block/*/edit' => TRUE, + 'block/*/delete' => TRUE, + 'admin/structure/custom-blocks/*' => TRUE, + ); + return $paths; +} diff --git a/core/modules/block/custom_block/custom_block.pages.inc b/core/modules/block/custom_block/custom_block.pages.inc new file mode 100644 index 0000000..958a610 --- /dev/null +++ b/core/modules/block/custom_block/custom_block.pages.inc @@ -0,0 +1,139 @@ + array('theme' => $_GET['theme']) + ); + } + $types = config_get_storage_names_with_prefix('custom_block.type.'); + if (count($types) == 1) { + $config = config(reset($types)); + drupal_goto('block/add/' . $config->get('id'), $options); + } + // Multiple custom block types exist, present a list of links. + $links = array(); + + foreach ($types as $config_name) { + $config = config($config_name); + $links[] = l($config->get('label'), 'block/add/' . $config->get('id'), $options); + } + return array( + '#theme' => array( + 'item_list', + 'item_list__custom_block_add' + ), + '#items' => $links, + '#title' => t('Choose a block type to continue.') + ); +} + +/** + * Page callback: Presents the custom block creation form. + * + * @param Drupal\custom_block\Plugin\Core\Entity\CustomBlockType $block_type + * The custom block type to add. + * + * @return array + * A form array as expected by drupal_render(). + * + * @see custom_block_menu() + */ +function custom_block_add(CustomBlockType $block_type) { + drupal_set_title(t('Add %type custom block', array( + '%type' => $block_type->label() + )), PASS_THROUGH); + $block = entity_create('custom_block', array( + 'type' => $block_type->id() + )); + $options = array(); + if (isset($_GET['theme']) && in_array($_GET['theme'], array_keys(list_themes()))) { + // We have navigated to this page from the block library and will keep track + // of the theme for redirecting the user to the configuration page for the + // newly created block in the given theme. + $block->setTheme($_GET['theme']); + } + return entity_get_form($block); +} + +/** + * Page callback: Presents the custom block edit form. + * + * @param Drupal\custom_block\Plugin\Core\Entity\CustomBlock $block + * The custom block to edit. + * + * @return array + * A form array as expected by drupal_render(). + * + * @see custom_block_menu() + */ +function custom_block_edit(CustomBlock $block) { + drupal_set_title(t('Edit custom block %label', array('%label' => $block->label())), PASS_THROUGH); + return entity_get_form($block); +} + +/** + * Page callback: Form constructor for the custom block deletion form. + * + * @param Drupal\custom_block\Plugin\Core\Entity\CustomBlock $block + * The custom block to be deleted. + * + * @see custom_block_menu() + * @see custom_block_delete_form_submit() + * + * @ingroup forms + */ +function custom_block_delete_form($form, &$form_state, CustomBlock $block) { + $form_state['custom_block'] = $block; + $form['id'] = array( + '#type' => 'value', + '#value' => $block->id(), + ); + + return confirm_form( + $form, + t('Are you sure you want to delete %label?', array('%label' => $block->label())), + 'admin/structure/block', + t('This action cannot be undone.'), + t('Delete') + ); +} + +/** + * Form submission handler for custom_block_delete_form(). + */ +function custom_block_delete_form_submit($form, &$form_state) { + // @todo Delete all configured instances of the block. + $block = $form_state['custom_block']; + $block->delete(); + + drupal_set_message(t('Custom block %label has been deleted.', array('%label' => $block->label()))); + watchdog('custom_block', 'Custom block %label has been deleted.', array('%label' => $block->label()), WATCHDOG_NOTICE); + + $form_state['redirect'] = 'admin/structure/block'; +} diff --git a/core/modules/block/custom_block/lib/Drupal/custom_block/CustomBlockFormController.php b/core/modules/block/custom_block/lib/Drupal/custom_block/CustomBlockFormController.php new file mode 100644 index 0000000..8cfdbdd --- /dev/null +++ b/core/modules/block/custom_block/lib/Drupal/custom_block/CustomBlockFormController.php @@ -0,0 +1,240 @@ +type); + // If this is a new custom block, fill in the default values. + if (isset($block->bid)) { + $block->log = NULL; + } + // Always use the default revision setting. + $block->setNewRevision($block_type->revision); + + module_invoke_all('custom_block_prepare', $block); + } + + /** + * Overrides Drupal\Core\Entity\EntityFormController::form(). + */ + public function form(array $form, array &$form_state, EntityInterface $block) { + + // Override the default CSS class name, since the user-defined custom block + // type name in 'TYPE-block-form' potentially clashes with third-party class + // names. + $form['#attributes']['class'][0] = drupal_html_class('block-' . $block->type . '-form'); + + // Basic block information. + // These elements are just values so they are not even sent to the client. + foreach (array('bid', 'vid') as $key) { + $form[$key] = array( + '#type' => 'value', + '#value' => isset($block->$key) ? $block->$key : NULL, + ); + } + + $form['info'] = array( + '#type' => 'textfield', + '#title' => t('Block description'), + '#required' => TRUE, + '#default_value' => $block->info, + '#weight' => -5, + '#description' => t('A brief description of your block. Used on the Blocks administration page.', array('@overview' => url('admin/structure/block'))), + ); + + $form['machine_name'] = array( + '#type' => 'machine_name', + '#default_value' => $block->machine_name, + '#machine_name' => array( + 'exists' => 'custom_block_load', + 'source' => array('info') + ), + '#weight' => -4, + '#disabled' => !$block->isNew(), + ); + + $language_configuration = module_invoke('language', 'get_default_configuration', 'custom_block', $block->type); + $form['langcode'] = array( + '#title' => t('Language'), + '#type' => 'language_select', + '#default_value' => $block->langcode, + '#languages' => LANGUAGE_ALL, + '#access' => isset($language_configuration['language_hidden']) && !$language_configuration['language_hidden'], + ); + + $form['additional_settings'] = array( + '#type' => 'vertical_tabs', + '#weight' => 99, + ); + + // Add a log field if the "Create new revision" option is checked, or if the + // current user has the ability to check that option. + $form['revision_information'] = array( + '#type' => 'details', + '#title' => t('Revision information'), + '#collapsible' => TRUE, + // Collapsed by default when "Create new revision" is unchecked. + '#collapsed' => !$block->isNewRevision(), + '#group' => 'additional_settings', + '#attributes' => array( + 'class' => array('custom-block-form-revision-information'), + ), + '#attached' => array( + 'js' => array(drupal_get_path('module', 'custom_block') . '/custom_block.js'), + ), + '#weight' => 20, + '#access' => $block->isNewRevision() || user_access('administer blocks'), + ); + + $form['revision_information']['revision'] = array( + '#type' => 'checkbox', + '#title' => t('Create new revision'), + '#default_value' => $block->isNewRevision(), + '#access' => user_access('administer blocks'), + ); + + // Check the revision log checkbox when the log textarea is filled in. + // This must not happen if "Create new revision" is enabled by default, + // since the state would auto-disable the checkbox otherwise. + if (!$block->isNewRevision()) { + $form['revision_information']['revision']['#states'] = array( + 'checked' => array( + 'textarea[name="log"]' => array('empty' => FALSE), + ), + ); + } + + $form['revision_information']['log'] = array( + '#type' => 'textarea', + '#title' => t('Revision log message'), + '#rows' => 4, + '#default_value' => !empty($block->log) ? $block->log : '', + '#description' => t('Briefly describe the changes you have made.'), + ); + + return parent::form($form, $form_state, $block); + } + + /** + * Overrides Drupal\Core\Entity\EntityFormController::validate(). + */ + public function validate(array $form, array &$form_state) { + $block = $this->buildEntity($form, $form_state); + + foreach (module_implements('custom_block_validate') as $module) { + $function = $module . '_custom_block_validate'; + $function($block, $form, $form_state); + } + + parent::validate($form, $form_state); + } + + /** + * Updates the custom block object by processing the submitted values. + * + * This function can be called by a "Next" button of a wizard to update the + * form state's entity with the current step's values before proceeding to the + * next step. + * + * Overrides Drupal\Core\Entity\EntityFormController::submit(). + */ + public function submit(array $form, array &$form_state) { + // Build the block object from the submitted values. + $block = parent::submit($form, $form_state); + + // Save as a new revision if requested to do so. + if (!empty($form_state['values']['revision'])) { + $block->setNewRevision(); + } + + foreach (module_implements('custom_block_submit') as $module) { + $function = $module . '_custom_block_submit'; + $function($block, $form, $form_state); + } + + return $block; + } + + /** + * Overrides Drupal\Core\Entity\EntityFormController::save(). + */ + public function save(array $form, array &$form_state) { + $block = $this->getEntity($form_state); + $insert = empty($block->bid); + $block->save(); + $watchdog_args = array('@type' => $block->bundle(), '%info' => $block->label()); + $block_type = entity_load('custom_block_type', $block->type); + $t_args = array('@type' => $block_type->label(), '%info' => $block->label()); + + if ($insert) { + watchdog('content', '@type: added %info.', $watchdog_args, WATCHDOG_NOTICE); + drupal_set_message(t('@type %info has been created.', $t_args)); + } + else { + watchdog('content', '@type: updated %info.', $watchdog_args, WATCHDOG_NOTICE); + drupal_set_message(t('@type %info has been updated.', $t_args)); + } + + if ($block->bid) { + $form_state['values']['bid'] = $block->bid; + $form_state['bid'] = $block->bid; + if ($insert) { + if ($theme = $block->getTheme()) { + $form_state['redirect'] = 'admin/structure/block/manage/custom_block:' . $block->machine_name . '/' . $theme; + } + else { + $form_state['redirect'] = 'admin/structure/block/manage/custom_block:' . $block->machine_name . '/' . variable_get('theme_default', 'stark'); + } + } + else { + $form_state['redirect'] = 'admin/structure/block'; + } + } + else { + // In the unlikely case something went wrong on save, the block will be + // rebuilt and block form redisplayed. + drupal_set_message(t('The block could not be saved.'), 'error'); + $form_state['rebuild'] = TRUE; + } + + // Clear the page and block caches. + cache_invalidate_tags(array('content' => TRUE)); + } + + /** + * Overrides Drupal\Core\Entity\EntityFormController::delete(). + */ + public function delete(array $form, array &$form_state) { + $destination = array(); + if (isset($_GET['destination'])) { + $destination = drupal_get_destination(); + unset($_GET['destination']); + } + $block = $this->getEntity($form_state); + $form_state['redirect'] = array('block/' . $block->bid . '/delete', array('query' => $destination)); + } + +} diff --git a/core/modules/block/custom_block/lib/Drupal/custom_block/CustomBlockRenderController.php b/core/modules/block/custom_block/lib/Drupal/custom_block/CustomBlockRenderController.php new file mode 100644 index 0000000..eb227d3 --- /dev/null +++ b/core/modules/block/custom_block/lib/Drupal/custom_block/CustomBlockRenderController.php @@ -0,0 +1,29 @@ +bid) && $view_mode == 'full') { + $build['#contextual_links']['custom_block'] = array('custom_block', array($entity->machine_name)); + } + } + +} diff --git a/core/modules/block/custom_block/lib/Drupal/custom_block/CustomBlockStorageController.php b/core/modules/block/custom_block/lib/Drupal/custom_block/CustomBlockStorageController.php new file mode 100644 index 0000000..1d3dcb1 --- /dev/null +++ b/core/modules/block/custom_block/lib/Drupal/custom_block/CustomBlockStorageController.php @@ -0,0 +1,60 @@ +isNewRevision()) { + // When inserting either a new custom block or a new custom_block + // revision, $entity->log must be set because {block_custom_revision}.log + // is a text column and therefore cannot have a default value. However, + // it might not be set at this point (for example, if the user submitting + // the form does not have permission to create revisions), so we ensure + // that it is at least an empty string in that case. + // @todo: Make the {block_custom_revision}.log column nullable so that we + // can remove this check. + if (!isset($record->log)) { + $record->log = ''; + } + } + elseif (!isset($record->log) || $record->log === '') { + // If we are updating an existing custom_block without adding a new + // revision, we need to make sure $entity->log is unset whenever it is + // empty. As long as $entity->log is unset, drupal_write_record() will not + // attempt to update the existing database column when re-saving the + // revision; therefore, this code allows us to avoid clobbering an + // existing log entry with an empty one. + unset($record->log); + } + + } + + /** + * Overrides Drupal\Core\Entity\DatabaseStorageController::postSave(). + */ + function postSave(EntityInterface $block, $update) { + // Invalidate the block cache to update custom block-based derivatives. + if (module_exists('block')) { + drupal_container()->get('plugin.manager.block')->clearCachedDefinitions(); + } + } + +} diff --git a/core/modules/block/custom_block/lib/Drupal/custom_block/CustomBlockTypeFormController.php b/core/modules/block/custom_block/lib/Drupal/custom_block/CustomBlockTypeFormController.php new file mode 100644 index 0000000..e17e040 --- /dev/null +++ b/core/modules/block/custom_block/lib/Drupal/custom_block/CustomBlockTypeFormController.php @@ -0,0 +1,85 @@ + 'textfield', + '#title' => t('Label'), + '#maxlength' => 255, + '#default_value' => $block_type->label(), + '#description' => t("Provide a label for this block type to help identify it in the administration pages."), + '#required' => TRUE, + ); + $form['id'] = array( + '#type' => 'machine_name', + '#default_value' => $block_type->id(), + '#machine_name' => array( + 'exists' => 'custom_block_type_load', + ), + '#disabled' => !$block_type->isNew(), + ); + + $form['revision'] = array( + '#type' => 'checkbox', + '#title' => t('Create new revision'), + '#default_value' => $block_type->revision, + '#desciption' => t('Create a new revision by default for this block type.') + ); + + $form['actions'] = array('#type' => 'actions'); + $form['actions']['submit'] = array( + '#type' => 'submit', + '#value' => t('Save'), + ); + + return $form; + } + + /** + * Overrides Drupal\Core\Entity\EntityFormController::save(). + */ + public function save(array $form, array &$form_state) { + $block_type = $this->getEntity($form_state); + $status = $block_type->save(); + + $uri = $block_type->uri(); + if ($status == SAVED_UPDATED) { + drupal_set_message(t('Custom block type %label has been updated.', array('%label' => $block_type->label()))); + watchdog('custom_block', 'Custom block type %label has been updated.', array('%label' => $block_type->label()), WATCHDOG_NOTICE, l(t('Edit'), $uri['path'] . '/edit')); + } + else { + drupal_set_message(t('Custom block type %label has been added.', array('%label' => $block_type->label()))); + watchdog('custom_block', 'Custom block type %label has been added.', array('%label' => $block_type->label()), WATCHDOG_NOTICE, l(t('Edit'), $uri['path'] . '/edit')); + } + + $form_state['redirect'] = 'admin/structure/custom-blocks'; + } + + /** + * Overrides Drupal\Core\Entity\EntityFormController::delete(). + */ + public function delete(array $form, array &$form_state) { + $block_type = $this->getEntity($form_state); + $form_state['redirect'] = 'admin/structure/custom-blocks/manage/' . $block_type->id() . '/delete'; + } + +} diff --git a/core/modules/block/custom_block/lib/Drupal/custom_block/CustomBlockTypeListController.php b/core/modules/block/custom_block/lib/Drupal/custom_block/CustomBlockTypeListController.php new file mode 100644 index 0000000..a59799e --- /dev/null +++ b/core/modules/block/custom_block/lib/Drupal/custom_block/CustomBlockTypeListController.php @@ -0,0 +1,58 @@ +uri(); + $operations['manage-fields'] = array( + 'title' => t('Manage fields'), + 'href' => $uri['path'] . '/fields', + 'options' => $uri['options'], + 'weight' => 11, + ); + $operations['manage-display'] = array( + 'title' => t('Manage display'), + 'href' => $uri['path'] . '/display', + 'options' => $uri['options'], + 'weight' => 12, + ); + } + return $operations; + } + + /** + * Overrides Drupal\Core\Entity\EntityListController::buildHeader(). + */ + public function buildHeader() { + $row['type'] = t('Block type'); + $row['operations'] = t('Operations'); + return $row; + } + + /** + * Overrides Drupal\Core\Entity\EntityListController::buildRow(). + */ + public function buildRow(EntityInterface $entity) { + $row['type'] = check_plain($entity->label()); + $row['operations']['data'] = $this->buildOperations($entity); + return $row; + } + +} diff --git a/core/modules/block/custom_block/lib/Drupal/custom_block/CustomBlockTypeStorageController.php b/core/modules/block/custom_block/lib/Drupal/custom_block/CustomBlockTypeStorageController.php new file mode 100644 index 0000000..2e27b39 --- /dev/null +++ b/core/modules/block/custom_block/lib/Drupal/custom_block/CustomBlockTypeStorageController.php @@ -0,0 +1,44 @@ +id()); + } + elseif ($entity->original->id() != $entity->id()) { + field_attach_rename_bundle('custom_block', $entity->original->id(), $entity->id()); + } + custom_block_add_body_field($entity->id()); + } + + /** + * Overrides \Drupal\Core\Config\Entity\ConfigStorageController::postDelete(). + */ + protected function postDelete($entities) { + parent::postDelete($entities); + + foreach ($entities as $entity) { + field_attach_delete_bundle('custom_block', $entity->id()); + } + } + +} diff --git a/core/modules/block/custom_block/lib/Drupal/custom_block/Plugin/Core/Entity/CustomBlock.php b/core/modules/block/custom_block/lib/Drupal/custom_block/Plugin/Core/Entity/CustomBlock.php new file mode 100644 index 0000000..7428aee --- /dev/null +++ b/core/modules/block/custom_block/lib/Drupal/custom_block/Plugin/Core/Entity/CustomBlock.php @@ -0,0 +1,207 @@ +bid; + } + + /** + * Implements Drupal\Core\Entity\EntityInterface::bundle(). + */ + public function bundle() { + return $this->type; + } + + /** + * Implements Drupal\Core\Entity\EntityInterface::label(). + */ + public function label($langcode = NULL) { + return $this->info; + } + + /** + * Overrides Drupal\Core\Entity\Entity::createDuplicate(). + */ + public function createDuplicate() { + $duplicate = parent::createDuplicate(); + $duplicate->vid = NULL; + return $duplicate; + } + + /** + * Overrides Drupal\Core\Entity\Entity::getRevisionId(). + */ + public function getRevisionId() { + return $this->vid; + } + + /** + * Sets the theme value. + * + * When creating a new custom block from the block library, the user is + * redirected to the configure form for that block in the given theme. The + * theme is stored against the block when the custom block add form is shown. + * + * @param string $theme + * The theme name. + */ + public function setTheme($theme) { + $this->theme = $theme; + } + + /** + * Gets the theme value. + * + * When creating a new custom block from the block library, the user is + * redirected to the configure form for that block in the given theme. The + * theme is stored against the block when the custom block add form is shown. + * + * @return string + * The theme name. + */ + public function getTheme() { + return $this->theme; + } +} diff --git a/core/modules/block/custom_block/lib/Drupal/custom_block/Plugin/Core/Entity/CustomBlockType.php b/core/modules/block/custom_block/lib/Drupal/custom_block/Plugin/Core/Entity/CustomBlockType.php new file mode 100644 index 0000000..fb6f2b5 --- /dev/null +++ b/core/modules/block/custom_block/lib/Drupal/custom_block/Plugin/Core/Entity/CustomBlockType.php @@ -0,0 +1,65 @@ +derivatives[$result->bid] = $base_plugin_definition; - $this->derivatives[$result->bid]['settings'] = array( + $this->derivatives[$result->machine_name] = $base_plugin_definition; + $this->derivatives[$result->machine_name]['settings'] = array( 'info' => $result->info, - 'body' => $result->body, - 'format' => $result->format, ) + $base_plugin_definition['settings']; - $this->derivatives[$result->bid]['subject'] = $result->info; + $this->derivatives[$result->machine_name]['subject'] = $result->info; } - $this->derivatives['custom_block'] = $base_plugin_definition; return $this->derivatives; } } diff --git a/core/modules/block/custom_block/lib/Drupal/custom_block/Plugin/block/block/CustomBlock.php b/core/modules/block/custom_block/lib/Drupal/custom_block/Plugin/block/block/CustomBlock.php index 8b142ef..7a7bc00 100644 --- a/core/modules/block/custom_block/lib/Drupal/custom_block/Plugin/block/block/CustomBlock.php +++ b/core/modules/block/custom_block/lib/Drupal/custom_block/Plugin/block/block/CustomBlock.php @@ -1,6 +1,6 @@ '', + 'view_mode' => 'full' ); } @@ -47,8 +47,7 @@ public function getConfig() { $this->configuration = parent::getConfig(); $this->configuration['status'] = $definition['settings']['status']; $this->configuration['info'] = $definition['settings']['info']; - $this->configuration['body'] = $definition['settings']['body']; - $this->configuration['format'] = $definition['settings']['format']; + $this->configuration['view_mode'] = $definition['settings']['view_mode']; return $this->configuration; } @@ -58,25 +57,17 @@ public function getConfig() { * Adds body and description fields to the block configuration form. */ public function blockForm($form, &$form_state) { - // @todo Disable this field when editing an existing block and provide a - // separate interface for administering custom blocks. - $form['custom_block']['info'] = array( - '#type' => 'textfield', - '#title' => t('Block description'), - '#required' => TRUE, - '#default_value' => $this->configuration['info'], - '#description' => t('A brief description of your block. Used on the Blocks administration page. Changing this field will change the description for all copies of this block.', array('@overview' => url('admin/structure/block'))), - ); - // @todo Disable this field when editing an existing block and provide a - // separate interface for administering custom blocks. - $form['custom_block']['body'] = array( - '#type' => 'text_format', - '#title' => t('Block body'), - '#default_value' => $this->configuration['body'], - '#format' => isset($this->configuration['format']) ? $this->configuration['format'] : filter_default_format(), - '#description' => t('The content of the block as shown to the user. Changing this field will change the block body everywhere it is used.'), - '#rows' => 15, - '#required' => TRUE, + $view_modes = array(); + $info = entity_get_info('custom_block'); + foreach ($info['view_modes'] as $view_mode => $detail) { + $view_modes[$view_mode] = $detail['label']; + } + $form['custom_block']['view_mode'] = array( + '#type' => 'select', + '#options' => $view_modes, + '#title' => t('View mode'), + '#description' => t('Output the block in this view mode.'), + '#default_value' => $this->configuration['view_mode'] ); $form['custom_block']['title']['#description'] = t('The title of the block as shown to the user.'); return $form; @@ -86,15 +77,6 @@ public function blockForm($form, &$form_state) { * Overrides \Drupal\block\BlockBase::blockSubmit(). */ public function blockSubmit($form, &$form_state) { - list(, $bid) = explode(':', $this->getPluginId()); - $block = array( - 'info' => $form_state['values']['info'], - 'body' => $form_state['values']['body']['value'], - 'format' => $form_state['values']['body']['format'], - 'bid' => is_numeric($bid) ? $bid : NULL, - ); - drupal_write_record('block_custom', $block, !is_null($block['bid']) ? array('bid') : array()); - $this->configuration['id'] = 'custom_block:' . $block['bid']; // Invalidate the block cache to update custom block-based derivatives. if (module_exists('block')) { drupal_container()->get('plugin.manager.block')->clearCachedDefinitions(); @@ -105,12 +87,19 @@ public function blockSubmit($form, &$form_state) { * Implements \Drupal\block\BlockBase::blockBuild(). */ public function blockBuild() { - // Populate the block with the user-defined block body. - return array( - '#theme' => 'custom_block_block', - '#body' => $this->configuration['body'], - '#format' => $this->configuration['format'], - ); + list(, $machine_name) = explode(':', $this->getPluginId()); + if ($block = custom_block_machine_name_load($machine_name)) { + return entity_view($block, $this->configuration['view_mode']); + } + else { + return array( + '#markup' => t('Block with name %name does not exist, please create it.', array( + '%name' => $block_id, + '!url' => 'block/add' + )), + '#access' => user_access('administer blocks') + ); + } } } 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 index 260eb9e..f2f1226 100644 --- 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 @@ -64,7 +64,7 @@ public function form($form, &$form_state, $facet = NULL) { $rows = array(); foreach ($plugins as $plugin_id => $display_plugin_definition) { if (empty($facet) || $this->facetCompare($facet, $display_plugin_definition)) { - $rows[] = $this->row($plugin_id, $display_plugin_definition); + $rows[$plugin_id] = $this->row($plugin_id, $display_plugin_definition); } foreach ($plugin_definition['facets'] as $key => $title) { $facets[$key][$display_plugin_definition[$key]] = $this->facetLink($key, $plugin_id, $display_plugin_definition);