Commit 991faf1d authored by webchick's avatar webchick

Issue #1938314 by kgoel, mparker17, Kars-T, disasm, tim.plunkett,...

Issue #1938314 by kgoel, mparker17, Kars-T, disasm, tim.plunkett, daffodil_naveen, ygerasimov, jibran, dawehner, wamilton, Letharion | Crell: Convert book_export() to a new-style Controller.
parent 0a2ca3cc
......@@ -187,14 +187,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',
......@@ -217,16 +209,6 @@ function book_menu() {
return $items;
}
/**
* Access callback: Determines if the book export page is accessible.
*
* @param \Drupal\node\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.
*
......@@ -1144,68 +1126,6 @@ function template_preprocess_book_export_html(&$variables) {
$variables['html_attributes'] = new Attribute($attributes);
}
/**
* 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
* The node that will be output.
* @param $children
* (optional) All the rendered child nodes within the current node. Defaults
* to an empty string.
*
* @return
* The HTML generated for the given node.
*
* @see book_export_traverse()
*/
function book_node_export(EntityInterface $node, $children = '') {
$build = node_view($node, 'print');
unset($build['#theme']);
// @todo Rendering should happen in the template using render().
$node->rendered = drupal_render($build);
$book_node_export_html = array('#theme' => 'book_node_export_html', '#node' => $node, '#children' => $children );
return drupal_render($book_node_export_html);
}
/**
* Prepares variables for single node export templates.
*
......
......@@ -9,81 +9,6 @@
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
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\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) <div> 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\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.
*
......
......@@ -18,3 +18,13 @@ book_settings:
_form: 'Drupal\book\Form\BookSettingsForm'
requirements:
_permission: 'administer site configuration'
book_export:
pattern: '/book/export/{type}/{node}'
defaults:
_controller: '\Drupal\book\Controller\BookController::bookExport'
options:
_access_mode: 'ALL'
requirements:
_permission: 'access printer-friendly version'
_entity_access: 'node.view'
......@@ -2,3 +2,6 @@ services:
book.manager:
class: Drupal\book\BookManager
arguments: ['@database', '@entity.manager']
book.export:
class: Drupal\book\BookExport
arguments: ['@entity.manager']
<?php
/**
* @file
* Contains \Drupal\book\BookExport.
*/
namespace Drupal\book;
use Drupal\Core\Entity\EntityManager;
use Drupal\node\NodeInterface;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
/**
* Provides methods for exporting book to different formats.
*
* If you would like to add another format, swap this class in container.
*/
class BookExport {
/**
* The node storage controller.
*
* @var \Drupal\Core\Entity\EntityStorageControllerInterface
*/
protected $nodeStorage;
/**
* The node render controller.
*
* @var \Drupal\Core\Entity\EntityRenderControllerInterface
*/
protected $nodeRender;
/**
* Constructs a BookExport object.
*
* @param \Drupal\Core\Entity\EntityManager $entityManager
* The entity manager.
*/
public function __construct(EntityManager $entityManager) {
$this->nodeStorage = $entityManager->getStorageController('node');
$this->nodeRender = $entityManager->getRenderController('node');
}
/**
* 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) <div> 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 \Exception
* Thrown when the node was not attached to a book.
*
* @return array
* A render array representing the HTML for a node and its children in the
* book hierarchy.
*/
public function bookExportHtml(NodeInterface $node) {
if (!isset($node->book)) {
throw new \Exception();
}
$tree = book_menu_subtree_data($node->book);
$contents = $this->exportTraverse($tree, array($this, 'bookNodeExport'));
return array(
'#theme' => 'book_export_html',
'#title' => $node->label(),
'#contents' => $contents,
'#depth' => $node->book['depth'],
);
}
/**
* Traverses the book tree to build printable or exportable output.
*
* During the traversal, the 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 callable $callable
* A callback to be called upon visiting a node in the tree.
*
* @return string
* The output generated in visiting each node.
*/
protected function exportTraverse(array $tree, $callable) {
// If there is no valid callable, use the default callback.
$callable = !empty($callable) ? $callable : array($this, 'bookNodeExport');
$output = '';
foreach ($tree as $data) {
// Note- access checking is already performed when building the tree.
if ($node = $this->nodeStorage->load($data['link']['nid'])) {
$children = $data['below'] ? $this->exportTraverse($data['below'], $callable) : '';
$callable_output = call_user_func($callable, $node, $children);
$output .= drupal_render($callable_output);
}
}
return $output;
}
/**
* Generates printer-friendly HTML for a node.
*
* @param \Drupal\node\NodeInterface $node
* The node that will be output.
* @param string $children
* (optional) All the rendered child nodes within the current node. Defaults
* to an empty string.
*
* @return array
* A render array for the exported HTML of a given node.
*
* @see \Drupal\book\BookExport::exportTraverse()
*/
protected function bookNodeExport(NodeInterface $node, $children = '') {
$build = $this->nodeRender->view($node, 'print', NULL);
unset($build['#theme']);
// @todo Rendering should happen in the template using render().
$node->rendered = drupal_render($build);
return array(
'#theme' => 'book_node_export_html',
'#node' => $node,
'#children' => $children,
);
}
}
<?php
/**
* @file
* Contains \Drupal\book\Controller\BookController.
......@@ -6,10 +7,14 @@
namespace Drupal\book\Controller;
use Drupal\book\BookManager;
use Drupal\book\BookExport;
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
use Drupal\node\NodeInterface;
use Symfony\Component\DependencyInjection\Container;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\book\BookManager;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
/**
* Controller routines for book routes.
......@@ -17,24 +22,40 @@
class BookController implements ContainerInjectionInterface {
/**
* Book Manager Service.
* The book manager.
*
* @var \Drupal\book\BookManager
*/
protected $bookManager;
/**
* Injects BookManager Service.
* The book export service.
*
* @var \Drupal\book\BookExport
*/
public static function create(ContainerInterface $container) {
return new static($container->get('book.manager'));
}
protected $bookExport;
/**
* Constructs a BookController object.
*
* @param \Drupal\book\BookManager $bookManager
* The book manager.
* @param \Drupal\book\BookExport $bookExport
* The book export service.
*/
public function __construct(BookManager $bookManager) {
public function __construct(BookManager $bookManager, BookExport $bookExport) {
$this->bookManager = $bookManager;
$this->bookExport = $bookExport;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('book.manager'),
$container->get('book.export')
);
}
/**
......@@ -48,7 +69,6 @@ public function adminOverview() {
$rows = array();
$headers = array(t('Book'), t('Operations'));
// Add any recognized books to the table list.
foreach ($this->bookManager->getAllBooks() as $book) {
$row = array(
......@@ -67,8 +87,12 @@ public function adminOverview() {
);
$rows[] = $row;
}
$table = array('#theme' => 'table', '#header' => $headers, '#rows' => $rows, '#empty' => t('No books available.'));
return drupal_render($table);
return array(
'#theme' => 'table',
'#header' => $headers,
'#rows' => $rows,
'#empty' => t('No books available.'),
);
}
/**
......@@ -82,8 +106,45 @@ public function bookRender() {
foreach ($this->bookManager->getAllBooks() as $book) {
$book_list[] = l($book['title'], $book['href'], $book['options']);
}
$item_list = array('#theme' => 'item_list' , '#items' => $book_list);
return drupal_render($item_list);
return array(
'#theme' => 'item_list',
'#items' => $book_list,
);
}
/**
* Generates representations of a book page and its children.
*
* The method delegates the generation of output to helper methods. The method
* name is derived by prepending 'bookExport' to the camelized form of given
* output type. For example, a type of 'html' results in a call to the method
* bookExportHtml().
*
* @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 array
* A render array 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) {
$method = 'bookExport' . Container::camelize($type);
// @todo Convert the custom export functionality to serializer.
if (!method_exists($this->bookExport, $method)) {
drupal_set_message(t('Unknown export format.'));
throw new NotFoundHttpException();
}
$exported_book = $this->bookExport->{$method}($node);
return new Response(drupal_render($exported_book));
}
}
......@@ -11,6 +11,8 @@
use Drupal\Core\Entity\EntityStorageControllerInterface;
use Drupal\Core\Entity\Annotation\EntityType;
use Drupal\Core\Annotation\Translation;
use Drupal\Core\Language\Language;
use Drupal\Core\Session\AccountInterface;
use Drupal\node\NodeInterface;
/**
......@@ -137,6 +139,40 @@ public function getType() {
return $this->bundle();
}
/**
* {@inheritdoc}
*/
public function access($operation = 'view', AccountInterface $account = NULL) {
if ($operation == 'create') {
return parent::access($operation, $account);
}
return \Drupal::entityManager()
->getAccessController($this->entityType)
->access($this, $operation, $this->prepareLangcode(), $account);
}
/**
* {@inheritdoc}
*/
public function prepareLangcode() {
$langcode = $this->language()->id;
// If the Language module is enabled, try to use the language from content
// negotiation.
if (\Drupal::moduleHandler()->moduleExists('language')) {
// Load languages the node exists in.
$node_translations = $this->getTranslationLanguages();
// Load the language from content negotiation.
$content_negotiation_langcode = \Drupal::languageManager()->getLanguage(Language::TYPE_CONTENT)->id;
// If there is a translation available, use it.
if (isset($node_translations[$content_negotiation_langcode])) {
$langcode = $content_negotiation_langcode;
}
}
return $langcode;
}
/**
* {@inheritdoc}
*/
......
......@@ -187,4 +187,12 @@ public function getRevisionAuthor();
*/
public function setRevisionAuthorId($uid);
/**
* Prepares the langcode for a node.
*
* @return string
* The langcode for this node.
*/
public function prepareLangcode();
}
......@@ -2006,19 +2006,7 @@ function node_access($op, $node, $account = NULL, $langcode = NULL) {
// If no language code was provided, default to the node's langcode.
if (empty($langcode)) {
$langcode = $node->language()->id;
// If the Language module is enabled, try to use the language from content
// negotiation.
if (module_exists('language')) {
// Load languages the node exists in.
$node_translations = $node->getTranslationLanguages();
// Load the language from content negotiation.
$content_negotiation_langcode = language(Language::TYPE_CONTENT)->id;
// If there is a translation available, use it.
if (isset($node_translations[$content_negotiation_langcode])) {
$langcode = $content_negotiation_langcode;
}
}
$langcode = $node->prepareLangcode();
}
return $access_controller->access($node, $op, $langcode, $account);
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment