Problem

  1. The preprocessing and rendering for the maintenance-page and install-page templates requires tons of ugly hacks and workarounds, because the two pages are not wrapped into the usual html template.

Proposed solution

  1. Remove that special casing and wrap the page output into the html template as usual.

Comments

sun’s picture

Status: Active » Needs review
StatusFileSize
new20.11 KB

My latest & greatest trick to further simplify the early installer environment:

    // Various services need a database connection. Supply one.
    if (!$install_state['database_verified']) {
      Database::addConnectionInfo('install', 'default', array(
        'driver' => 'sqlite',
        'database' => ':memory:',
      ));
    }

:-)

Status: Needs review » Needs work

The last submitted patch, 1: theme.maintenance.1.patch, failed testing.

sun’s picture

StatusFileSize
new289.71 KB

I... have no idea how this patch manages to do this, but it only occurs with Standard profile:

sun’s picture

StatusFileSize
new20.56 KB
new1.2 KB

This backtrace clarifies it:

#0  Drupal\user\Form\UserLoginForm->__construct() called at [\core\modules\user\lib\Drupal\user\Form\UserLoginForm.php:67]
#1  Drupal\user\Form\UserLoginForm::create() called at [\core\lib\Drupal\Core\Form\FormBuilder.php:156]
#2  Drupal\Core\Form\FormBuilder->getFormId() called at [\core\lib\Drupal\Core\Form\FormBuilder.php:187]
#3  Drupal\Core\Form\FormBuilder->getForm()
#4  call_user_func_array() called at [\core\includes\form.inc:116]
#5  drupal_get_form() called at [\core\modules\user\lib\Drupal\user\Plugin\Block\UserLoginBlock.php:35]
#6  Drupal\user\Plugin\Block\UserLoginBlock->build() called at [\core\modules\block\lib\Drupal\block\BlockViewBuilder.php:47]
#7  Drupal\block\BlockViewBuilder->viewMultiple() called at [\core\modules\block\lib\Drupal\block\BlockViewBuilder.php:31]
#8  Drupal\block\BlockViewBuilder->view() called at [\core\includes\entity.inc:466]
#9  entity_view() called at [\core\modules\block\block.module:203]
#10 _block_get_renderable_region() called at [\core\modules\block\block.module:172]
#11 block_get_blocks_by_region() called at [\core\modules\block\block.module:126]
#12 block_page_build() called at [\core\includes\common.inc:3648]
#13 drupal_prepare_page() called at [\core\includes\install.core.inc:945]
#14 install_display_output() called at [\core\includes\install.core.inc:135]
#15 install_drupal() called at [\core\install.php:33]

block_page_build() kicks in, because Block module is installed by Standard profile.

This patch removes all the special-casing for the install page, so hook_page_build() of Block module is actually executed as soon as it is installed! :-)

And block.block.seven_login.yml contains:

region: content

So we have these options:

  1. Intentionally cripple the installer environment to force only system + user modules to be active.
  2. Don't invoke hook_page_build() in the installer.
  3. Move block.block.seven_login.yml into a different region.
  4. Don't allow the install profile to ship with content region blocks in the theme that is used in the installer.

3) + 4) are plain silly, so only 1) + 2) are sensible options.

My favorite would be (1), but I already guess that the module list is actively used for much more when modules are installed (e.g., to discover/install config for other modules).

But let's try that first:

Intentionally cripple the installer environment to system + ?user + ?profile modules.

sun’s picture

Status: Needs work » Needs review

Status: Needs review » Needs work

The last submitted patch, 4: theme.maintenance.4.patch, failed testing.

sun’s picture

Status: Needs work » Needs review
StatusFileSize
new20.4 KB
new1.86 KB

OK, that ends with:

