diff --git a/core/modules/book/book.module b/core/modules/book/book.module index 48f8c5d..cdc87b6 100644 --- a/core/modules/book/book.module +++ b/core/modules/book/book.module @@ -113,6 +113,13 @@ function book_permission() { } /** + * Implements hook_entity_info(). + */ +function book_entity_info(&$entity_info) { + $entity_info['node']['controllers']['form']['book_outline'] = '\Drupal\book\Form\BookOutlineForm'; +} + +/** * Adds relevant book links to the node's links. * * @param \Drupal\Core\Entity\EntityInterface $node @@ -288,19 +295,18 @@ function book_get_books() { * @see book_pick_book_nojs_submit() */ function book_form_node_form_alter(&$form, &$form_state, $form_id) { - // Get BookManager service - $book_manager = Drupal::service('book.manager'); - + $user = Drupal::currentUser(); $node = $form_state['controller']->getEntity(); - $access = user_access('administer book outlines'); + $access = $user->hasPermission('administer book outlines'); if (!$access) { - if (user_access('add content to books') && ((!empty($node->book['mlid']) && !$node->isNew()) || book_type_is_allowed($node->getType()))) { + if ($user->hasPermission('add content to books') && ((!empty($node->book['mlid']) && !$node->isNew()) || book_type_is_allowed($node->getType()))) { // Already in the book hierarchy, or this node type is allowed. $access = TRUE; } } + if ($access) { - $book_manager->addFormElements($form, $form_state, $node); + $form = Drupal::service('book.manager')->addFormElements($form, $form_state, $node, $user); // Since the "Book" dropdown can't trigger a form submission when // JavaScript is disabled, add a submit button to do that. book.admin.css hides // this button when JavaScript is enabled. @@ -327,15 +333,6 @@ function book_node_builder($entity_type, $entity, &$form, &$form_state) { } /** - * Implements hook_form_BASE_FORM_ID_alter() for the book_outline form. - */ -function book_form_book_outline_alter(&$form, &$form_state, $form_id) { - // Dynamically set title - $node = reset($form_state['build_info']['args']); - drupal_set_title($node->label()); -} - -/** * Form submission handler for node_form(). * * This handler is run when JavaScript is disabled. It triggers the form to diff --git a/core/modules/book/book.pages.inc b/core/modules/book/book.pages.inc index c9682f2..c059633 100644 --- a/core/modules/book/book.pages.inc +++ b/core/modules/book/book.pages.inc @@ -85,17 +85,6 @@ function book_export_html(EntityInterface $node) { } /** - * Form submission handler for book_outline_form(). - * - * Redirects to removal confirmation form. - * - * @see \Drupal\book\Form\BookOutlineForm::buildForm() - */ -function book_remove_button_submit($form, &$form_state) { - $form_state['redirect'] = 'node/' . $form['#node']->id() . '/outline/remove'; -} - -/** * Form constructor to confirm removal of a node from a book. * * @param \Drupal\Core\Entity\EntityInterface $node diff --git a/core/modules/book/book.routing.yml b/core/modules/book/book.routing.yml index b0aed37..414daf5 100644 --- a/core/modules/book/book.routing.yml +++ b/core/modules/book/book.routing.yml @@ -22,6 +22,9 @@ book_settings: book_outline: pattern: '/node/{node}/outline' defaults: - _form: '\Drupal\book\Form\BookOutlineForm' + _entity_form: 'node.book_outline' + options: + _access_mode: 'ALL' requirements: _permission: 'administer book outlines' + _entity_access: 'node.view' diff --git a/core/modules/book/book.services.yml b/core/modules/book/book.services.yml index 9d8c140..a5f9d5a 100644 --- a/core/modules/book/book.services.yml +++ b/core/modules/book/book.services.yml @@ -1,4 +1,4 @@ services: book.manager: class: Drupal\book\BookManager - arguments: ['@database', '@plugin.manager.entity'] + arguments: ['@database', '@plugin.manager.entity', '@string_translation'] diff --git a/core/modules/book/lib/Drupal/book/BookManager.php b/core/modules/book/lib/Drupal/book/BookManager.php index 8d2c982..32b5916 100644 --- a/core/modules/book/lib/Drupal/book/BookManager.php +++ b/core/modules/book/lib/Drupal/book/BookManager.php @@ -9,6 +9,9 @@ use Drupal\Core\Database\Connection; use Drupal\Core\Entity\EntityManager; use Drupal\Core\Entity\EntityInterface; +use Drupal\Core\Session\AccountInterface; +use Drupal\Core\StringTranslation\TranslationInterface; +use Drupal\node\NodeInterface; use Symfony\Component\DependencyInjection\ContainerInterface; /** @@ -31,6 +34,13 @@ class BookManager { protected $entityManager; /** + * The translation service. + * + * @var \Drupal\Core\StringTranslation\TranslationInterface + */ + protected $translation; + + /** * Books Array. * * @var array @@ -40,9 +50,10 @@ class BookManager { /** * Constructs a BookManager object. */ - public function __construct(Connection $database, EntityManager $entityManager) { + public function __construct(Connection $database, EntityManager $entityManager, TranslationInterface $translation) { $this->database = $database; $this->entityManager = $entityManager; + $this->translation = $translation; } /** @@ -98,17 +109,26 @@ protected function loadBooks() { /** * Returns an array with default values for a book page's menu link. * - * @param $nid + * @param string|int $nid * The ID of the node whose menu link is being created. * - * @return + * @return array * The default values for the menu link. */ public function linkDefaults($nid) { - return array('original_bid' => 0, 'menu_name' => '', 'nid' => $nid, - 'bid' => 0, 'router_path' => 'node/%', 'plid' => 0, 'mlid' => 0, - 'has_children' => 0, 'weight' => 0, 'module' => 'book', - 'options' => array()); + return array( + 'original_bid' => 0, + 'menu_name' => '', + 'nid' => $nid, + 'bid' => 0, + 'router_path' => 'node/%', + 'plid' => 0, + 'mlid' => 0, + 'has_children' => 0, + 'weight' => 0, + 'module' => 'book', + 'options' => array(), + ); } /** @@ -117,20 +137,29 @@ public function linkDefaults($nid) { * @param $book_link * A fully loaded menu link that is part of the book hierarchy. * - * @return + * @return int * The depth limit for items in the parent select. */ public function parentDepthLimit($book_link) { - return MENU_MAX_DEPTH - 1 - (($book_link['mlid'] && $book_link['has_children']) ? entity_get_controller('menu_link')->findChildrenRelativeDepth($book_link) : 0); + return MENU_MAX_DEPTH - 1 - (($book_link['mlid'] && $book_link['has_children']) ? $this->entityManager->getStorageController('menu_link')->findChildrenRelativeDepth($book_link) : 0); } /** * Builds the common elements of the book form for the node and outline forms. * - * @param \Drupal\Core\Entity\EntityInterface $node + * @param array $form + * An associative array containing the structure of the form. + * @param array $form_state + * An associative array containing the current state of the form. + * @param \Drupal\node\NodeInterface $node * The node whose form is being viewed. + * @param \Drupal\Core\Session\AccountInterface $account + * The account viewing the form. + * + * @return array + * The form structure, with the book elements added. */ - public function addFormElements(&$form, &$form_state, EntityInterface $node) { + public function addFormElements(array $form, array &$form_state, NodeInterface $node, AccountInterface $account) { // If the form is being processed during the Ajax callback of our book bid // dropdown, then $form_state will hold the value that was selected. if (isset($form_state['values']['book'])) { @@ -138,7 +167,7 @@ public function addFormElements(&$form, &$form_state, EntityInterface $node) { } $form['book'] = array( '#type' => 'details', - '#title' => t('Book outline'), + '#title' => $this->t('Book outline'), '#weight' => 10, '#collapsed' => TRUE, '#group' => 'advanced', @@ -162,11 +191,11 @@ public function addFormElements(&$form, &$form_state, EntityInterface $node) { // @see _book_admin_table_tree(). The weight may be larger than 15. $form['book']['weight'] = array( '#type' => 'weight', - '#title' => t('Weight'), + '#title' => $this->t('Weight'), '#default_value' => $node->book['weight'], '#delta' => max(15, abs($node->book['weight'])), '#weight' => 5, - '#description' => t('Pages at a given level are ordered first by weight and then by title.'), + '#description' => $this->t('Pages at a given level are ordered first by weight and then by title.'), ); $options = array(); $nid = !$node->isNew() ? $node->id() : 'new'; @@ -175,28 +204,28 @@ public function addFormElements(&$form, &$form_state, EntityInterface $node) { $options[$node->id()] = $node->label(); } else { - foreach (book_get_books() as $book) { + foreach ($this->getAllBooks() as $book) { $options[$book['nid']] = $book['title']; } } - if (user_access('create new books') && ($nid == 'new' || ($nid != $node->book['original_bid']))) { + if ($account->hasPermission('create new books') && ($nid == 'new' || ($nid != $node->book['original_bid']))) { // The node can become a new book, if it is not one already. - $options = array($nid => t('- Create a new book -')) + $options; + $options = array($nid => $this->t('- Create a new book -')) + $options; } if (!$node->book['mlid']) { // The node is not currently in the hierarchy. - $options = array(0 => t('- None -')) + $options; + $options = array(0 => $this->t('- None -')) + $options; } // Add a drop-down to select the destination book. $form['book']['bid'] = array( '#type' => 'select', - '#title' => t('Book'), + '#title' => $this->t('Book'), '#default_value' => $node->book['bid'], '#options' => $options, '#access' => (bool) $options, - '#description' => t('Your page will be a part of the selected book.'), + '#description' => $this->t('Your page will be a part of the selected book.'), '#weight' => -5, '#attributes' => array('class' => array('book-title-select')), '#ajax' => array( @@ -206,6 +235,7 @@ public function addFormElements(&$form, &$form_state, EntityInterface $node) { 'speed' => 'fast', ), ); + return $form; } /** @@ -214,10 +244,53 @@ public function addFormElements(&$form, &$form_state, EntityInterface $node) { * A node can be removed from a book if it is actually in a book and it either * is not a top-level page or is a top-level page with no children. * - * @param \Drupal\Core\Entity\EntityInterface $node + * @param \Drupal\node\NodeInterface $node * The node to remove from the outline. + * + * @return bool + * TRUE if a node can be removed from the book, FALSE otherwise. */ - public function nodeIsRemovable(EntityInterface $node) { + public function nodeIsRemovable(NodeInterface $node) { return (!empty($node->book['bid']) && (($node->book['bid'] != $node->id()) || !$node->book['has_children'])); } + + /** + * Handles additions and updates to the book outline. + * + * This common helper function performs all additions and updates to the book + * outline through node addition, node editing, node deletion, or the outline + * tab. + * + * @param \Drupal\node\NodeInterface $node + * The node that is being saved, added, deleted, or moved. + * + * @return bool + * TRUE if the menu link was saved; FALSE otherwise. + */ + public function updateBookOutline(NodeInterface $node) { + return _book_update_outline($node); + } + + /** + * Translates a string to the current language or to a given language. + * + * @param string $string + * A string containing the English string to translate. + * @param array $args + * An associative array of replacements to make after translation. Based + * on the first character of the key, the value is escaped and/or themed. + * See \Drupal\Core\Utility\String::format() for details. + * @param array $options + * An associative array of additional options, with the following elements: + * - 'langcode': The language code to translate to a language other than + * what is used to display the page. + * - 'context': The context the source string belongs to. + * + * @return string + * The translated string. + */ + protected function t($string, array $args = array(), array $options = array()) { + return $this->translation->translate($string, $args, $options); + } + } diff --git a/core/modules/book/lib/Drupal/book/Form/BookOutlineForm.php b/core/modules/book/lib/Drupal/book/Form/BookOutlineForm.php index fbfd6be..a1092db 100644 --- a/core/modules/book/lib/Drupal/book/Form/BookOutlineForm.php +++ b/core/modules/book/lib/Drupal/book/Form/BookOutlineForm.php @@ -2,25 +2,31 @@ /** * @file - * Contains \Drupal\book\Form\BasicSettingsForm. + * Contains \Drupal\book\Form\BookOutlineForm. */ namespace Drupal\book\Form; -use Drupal\Core\ControllerInterface; -use Drupal\Core\Form\FormInterface; -use Symfony\Component\DependencyInjection\ContainerInterface; +use Drupal\Core\Entity\EntityFormControllerNG; use Drupal\book\BookManager; -use Drupal\Core\Entity\EntityBCDecorator; +use Symfony\Component\DependencyInjection\ContainerInterface; /** * Displays the book outline form. */ -class BookOutlineForm implements ControllerInterface, FormInterface { +class BookOutlineForm extends EntityFormControllerNG { /** - * BookManager service - * @var Drupal\book\BookManager + * The book being displayed. + * + * @var \Drupal\node\NodeInterface + */ + protected $entity; + + /** + * BookManager service. + * + * @var \Drupal\book\BookManager */ protected $bookManager; @@ -42,45 +48,31 @@ public static function create(ContainerInterface $container) { } /** - * Implements \Drupal\Core\Form\FormInterface::getFormID(). + * {@inheritdoc} */ - public function getFormID() { - return 'book_outline'; + public function getBaseFormID() { + return FALSE; } /** * {@inheritdoc} */ - public function buildForm(array $form, array &$form_state, EntityBCDecorator $node = NULL) { - if (!isset($node->book)) { + public function form(array $form, array &$form_state) { + $form['#title'] = $this->entity->label(); + + if (!isset($this->entity->book)) { // The node is not part of any book yet - set default options. - $node->book = $this->bookManager->linkDefaults($node->id()); + $this->entity->book = $this->bookManager->linkDefaults($this->entity->id()); } else { - $node->book['original_bid'] = $node->book['bid']; + $this->entity->book['original_bid'] = $this->entity->book['bid']; } // Find the depth limit for the parent select. - if (!isset($node->book['parent_depth_limit'])) { - $node->book['parent_depth_limit'] = $this->bookManager->parentDepthLimit($node->book); + if (!isset($this->entity->book['parent_depth_limit'])) { + $this->entity->book['parent_depth_limit'] = $this->bookManager->parentDepthLimit($this->entity->book); } - $form['#node'] = $node; - $form['#id'] = 'book-outline'; - $this->bookManager->addFormElements($form, $form_state, $node); - - $form['update'] = array( - '#type' => 'submit', - '#value' => $node->book['original_bid'] ? t('Update book outline') : t('Add to book outline'), - '#weight' => 15, - ); - - $form['remove'] = array( - '#type' => 'submit', - '#value' => t('Remove from book outline'), - '#access' => $this->bookManager->nodeIsRemovable($node), - '#weight' => 20, - '#submit' => array('book_remove_button_submit'), - ); + $form = $this->bookManager->addFormElements($form, $form_state, $this->entity, $this->getCurrentUser()); return $form; } @@ -88,37 +80,49 @@ public function buildForm(array $form, array &$form_state, EntityBCDecorator $no /** * {@inheritdoc} */ - public function validateForm(array &$form, array &$form_state) {} + protected function actions(array $form, array &$form_state) { + $actions = parent::actions($form, $form_state); + $actions['submit']['#value'] = $this->entity->book['original_bid'] ? $this->t('Update book outline') : $this->t('Add to book outline'); + $actions['delete']['#value'] = $this->t('Remove from book outline'); + $actions['delete']['#access'] = $this->bookManager->nodeIsRemovable($this->entity); + return $actions; + } /** * {@inheritdoc} * * @see book_remove_button_submit() */ - public function submitForm(array &$form, array &$form_state) { - $node = $form['#node']; - $form_state['redirect'] = "node/" . $node->id(); + public function submit(array $form, array &$form_state) { + $form_state['redirect'] = 'node/' . $this->entity->id(); $book_link = $form_state['values']['book']; if (!$book_link['bid']) { - drupal_set_message(t('No changes were made')); - + drupal_set_message($this->t('No changes were made')); return; } $book_link['menu_name'] = book_menu_name($book_link['bid']); - $node->book = $book_link; - if (_book_update_outline($node)) { - if ($node->book['parent_mismatch']) { + $this->entity->book = $book_link; + if ($this->bookManager->updateBookOutline($this->entity)) { + if ($this->entity->book['parent_mismatch']) { // This will usually only happen when JS is disabled. - drupal_set_message(t('The post has been added to the selected book. You may now position it relative to other pages.')); - $form_state['redirect'] = "node/" . $node->id() . "/outline"; + drupal_set_message($this->t('The post has been added to the selected book. You may now position it relative to other pages.')); + $form_state['redirect'] = 'node/' . $this->entity->id() . '/outline'; } else { - drupal_set_message(t('The book outline has been updated.')); + drupal_set_message($this->t('The book outline has been updated.')); } } else { - drupal_set_message(t('There was an error adding the post to the book.'), 'error'); + drupal_set_message($this->t('There was an error adding the post to the book.'), 'error'); } } + + /** + * {@inheritdoc} + */ + public function delete(array $form, array &$form_state) { + $form_state['redirect'] = 'node/' . $this->entity->id() . '/outline/remove'; + } + }