diff --git a/core/modules/book/book.module b/core/modules/book/book.module index 1fe04c8..948ec1a 100644 --- a/core/modules/book/book.module +++ b/core/modules/book/book.module @@ -185,14 +185,6 @@ function book_menu() { 'route_name' => 'book_render', 'type' => MENU_SUGGESTED_ITEM, ); - $items['book/export/%/%node'] = array( - 'page callback' => 'book_export', - 'page arguments' => array(2, 3), - 'access callback' => 'book_export_access', - 'access arguments' => array(3), - 'type' => MENU_CALLBACK, - 'file' => 'book.pages.inc', - ); $items['node/%node/outline'] = array( 'title' => 'Outline', 'page callback' => 'book_outline', @@ -216,16 +208,6 @@ function book_menu() { } /** - * Access callback: Determines if the book export page is accessible. - * - * @param \Drupal\node\Plugin\Core\Entity\EntityInterface $node - * The node whose export page is to be viewed. - */ -function book_export_access(EntityInterface $node) { - return user_access('access printer-friendly version') && node_access('view', $node); -} - -/** * Access callback: Determines if the outline tab is accessible. * * Path: @@ -1133,45 +1115,6 @@ function template_preprocess_book_export_html(&$variables) { } /** - * Traverses the book tree to build printable or exportable output. - * - * During the traversal, the $visit_func() callback is applied to each node and - * is called recursively for each child of the node (in weight, title order). - * - * @param $tree - * A subtree of the book menu hierarchy, rooted at the current page. - * @param $visit_func - * A function callback to be called upon visiting a node in the tree. - * - * @return - * The output generated in visiting each node. - */ -function book_export_traverse($tree, $visit_func) { - $output = ''; - - foreach ($tree as $data) { - // Note- access checking is already performed when building the tree. - if ($node = node_load($data['link']['nid'])) { - $children = ''; - - if ($data['below']) { - $children = book_export_traverse($data['below'], $visit_func); - } - - if (!empty($visit_func)) { - $output .= call_user_func($visit_func, $node, $children); - } - else { - // Use the default function. - $output .= book_node_export($node, $children); - } - } - } - - return $output; -} - -/** * Generates printer-friendly HTML for a node. * * @param \Drupal\Core\Entity\EntityInterface $node diff --git a/core/modules/book/book.pages.inc b/core/modules/book/book.pages.inc index bc5ec53..2ba5a15 100644 --- a/core/modules/book/book.pages.inc +++ b/core/modules/book/book.pages.inc @@ -10,81 +10,6 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; /** - * Page callback: Generates representations of a book page and its children. - * - * The function delegates the generation of output to helper functions. The - * function name is derived by prepending 'book_export_' to the given output - * type. So, e.g., a type of 'html' results in a call to the function - * book_export_html(). - * - * @param $type - * A string encoding the type of output requested. The following types are - * currently supported in book module: - * - html: Printer-friendly HTML. - * Other types may be supported in contributed modules. - * @param \Drupal\node\Plugin\Core\Entity\EntityInterface $node - * The node to export. - * - * @return - * A string representing the node and its children in the book hierarchy in a - * format determined by the $type parameter. - * - * @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException - * - * @see book_menu() - */ -function book_export($type, EntityInterface $node) { - $type = drupal_strtolower($type); - - $export_function = 'book_export_' . $type; - - if (function_exists($export_function)) { - print call_user_func($export_function, $node); - } - else { - drupal_set_message(t('Unknown export format.')); - throw new NotFoundHttpException(); - } -} - -/** - * Generates HTML for export when invoked by book_export(). - * - * The given node is embedded to its absolute depth in a top level section. For - * example, a child node with depth 2 in the hierarchy is contained in - * (otherwise empty)
elements corresponding to depth 0 and depth 1. - * This is intended to support WYSIWYG output - e.g., level 3 sections always - * look like level 3 sections, no matter their depth relative to the node - * selected to be exported as printer-friendly HTML. - * - * @param \Drupal\node\Plugin\Core\Entity\Node - * The node to export. - * - * @return - * A string containing HTML representing the node and its children in - * the book hierarchy. - * - * @throws \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException - * @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException - */ -function book_export_html(EntityInterface $node) { - if (user_access('access printer-friendly version')) { - if (isset($node->book)) { - $tree = book_menu_subtree_data($node->book); - $contents = book_export_traverse($tree, 'book_node_export'); - $book_exported_html = array('#theme' => 'book_export_html', '#title' => $node->label(), '#contents' => $contents, '#depth' => $node->book['depth']); - return drupal_render($book_exported_html); - } - else { - throw new NotFoundHttpException(); - } - } - else { - throw new AccessDeniedHttpException(); - } -} - -/** * Page callback: Shows the outline form for a single node. * * @param \Drupal\Core\Entity\EntityInterface $node diff --git a/core/modules/book/book.routing.yml b/core/modules/book/book.routing.yml index 6c8b010..ac3cfd3 100644 --- a/core/modules/book/book.routing.yml +++ b/core/modules/book/book.routing.yml @@ -18,3 +18,10 @@ book_settings: _form: 'Drupal\book\BookSettingsForm' requirements: _permission: 'administer site configuration' + +book_export: + pattern: '/book/export/{type}/{node}' + defaults: + _content: '\Drupal\book\Controller\BookController::bookExport' + requirements: + _book_export_access: 'TRUE' diff --git a/core/modules/book/book.services.yml b/core/modules/book/book.services.yml index 9d8c140..dd7c545 100644 --- a/core/modules/book/book.services.yml +++ b/core/modules/book/book.services.yml @@ -2,3 +2,7 @@ services: book.manager: class: Drupal\book\BookManager arguments: ['@database', '@plugin.manager.entity'] + access_check.book_export: + class: Drupal\book\Access\BookExport + tags: + - { name: access_check } diff --git a/core/modules/book/lib/Drupal/book/Access/BookExport.php b/core/modules/book/lib/Drupal/book/Access/BookExport.php new file mode 100644 index 0000000..60040cc --- /dev/null +++ b/core/modules/book/lib/Drupal/book/Access/BookExport.php @@ -0,0 +1,34 @@ +getRequirements()); + } + + /** + * {@inheritdoc} + */ + public function access(Route $route, Request $request) { + return user_access('access printer-friendly version') && node_access('view', $request->attributes->get('node')); + } + +} diff --git a/core/modules/book/lib/Drupal/book/Controller/BookController.php b/core/modules/book/lib/Drupal/book/Controller/BookController.php index 7b10eaf..870349a 100644 --- a/core/modules/book/lib/Drupal/book/Controller/BookController.php +++ b/core/modules/book/lib/Drupal/book/Controller/BookController.php @@ -8,8 +8,14 @@ use Drupal\Core\Controller\ControllerInterface; use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; +use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; +use Drupal\Component\Utility\Unicode; +use Drupal\node\NodeInterface; use Drupal\book\BookManager; +use Drupal\node\Plugin\Core\Entity\Node; /** * Controller routines for book routes. @@ -86,4 +92,118 @@ public function bookRender() { return drupal_render($item_list); } + /** + * Generates representations of a book page and its children. + * + * The function delegates the generation of output to helper functions. The + * function name is derived by prepending 'book_export_' to the given output + * type. So, e.g., a type of 'html' results in a call to the function + * book_export_html(). + * + * @param string $type + * A string encoding the type of output requested. The following types are + * currently supported in book module: + * - html: Printer-friendly HTML. + * Other types may be supported in contributed modules. + * @param \Drupal\node\NodeInterface $node + * The node to export. + * + * @return \Symfony\Component\HttpFoundation\Response + * A string representing the node and its children in the book hierarchy in a + * format determined by the $type parameter. + * + * @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException + */ + public function bookExport($type, NodeInterface $node) { + $type = Unicode::strtolower($type); + + // @todo Conver the custom export functionality to serializer. + $method = 'bookExport' . $type; + if (method_exists($this, $method)) { + return new Response($this->{$method}($node)); + } + else { + drupal_set_message(t('Unknown export format.')); + throw new NotFoundHttpException(); + } + } + + /** + * Generates HTML for export when invoked by book_export(). + * + * The given node is embedded to its absolute depth in a top level section. For + * example, a child node with depth 2 in the hierarchy is contained in + * (otherwise empty)
elements corresponding to depth 0 and depth 1. + * This is intended to support WYSIWYG output - e.g., level 3 sections always + * look like level 3 sections, no matter their depth relative to the node + * selected to be exported as printer-friendly HTML. + * + * @param \Drupal\node\NodeInterface $node + * The node to export. + * + * @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException + * Thrown when the node was not attached to a book. + * @throws \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException + * Thrown when the user does not have access to export a book. + * + * @internal param $ \Drupal\node\Plugin\Core\Entity\Node + * The node to export. + * + * @return string A string containing HTML representing the node and its children in + */ + public function bookExportHtml(NodeInterface $node) { + if (user_access('access printer-friendly version')) { + if (isset($node->book)) { + $tree = book_menu_subtree_data($node->book); + $contents = $this->exportTraverse($tree, 'book_node_export'); + return theme('book_export_html', array('title' => $node->label(), 'contents' => $contents, 'depth' => $node->book['depth'])); + } + else { + throw new NotFoundHttpException(); + } + } + else { + throw new AccessDeniedHttpException(); + } + } + + /** + * Traverses the book tree to build printable or exportable output. + * + * During the traversal, the $visit_func() callback is applied to each node and + * is called recursively for each child of the node (in weight, title order). + * + * @param array $tree + * A subtree of the book menu hierarchy, rooted at the current page. + * @param string $visit_func + * A function callback to be called upon visiting a node in the tree. + * + * @return string + * The output generated in visiting each node. + */ + protected function exportTraverse($tree, $visit_func) { + $output = ''; + + foreach ($tree as $data) { + // Note- access checking is already performed when building the tree. + if ($node = node_load($data['link']['nid'])) { + $children = ''; + + if ($data['below']) { + $children = $this->exportTraverse($data['below'], $visit_func); + } + + if (!empty($visit_func)) { + $output .= call_user_func($visit_func, $node, $children); + } + else { + // Use the default function. + $output .= book_node_export($node, $children); + } + } + } + + return $output; + } + }