Fatal error: Uncaught exception 'Doctrine\Common\Annotations\AnnotationException'
with message '[Semantical Error] Couldn't find constant COMMENT_MODE_THREADED, class Drupal\comment\Plugin\Field\FieldType\CommentItem.'
in \core\vendor\doctrine\annotations\lib\Doctrine\Common\Annotations\AnnotationException.php on line 52

Thus, only option (2) remains. Which is essentially the same as HEAD.

However, HEAD bakes that special case into template_preprocess_maintenance_page(), which is invoked at the very end of the page rendering chain and thus that doesn't work with this patch. Instead, we need to cover the special case directly in drupal_prepare_page().

  1. Revert "Intentionally cripple the installer environment to system + ?user + ?profile modules."
  2. Only invoke hook_page_build() of System module in MAINTENANCE_MODE.

Status: Needs review » Needs work

The last submitted patch, 7: theme.maintenance.7.patch, failed testing.

sun’s picture

Status: Needs work » Needs review

But now... I'm unable to reproduce that installation failure reported by the testbot.

Installation with Standard profile works perfectly fine for me.

Another incident of #1808220: Remove run-tests.sh dependency on existing/installed parent site ?

dawehner’s picture

StatusFileSize
new26.51 KB
new8.19 KB

Manually tried to reproduce the failure. Both manual install, run-tests and drush si worked fine.

