diff --git a/core/lib/Drupal/Core/Booze/ContentRouteEnhancer.php b/core/lib/Drupal/Core/Booze/ContentRouteEnhancer.php
new file mode 100644
index 0000000..cebd222
--- /dev/null
+++ b/core/lib/Drupal/Core/Booze/ContentRouteEnhancer.php
@@ -0,0 +1,46 @@
+resolver = $resolver;
+ }
+
+ public function enhance(array $defaults, Request $request) {
+ if (isset($defaults['_content']) && !isset($defaults['_content_closure'])) {
+ // Clone the request and pretend like _content is the _controller
+ $request = clone $request;
+ $request->attributes->set('_controller', $defaults['_content']);
+
+ $controller = $this->resolver->getController($request);
+ $arguments = $this->resolver->getArguments($request, $controller);
+
+ $defaults['_content_closure'] = function() use ($controller, $arguments) {
+ return call_user_func_array($controller, $arguments);
+ };
+ }
+
+ return $defaults;
+ }
+}
diff --git a/core/lib/Drupal/Core/Booze/DisplayRouteEnhancer.php b/core/lib/Drupal/Core/Booze/DisplayRouteEnhancer.php
new file mode 100644
index 0000000..0d4c5f5
--- /dev/null
+++ b/core/lib/Drupal/Core/Booze/DisplayRouteEnhancer.php
@@ -0,0 +1,41 @@
+getPath()));
+ }
+ $defaults['_display'] = $entity;
+ }
+
+ return $defaults;
+ }
+}
diff --git a/core/lib/Drupal/Core/Booze/DrunkController.php b/core/lib/Drupal/Core/Booze/DrunkController.php
new file mode 100644
index 0000000..4bd1cd3
--- /dev/null
+++ b/core/lib/Drupal/Core/Booze/DrunkController.php
@@ -0,0 +1,181 @@
+moduleHandler = $moduleHandler;
+ $this->entityManager = $entityManager;
+ }
+
+ /**
+ * Injects the service container used by this object.
+ *
+ * @param \Symfony\Component\DependencyInjection\ContainerInterface $container
+ * The service container this object should use.
+ */
+ public function setContainer(ContainerInterface $container = NULL) {
+ $this->container = $container;
+ }
+
+ /**
+ * Controller method for normal rendering of blocks and layouts-driven HTML
+ * page.
+ *
+ * @todo the output may need more or less assembling, depending on how
+ * 'compiled' of a template we have in the display. We either can handle it
+ * below here via a delegator pattern, or with sibling classes.
+ *
+ * @param \Symfony\Component\HttpFoundation\Request $request
+ * The request object.
+ * @param \Drupal\layout\Config\BoundDisplayInterface $_display
+ * A configuration object containing the layout instance and set of
+ * block instances that have been configured to be used for the current
+ * route.
+ *
+ * @return \Symfony\Component\HttpFoundation\Response
+ */
+ public function respond(Request $request, BoundDisplayInterface $_display) {
+ $this->display = $_display;
+
+ // Create a Response object right away that we can easily decorate as we go.
+ $this->response = new Response();
+
+ $this->renderBlocks($request);
+ $this->renderLayout($request);
+ // @todo handle out-of-band stuff, e.g. css/js
+
+ $content = $this->display->getLayoutPluginInstance()->renderLayout($this->renderedRegions);
+ $this->response->setContent($content);
+
+ return $this->response;
+ }
+
+ /**
+ * Renders all blocks contained in the current display object.
+ *
+ * @param \Symfony\Component\HttpFoundation\Request $request
+ */
+ protected function renderBlocks(Request $request) {
+ foreach ($this->display->getAllBlockInfo() as $name => $info) {
+ $instance = entity_load('block', $name);
+ $plugin = $instance->getPlugin();
+
+ // @todo this should be rolled into the more general pattern of context prep
+ if ($plugin instanceof SystemMainBlock) {
+ $instance->set('_content_closure', $request->attributes->get('_content_closure'));
+ }
+
+ // @todo block/pane styles - we're keeping them, right?
+ // @todo still need very much to handle context injection. yikes.
+ // @todo handle out-of-band attached stuff from blocks, e.g. css/js
+ if (empty($info['method']) || $info['method'] === 'direct') {
+ // this is the direct rendering approach.
+ // Should probably not be called like this and not keyed by name but instance - dysrama
+ $this->renderedBlocks[$name] = $this->entityManager
+ ->getRenderController($instance->entityType())
+ ->render($instance);
+ }
+ elseif ($info['method'] === 'subrequest') {
+ // this routes rendering through a subrequest.
+ // @todo When we have a Generator, we can replace the forward() call with
+ // a render() call, which would handle ESI and hInclude as well. That will
+ // require an _internal route. For examples, see:
+ // https://github.com/symfony/symfony/blob/master/src/Symfony/Bundle/FrameworkBundle/Resources/config/routing/internal.xml
+ // https://github.com/symfony/symfony/blob/master/src/Symfony/Bundle/FrameworkBundle/Controller/InternalController.php
+ $response = $this->container->get('http_kernel')->forward(array($instance, 'render'), $attributes, $request->query->all());
+ $this->renderedBlocks[$name] = $response->getContent();
+ }
+ }
+ }
+
+ /**
+ * Renders all rendered blocks into their respective layout regions, using
+ * the layout attached to the current display object.
+ *
+ * @param \Symfony\Component\HttpFoundation\Request $request
+ */
+ protected function renderLayout(Request $request) {
+ $layout = $this->display->getLayoutPluginInstance();
+ // @todo need to add this method to the layout plugin interface.
+ // @todo this is another place we could do block placement renegotiation by region role (@see Display)
+ foreach ($layout->getRegions() as $region => $info) {
+ // @todo region styles, if we do them, would go in around here
+ $to_render = array();
+ foreach ($this->display->getBlocksByRegion($region) as $block) {
+ $to_render[] = $this->renderedBlocks[$block];
+ }
+ $this->renderedRegions[$region] = $layout->renderRegion($region);
+ }
+ }
+}
diff --git a/core/lib/Drupal/Core/CoreBundle.php b/core/lib/Drupal/Core/CoreBundle.php
index 035f282..f060450 100644
--- a/core/lib/Drupal/Core/CoreBundle.php
+++ b/core/lib/Drupal/Core/CoreBundle.php
@@ -212,6 +212,11 @@ public function build(ContainerBuilder $container) {
$container->register('paramconverter.entity', 'Drupal\Core\ParamConverter\EntityConverter')
->addArgument(new Reference('plugin.manager.entity'))
->addTag('paramconverter');
+ $container->register('display_route_enhancer', 'Drupal\Core\Booze\DisplayRouteEnhancer')
+ ->addTag('route_enhancer');
+ $container->register('content_route_enhancer', 'Drupal\Core\Booze\ContentRouteEnhancer')
+ ->addArgument(new Reference('controller_resolver'))
+ ->addTag('route_enhancer');
$container->register('router_processor_subscriber', 'Drupal\Core\EventSubscriber\RouteProcessorSubscriber')
->addArgument(new Reference('content_negotiation'))
@@ -270,6 +275,14 @@ public function build(ContainerBuilder $container) {
$container->register('flood', 'Drupal\Core\Flood\DatabaseBackend')
->addArgument(new Reference('database'));
+ $container->register('drunk_controller_subscriber', 'Drupal\Core\EventSubscriber\DrunkControllerSubscriber')
+ ->addArgument(new Reference('content_negotiation'))
+ ->addTag('event_subscriber');
+
+ $container->register('drunk_controller', 'Drupal\Core\Booze\DrunkController')
+ ->addArgument(new Reference('module_handler'))
+ ->addArgument(new Reference('plugin.manager.entity'));
+
$container->addCompilerPass(new RegisterMatchersPass());
$container->addCompilerPass(new RegisterRouteFiltersPass());
// Add a compiler pass for registering event subscribers.
diff --git a/core/lib/Drupal/Core/EventSubscriber/DrunkControllerSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/DrunkControllerSubscriber.php
new file mode 100644
index 0000000..d44015d
--- /dev/null
+++ b/core/lib/Drupal/Core/EventSubscriber/DrunkControllerSubscriber.php
@@ -0,0 +1,70 @@
+getRequest();
+
+ // @todo implement a separate flag and remove this hardmapping
+ if ('drunk_controller:respond' !== $request->attributes->get('_controller')
+ || $request->attributes->get('_display') instanceof BoundDisplayInterface) {
+ return;
+ }
+
+ $route = $request->attributes->get('_route');
+
+ if (!empty($route)) {
+ $display = entity_load('display', $route);
+ }
+
+ if (empty($display)) {
+ throw new NotFoundHttpException(sprintf('No display could be located for the route %s.', $route));
+ return;
+ }
+
+ $display->setMainContent($request->attributes->get('_content'));
+
+ $request->attributes->set('_display', $display);
+ }
+
+ /**
+ * Registers the methods in this class that should be listeners.
+ *
+ * @return array
+ * An array of event listener definitions.
+ */
+ public static function getSubscribedEvents() {
+ $events[KernelEvents::CONTROLLER][] = array('onDrunkenKernelController', 100);
+
+ return $events;
+ }
+}
\ No newline at end of file
diff --git a/core/lib/Drupal/Core/EventSubscriber/RouteProcessorSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/RouteProcessorSubscriber.php
index 0061530..cb1862a 100644
--- a/core/lib/Drupal/Core/EventSubscriber/RouteProcessorSubscriber.php
+++ b/core/lib/Drupal/Core/EventSubscriber/RouteProcessorSubscriber.php
@@ -39,7 +39,7 @@ public function onRequestSetController(GetResponseEvent $event) {
$request = $event->getRequest();
if (!$request->attributes->has('_controller') && $this->negotiation->getContentType($request) === 'html') {
- $request->attributes->set('_controller', '\Drupal\Core\HtmlPageController::content');
+ $request->attributes->set('_controller', 'drunk_controller:respond');
}
}
diff --git a/core/modules/block/lib/Drupal/block/BlockRenderController.php b/core/modules/block/lib/Drupal/block/BlockRenderController.php
index e705310..cc751f2 100644
--- a/core/modules/block/lib/Drupal/block/BlockRenderController.php
+++ b/core/modules/block/lib/Drupal/block/BlockRenderController.php
@@ -87,4 +87,15 @@ public function viewMultiple(array $entities = array(), $view_mode = 'full', $la
return $build;
}
+ /**
+ * Use render instead of view
+ * @param EntityInterface $entity
+ * @param type $view_mode
+ * @param type $langcode
+ * @return type
+ */
+ public function render(EntityInterface $entity, $langcode = NULL) {
+ $render = $this->view($entity, 'block', $langcode);
+ return drupal_render($render);
+ }
}
diff --git a/core/modules/layout/layout.info b/core/modules/layout/layout.info
index 187bfea..4d5d23a 100644
--- a/core/modules/layout/layout.info
+++ b/core/modules/layout/layout.info
@@ -3,3 +3,4 @@ description = Makes it possible to swap different page layouts.
package = Core
version = VERSION
core = 8.x
+required = TRUE
diff --git a/core/modules/layout/lib/Drupal/layout/Config/UnboundDisplayStorageController.php b/core/modules/layout/lib/Drupal/layout/Config/UnboundDisplayStorageController.php
new file mode 100644
index 0000000..dc28ef3
--- /dev/null
+++ b/core/modules/layout/lib/Drupal/layout/Config/UnboundDisplayStorageController.php
@@ -0,0 +1,41 @@
+create($new_config->get());
+ $unbound_display->save();
+
+ // Now, create a corresponding bound display - if one doesn't exist already.
+ if (!entity_load('display', $name)) {
+ // @todo need finer-grained control than a single global default
+ // @todo how do we figure out whether we're doing frontend, admin, or...other?
+ $layout = drupal_container()->get('plugin.manager.layout')
+ ->createInstance(config('system.site')->get('default_layout'));
+ $bound_display = $unbound_display->generateDisplay($layout, $name);
+ $bound_display->save();
+ }
+
+ // @todo consider adding flexibility around which display type to create
+// $controller = drupal_container()
+// ->get('plugin.manager.entity')
+// ->getStorageController('display');
+
+ // $controller->create();
+ return TRUE;
+ }
+}
diff --git a/core/modules/layout/lib/Drupal/layout/Plugin/Core/Entity/UnboundDisplay.php b/core/modules/layout/lib/Drupal/layout/Plugin/Core/Entity/UnboundDisplay.php
index 5450e2c..c4bf8c2 100644
--- a/core/modules/layout/lib/Drupal/layout/Plugin/Core/Entity/UnboundDisplay.php
+++ b/core/modules/layout/lib/Drupal/layout/Plugin/Core/Entity/UnboundDisplay.php
@@ -24,7 +24,7 @@
* id = "unbound_display",
* label = @Translation("Unbound Display"),
* module = "layout",
- * controller_class = "Drupal\Core\Config\Entity\ConfigStorageController",
+ * controller_class = "Drupal\layout\Config\UnboundDisplayStorageController",
* config_prefix = "display.unbound",
* entity_keys = {
* "id" = "id",
@@ -40,6 +40,7 @@ class UnboundDisplay extends DisplayBase implements UnboundDisplayInterface {
* @throws \Exception
*/
public function generateDisplay(LayoutInterface $layout, $id, $entity_type = 'display') {
+ // @todo might be better to decouple this from the UnboundDisplay class
$values = array(
'layout' => $layout->getPluginId(),
'blockInfo' => $this->mapBlocksToLayout($layout),
diff --git a/core/modules/system/lib/Drupal/system/Plugin/block/block/SystemMainBlock.php b/core/modules/system/lib/Drupal/system/Plugin/block/block/SystemMainBlock.php
index 2f10f57..ea6a137 100644
--- a/core/modules/system/lib/Drupal/system/Plugin/block/block/SystemMainBlock.php
+++ b/core/modules/system/lib/Drupal/system/Plugin/block/block/SystemMainBlock.php
@@ -26,9 +26,13 @@ class SystemMainBlock extends BlockBase {
* Implements \Drupal\block\BlockBase::build().
*/
public function build() {
- return array(
- drupal_set_page_content()
- );
+ // @todo this lets the old way work transparently while we do new stuff.
+ if ($ret = drupal_set_page_content()) {
+ return array($ret);
+ }
+
+ $callback = $this->entity->get('_content_closure');
+ return call_user_func($callback);
}
}
diff --git a/core/profiles/standard/config/display.bound.frontend.yml b/core/profiles/standard/config/display.bound.frontend.yml
new file mode 100644
index 0000000..e1161b4
--- /dev/null
+++ b/core/profiles/standard/config/display.bound.frontend.yml
@@ -0,0 +1,35 @@
+id: frontend
+label: Frontend master display
+layout: static_layout:bartik__frontend
+layoutSettings: { }
+blockInfo:
+# these have bartik in their namespace, but that's irrelevant to our use here -
+# we're just reusing them while we get everything together.
+ block.block.bartik.help:
+ region: help
+ region-type: content
+ weight: 0 # should float in unbound/master
+ block.block.bartik.content:
+ region: content
+ region-type: content
+ weight: 0 # should float in unbound/master
+ block.block.bartik.footer:
+ region: footer
+ region-type: footer
+ weight: 0 # stick-top in unbound/master
+ block.block.bartik.powered:
+ region: footer
+ region-type: footer
+ weight: 10 # float in unbound/master
+ block.block.bartik.search:
+ region: sidebar_first
+ region-type: aside
+ weight: 0 # stick-top in unbound/master
+ block.block.bartik.login:
+ region: sidebar_first
+ region-type: aside
+ weight: 10 # stick-top in unbound/master
+ block.block.bartik.tools:
+ region: sidebar_first
+ region-type: aside
+ weight: 20 # stick-top in unbound/master
\ No newline at end of file
diff --git a/core/profiles/standard/standard.install b/core/profiles/standard/standard.install
index 85c818c..10f67cb 100644
--- a/core/profiles/standard/standard.install
+++ b/core/profiles/standard/standard.install
@@ -21,6 +21,8 @@ function standard_install() {
// Set front page to "node".
config('system.site')->set('page.front', 'node')->save();
+ // Set default layout to Bartik's frontend layout.
+ config('system.site')->set('default_layout', 'static_layout:bartik__frontend');
// Insert default pre-defined node types into the database. For a complete
// list of available node type attributes, refer to the node type API
diff --git a/core/themes/bartik/layouts/static/frontend/frontend.html.twig b/core/themes/bartik/layouts/static/frontend/frontend.html.twig
new file mode 100644
index 0000000..195e411
--- /dev/null
+++ b/core/themes/bartik/layouts/static/frontend/frontend.html.twig
@@ -0,0 +1,252 @@
+{#
+
+@file
+Bartik's primary frontend layout implementation.
+
+
+The doctype, html, head, and body tags are not in this template. Instead
+they can be found in the html.tpl.php template normally located in the
+core/modules/system directory.
+
+Available variables:
+
+General utility variables:
+- base_path: The base URL path of the Drupal installation. At the very
+ least, this will always default to /.
+- directory: The directory the template is located in, e.g. modules/system
+ or themes/bartik.
+- is_front: TRUE if the current page is the front page.
+- logged_in: TRUE if the user is registered and signed in.
+- is_admin: TRUE if the user has permission to access administration pages.
+
+Site identity:
+- front_page: The URL of the front page. Use this instead of base_path,
+ when linking to the front page. This includes the language domain or
+ prefix.
+- logo: The path to the logo image, as defined in theme configuration.
+- site_name: The name of the site, empty when display has been disabled
+ in theme settings.
+- site_slogan: The slogan of the site, empty when display has been disabled
+ in theme settings.
+- hide_site_name: TRUE if the site name has been toggled off on the theme
+ settings page. If hidden, the "element-invisible" class is added to make
+ the site name visually hidden, but still accessible.
+- hide_site_slogan: TRUE if the site slogan has been toggled off on the
+ theme settings page. If hidden, the "element-invisible" class is added to
+ make the site slogan visually hidden, but still accessible.
+
+Navigation:
+- main_menu (array): An array containing the Main menu links for the
+ site, if they have been configured.
+- secondary_menu (array): An array containing the Secondary menu links for
+ the site, if they have been configured.
+- breadcrumb: The breadcrumb trail for the current page.
+
+Page content (in order of occurrence in the default page.tpl.php):
+- title_prefix (array): An array containing additional output populated by
+ modules, intended to be displayed in front of the main title tag that
+ appears in the template.
+- title: The page title, for use in the actual HTML content.
+- title_suffix (array): An array containing additional output populated by
+ modules, intended to be displayed after the main title tag that appears in
+ the template.
+- messages: HTML for status and error messages. Should be displayed
+ prominently.
+- tabs (array): Tabs linking to any sub-pages beneath the current page
+ (e.g., the view and edit tabs when displaying a node).
+- action_links (array): Actions local to the page, such as 'Add menu' on
+ the menu administration interface.
+- feed_icons: A string of all feed icons for the current page.
+- node: The node entity, if there is an automatically-loaded node
+ associated with the page, and the node ID is the second argument
+ in the page's path (e.g. node/12345 and node/12345/revisions, but not
+ comment/reply/12345).
+
+Regions:
+- page.header: Items for the header region.
+- page.featured: Items for the featured region.
+- page.highlighted: Items for the highlighted content region.
+- page.help: Dynamic help text, mostly for admin pages.
+- page.content: The main content of the current page.
+- page.sidebar_first: Items for the first sidebar.
+- page.sidebar_second: Items for the second sidebar.
+- page.triptych_first: Items for the first triptych.
+- page.triptych_middle: Items for the middle triptych.
+- page.triptych_last: Items for the last triptych.
+- page.footer_firstcolumn: Items for the first footer column.
+- page.footer_secondcolumn: Items for the second footer column.
+- page.footer_thirdcolumn: Items for the third footer column.
+- page.footer_fourthcolumn: Items for the fourth footer column.
+- page.footer: Items for the footer region.
+
+@see template_preprocess()
+@see template_preprocess_page()
+@see template_process()
+@see bartik_process_page()
+@see html.tpl.php
+
+@ingroup themeable
+
+#}
+
+{# system is emitted before & above everything else #}
+{% if page.system %}{{ page.system }}{% endif %}
+
+
+ {% if page.header %}
+
+ {# @todo remove/replace this hardcoding. #}
+ {% if secondary_nav %}
+
+ {% endif %}
+
+ {# @todo remove/replace this hardcoding. #}
+ {% if logo %}
+
+
+
+ {% endif %}
+
+ {# @todo remove/replace aaaaall this hardcoding. #}
+ {% if site_name or site_slogan %}
+