Just a random guess: do the testbot have sqlite installed?

  1. +++ b/core/includes/common.inc
    @@ -3635,9 +3635,21 @@ function drupal_prepare_page($page) {
    -  foreach (\Drupal::moduleHandler()->getImplementations('page_build') as $module) {
    +  if (!defined('MAINTENANCE_MODE')) {
    +    $implementations = \Drupal::moduleHandler()->getImplementations('page_build');
    +  }
    +  else {
    +    $implementations = array('system');
    +  }
    
    +++ b/core/includes/install.core.inc
    @@ -402,9 +412,11 @@ function install_begin_request(&$install_state) {
    +    system_register('module', 'system', $module_list['system']);
    ...
    +    system_register('profile', $profile, $module_list[$profile]);
    

    As far as I understand these reason for changes is that we want to "boot" up the system module. In a normal drupal we could use the DrupalKernel which would register the module as well as adapt the classloader.
    So tryed to use it, though this opens all kind of problems. How did that worked before? Afaik previously system_page_build was not fired so that it never needed its classes? What do you think about checking in _drupal_render_process_post_render_cache that we actually can call the render_cache callback? In an ideal world something like render cache would not fire at all during the installer.

  2. +++ b/core/includes/install.core.inc
    @@ -349,6 +351,14 @@ function install_begin_request(&$install_state) {
    +
    +    // Various services need a database connection. Supply one.
    +    if (!$install_state['database_verified']) {
    +      Database::addConnectionInfo('install', 'default', array(
    +        'driver' => 'sqlite',
    +        'database' => ':memory:',
    +      ));
    +    }
    

    Do we already have a dependecy on sqlite somewhere else? This feels like a pretty big additional tbh.

  3. +++ b/core/includes/install.core.inc
    @@ -913,20 +934,20 @@ function install_display_output($output, $install_state) {
    +
    +  print \Drupal::service('html_page_renderer')->render($page);
       exit;
    

    Let's construct at least a proper response object now.

    Additional I realized that the drupal_get_header() call is the last remaining one, so it would be pretty nice to already add it to the response object,
    which we will use later as well. Question: Is this actually needed in the installer? This code would not be needed if FinishResponseSubscriber could fire.

Status: Needs review » Needs work

The last submitted patch, 10: theme.maintenance.10.patch, failed testing.

tstoeckler’s picture

Would a solution to this problem be making the installer design a separate theme alltogether? I've never understood why it is officially the 'seven' theme even though it looks nothing like the Seven theme in the actual admin interface.

sun’s picture

StatusFileSize
new25.16 KB
new9.9 KB

@tstoeckler: Nope, forcing all installation profiles to ship with a separate theme for the installer would be crazy. ;-) Typically the installer appearance should follow the overall experience and just loads some additional asset libraries.

@dawehner:

  1. I was surprised to see that we're not actually registering the classloader namespaces for the minimal bootstrap modules in the early installer environment. We did not notice that lack yet, because the early installer pages are solely driven by callbacks in install.core.inc only thus far. — Yes, normally DrupalKernel takes care of registering the modules, but we are manually overriding the module list. — In light of this, I wonder whether these manual module list overrides aren't architecturally broken... IMHO, such code should call a method on DrupalKernel, instead of manually futzing with ModuleHandler. → Speaking of, all we're missing is a call to DrupalKernel::updateModules().
  2. The change to _drupal_render_process_post_render_cache() is debatable, since assigning non-existing callback should normally blow up... Therefore, I'd like to revert that. It's also a bit out of scope, as we've fixed the classloader registration already.
  3. Regardless of the classloader issue, it looks like we simply cannot invoke hook_page_build() for the maintenance page and install page. Therefore, my suggestion is to simply wrap the invocation into if (!defined('MAINTENANCE_MODE')).
  4. The SQLite memory database workaround was meant as a temporary trick for now, since I wanted to focus on the actual meat, instead of resolving silly container dependencies. ;) I'm not sure whether that can or will stay. The testbots definitely have pdo_sqlite. But perhaps this is why the testbot fails to install...
  5. I really appreciate the attempt to modernize the installer some more by returning proper responses! However, I'd like to omit/revert those changes in this patch, since I'm trying very hard to limit the scope as much as possible — the current patch contains a lot of major changes to the install system and theme system already, and as we can see, it is hard enough to make just those work... — There's nothing wrong with the additionally proposed changes, but let's do them in a follow-up issue to this one here, pretty please. :-)

Let's see how this performs:

  1. Properly update the module list on DrupalKernel.
  2. Correction: Properly boot the kernel with the appropriate module list.
  3. Do not invoke hook_page_build() in MAINTENANCE_MODE.
  4. Replaced SQLite workaround with MAINTENANCE_MODE conditions in template_preprocess_page().

Interdiff is against #7.

sun’s picture

Status: Needs work » Needs review

Status: Needs review » Needs work

The last submitted patch, 13: theme.maintenance.13.patch, failed testing.

sun’s picture

Status: Needs work » Needs review
StatusFileSize
new28.28 KB
new9.73 KB
  1. Updated maintenance page templates accordingly.
  2. Reverted error/exception handler override for installer.
  3. Cleanups to minimize changeset.

Status: Needs review » Needs work

The last submitted patch, 16: theme.maintenance.16.patch, failed testing.

sun’s picture

Status: Needs work » Needs review
StatusFileSize
new25.44 KB
new7.86 KB

Alright... let's further strip this down until we're able to pinpoint the testbot installation problem.

(btw, the recent testbot runs were based on PIFR 2.x and thus did not use drush site-install but manually walked through the interactive installer instead.)

  1. Reverted all changes to install_begin_request().
  2. Change template_preprocess_page() + drupal_prepare_page() to not call into menu.inc functions in MAINTENANCE_MODE.

Status: Needs review » Needs work

The last submitted patch, 18: theme.maintenance.18.patch, failed testing.

sun’s picture

Status: Needs work » Needs review
Issue tags: +Sunrise Sanity Cruise

At least we have test results now :-)

sun’s picture

StatusFileSize
new37.01 KB
new14.58 KB

This is ugly, but the current legacy code paths do not leave much room for a cleaner alternative right now:

Added *temporary* DefaultHtmlPageRenderer::renderPage() helper, in order to move forward.

Status: Needs review » Needs work

The last submitted patch, 21: theme.maintenance.21.patch, failed testing.

dawehner’s picture

It would be cool if you could explain with a few words why there is no alternative.

sun’s picture

Title: Render the maintenance/install page render like any other HTML page » Render the maintenance/install page like any other HTML page
Status: Needs work » Needs review
StatusFileSize
new36.62 KB
new1.49 KB

re #23: The maintenance page is rendered by all of the special front controllers (install.php, update.php, authorize.php, etc), which are not converted to a kernel + request/response architecture at all yet. — Converting (rewriting) those is out of scope here, so we need a temporary helper function that allows to render a maintenance/install page.

But even that is a step forward; centralizing all those special cases to call a single helper function will help us to convert those special front controllers.


  1. Fixed bogus $theme parameter in install_display_output().
  2. WTF: menu_local_tabs() returns no tabs unless menu_get_local_actions() is called upfront.

Status: Needs review » Needs work

The last submitted patch, 24: theme.maintenance.24.patch, failed testing.

sun’s picture

Status: Needs work » Needs review
StatusFileSize
new46.26 KB
new9.65 KB
  1. Attempt to update RenderElementTypesTest::testMaintenancePage().
  2. Removed obsolete maintenance page test introduced by #2072647: #theme 'maintenance_page' should support render arrays in #content

Status: Needs review » Needs work

The last submitted patch, 26: theme.maintenance.26.patch, failed testing.

sun’s picture

Status: Needs work » Needs review
StatusFileSize
new46.2 KB

Merged 8.x.

Status: Needs review » Needs work

The last submitted patch, 28: theme.maintenance.28.patch, failed testing.

mgifford’s picture

StatusFileSize
new172.62 KB
new36.38 KB

The output looks good. Here are some screenshots from SimplyTest.me:

Install Page with Focus

Maintenance Page with Focus

Eventually we should remove the Skip links on the Maintenance Page, but that should be in a different issue.

sun’s picture

Status: Needs work » Needs review
Related issues: +#318975: Remove confirmation page after installation
StatusFileSize
new46.45 KB
new574 bytes

Resolving the last test failures:

Fixed bogus variable name in update_check_requirements().

Not going to fix the InstallerTranslationTest failures, because those test assertions will be removed entirely with #318975: Remove confirmation page after installation, which is RTBC already.


The only remaining @todo that is also added in code by this patch is whether we want to try to rename the templates as part of this patch?

maintenance_page.html.twig → page-maintenance.html.twig
install_page.html.twig     → page-install.html.twig

I'd prefer to do that in a separate follow-up issue, but could be convinced to do it here if someone comes up with a good reason for doing so.

Status: Needs review » Needs work

The last submitted patch, 31: theme.maintenance.31.patch, failed testing.

sun’s picture

31: theme.maintenance.31.patch queued for re-testing.

sun’s picture

Status: Needs work » Needs review

Yay! :-)

mgifford’s picture

What testing should be done before this is marked RTBC? Glad the tests are in place.

sun’s picture

Since the new code mostly goes through the regular HTML page rendering pipeline, no special testing is required for this patch.

In fact, this patch removes a test for #theme maintenance_page, as mentioned in #26, because that test is pointless now.

From my perspective, this patch only needs technical reviews, because the UI is not changed at all (aside from adding the standard "Skip to content" link that we discussed already.)


The changes to drupal_prepare_page() could potentially be reverted, because that function is not invoked anymore. At the same time, that function only seems to get invoked from very similar edge-cases (e.g. BatchController), so we might as well keep those changes (and/or just don't care).

hook_page_build() is intentionally not invoked from the new helper method. I already considered to document that, but how do you document that some non-existing code is not executed, because you intentionally did not include/call the code? :P — j/k, I'll add some.

In any case, the new helper method needs reviews, and also, I'm not happy about the amount of added !defined('MAINTENANCE_MODE') conditions, although I don't think we can do much about the latter.

joelpittet’s picture

Status: Needs review » Needs work
Issue tags: +Needs reroll

This is looking awesome! Nice work @sun.

page--maintenance.html.twig and page--install.html.twig as normal suggestions would be my suggestion suggestion;) Though with theme suggestions it would have been nice to have their respective preprocess functions override the default template_preprocess_page in this case to avoid the !defined('MAINTENANCE_MODE'), though I don't think that's possible...

Needs a small re-roll on some bartik/seven maintenance page tweaks that have been commited.

sun’s picture

Status: Needs work » Needs review
Issue tags: -Needs reroll
StatusFileSize
new47.04 KB
new878 bytes

Updated for new .maintenance-background HTML class in Seven theme.

sun’s picture

StatusFileSize
new46.04 KB
new6.75 KB
  1. Reverted changes to drupal_prepare_page().
  2. Added docs to DefaultHtmlPageRenderer::renderPage().
sun’s picture

39: theme.maintenance.39.patch queued for re-testing.

sun’s picture

StatusFileSize
new46.04 KB

Merged 8.x.

jessebeach’s picture

Status: Needs review » Reviewed & tested by the community

I've manually tested maintenance pages and install pages in standard and minimal profiles. No issues.

The code looks much better than what we have now for maintenance page and install page rendering. This patch doesn't completely eliminate their special flower status, but it moves us in the right direction.

I had reservations introducing \Drupal\Core\Page\DefaultHtmlPageRenderer::renderPage(). It's highly specific to maintenance and install page rendering. But then, these two pages are special flowers already and this is an intermediate step to integrate these pages into a normal page render flow. The method is marked as @internal and the docblock warns devs not to use it.

I've gone through the patch and created issues for all the @todos. None are blocking or Major/Critical. This is follow-on work we can do at leisure.

Follow up issues

#2231853: Rename maintenance-page template into page--maintenance + install-page into page--maintenance--install
#2231857: Rename install-page.html.twig to page--install.html.twig

sun’s picture

Priority: Normal » Major

As this happens to affect the (critical) #1067408: Themes do not have an installation status, I guess we should at least bump this to major?

alexpott’s picture

Status: Reviewed & tested by the community » Fixed

Committed 10b0389 and pushed to 8.x. Thanks!

I was debating the need for a change notice - afaics we don't need one yet - the followups will though.

diff --git a/core/themes/bartik/bartik.theme b/core/themes/bartik/bartik.theme
index b290f6e..414f5af 100644
--- a/core/themes/bartik/bartik.theme
+++ b/core/themes/bartik/bartik.theme
@@ -5,7 +5,6 @@
  * Functions to support theming in the Bartik theme.
  */
 
-use Drupal\Core\Template\RenderWrapper;
 use Drupal\Core\Template\Attribute;
 
 /**
diff --git a/core/themes/seven/seven.theme b/core/themes/seven/seven.theme
index 44ac0bd..4aa6e5a 100644
--- a/core/themes/seven/seven.theme
+++ b/core/themes/seven/seven.theme
@@ -5,7 +5,6 @@
  * Functions to support theming in the Seven theme.
  */
 
-use Drupal\Core\Template\RenderWrapper;
 use Drupal\Component\Utility\String;
 
 /**

Removed unused uses during commit

  • Commit 10b0389 on 8.x by alexpott:
    Issue #2218039 by sun, dawehner: Render the maintenance/install page...
tim.plunkett’s picture

Title: Render the maintenance/install page like any other HTML page » REGRESSION: Render the maintenance/install page like any other HTML page
Category: Task » Bug report
Priority: Major » Critical
Status: Fixed » Active

rdf_preprocess_html() calls rdf_get_namespaces() which invokes hooks. Hitting /update.php with rdf.module turned on will throw exceptions.

Before this patch, it was completely reasonable for a module to invoke hooks in hook_preprocess_html/hook_preprocess_page, with this patch it is not.

sun’s picture

There are two conflicting expectations:

  1. hook_preprocess* is invoked for a page.
  2. On the update.php page, all hooks except for an explicit whitelist are not allowed.

→ If any hook_preprocess implementation calls any hook that isn't allowed, update.php blows up.

hook_preprocess* itself is allowed. But rdf_preprocess_html() tries to invoke hook_rdf_namespaces(). Thus, when visiting update.php when RDF module is enabled, then the exception is triggered.

Due to the conflicting expectations, I don't really have a proposal for how to resolve this.

As a matter of fact, it's pure coincidence that this patch revealed that problem. The same code path can be triggered through plenty of other mechanisms; e.g., #pre_render/#process/#after_build callbacks on form elements (that happen to be used on the update.php page), and most likely a lot more call chains. Just invoke a hook that happens to be not allowed in any of these, ka-boom.

We can't easily prevent the theme system from invoking preprocess functions in modules, because the "offline theme registry" (formerly used on the maintenance page) was removed a long time ago.

I'm aware why that hook limitation for update.php was introduced, but I'm not sure whether it is still necessary...?

IMO, this regression is a completely separate problem space — in essence, this change here is not flawed; it's the change that introduced that hook limitation that is flawed; because as we can see, the idea does not work out.

jessebeach’s picture

Issue tags: +beta blocker

This is back to being a beta blocker.

  • Commit 4f4b457 on 8.x by webchick:
    Revert "Issue #2218039 by sun, dawehner: Render the maintenance/install...
webchick’s picture

Status: Active » Needs work
Issue tags: -beta blocker +Needs followup

Flawed assumption or not, we can't sit around with broken update.php. Therefore, I've rolled this back for now until we can come to a resolution.

We also need to open a critical for test coverage of this code path, because that should never have happened.

alexpott’s picture

Issue tags: +beta blocker, +Needs tests

Tagging...

alexpott’s picture

Issue tags: -beta blocker

So I added the beta blocker tag because I thought it had been removed by accident. I was wrong. At most this is a soft blocker for #1067408: Themes do not have an installation status

xjm’s picture

I'd suggest we make the test coverage blocking for this issue, not a followup.

sun’s picture

Title: REGRESSION: Render the maintenance/install page like any other HTML page » Render the maintenance/install page like any other HTML page
Category: Bug report » Task
Priority: Critical » Major
Status: Needs work » Needs review
Issue tags: -Needs followup, -Needs tests

This patch only revealed a problem with the current update.php environment. The changes of this patch itself are fine on their own; there's nothing this patch could do to fix a problem that it didn't introduce. But yes, of course, we want update.php to work :-)

Created #2233403: Let update.php invoke hooks again

sun’s picture

41: theme.maintenance.41.patch queued for re-testing.

Status: Needs review » Needs work

The last submitted patch, 41: theme.maintenance.41.patch, failed testing.

sun’s picture

Status: Needs work » Needs review
StatusFileSize
new49.36 KB
new4.79 KB

#2233403: Let update.php invoke hooks again is resolved.

  1. Merged 8.x.
  2. Updated for changed update_task_list().
dawehner’s picture

Status: Needs review » Reviewed & tested by the community
+++ b/core/lib/Drupal/Core/Page/DefaultHtmlPageRenderer.php
@@ -23,4 +23,87 @@ public function render(HtmlPage $page) {
+    return \Drupal::service('html_page_renderer')->render($page);

This is quite a WTF but okay as this function is just temporary.

webchick’s picture

Status: Reviewed & tested by the community » Fixed

Ok, cool. Let's try this again.

Committed and pushed to 8.x. Thanks!

sun’s picture

Status: Fixed » Reviewed & tested by the community

Doesn't appear to have been committed/pushed.

webchick’s picture

Status: Reviewed & tested by the community » Fixed

Sorry about that! Try now. :)

  • Commit 1521f98 on 8.x by webchick:
    Issue #2218039 by dawehner, sun: Render the maintenance/install page...

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.

wim leers’s picture