Updated: Comment #N

Problem/Motivation

It is possible to 'enable' a non-existing theme like

    \Drupal::config('system.theme')
        ->set('default', 'non_existing')
        ->save();

This was found through writing test for #2084907: hook_views_pre_render() is broken if your theme implements a base theme when not having the test_theme module enabled for testing thus no system alteration happened.

Proposed resolution

This should throw an exception.

Or otherwise we should add \Drupal\Core\Extension\ThemeHandlerInterface::setDefault($theme)

Remaining tasks

  1. Define a better @throw \Exceptions: It's way to broad. Should be kinda ThemeDoesNotExists or something
  2. Fix AjaxBasePageNegotiator makes $configFactory obsolete so this needs a DIC change I guess. Does it?
  3. Both the get | set for $ grep -ir "get('system.theme'" * needs a checkup for get|setDefault() calls. Done through followup #2151469: Clean-up usage of deprecated list_themes() and _system_rebuild_theme_data() in favor of theme_handler service

User interface changes

API changes

Comments

clemens.tolboom’s picture

Status: Active » Needs review
StatusFileSize
new3.24 KB

Attached patch adds get|setDefault() with 'validation'

It is still possible to set to a non-existing theme through the system config so needs more work.

Needs tests.

Let's see what testbot thinks first.

dawehner’s picture

  1. +++ b/core/lib/Drupal/Core/Extension/ThemeHandler.php
    @@ -109,6 +109,18 @@ class ThemeHandler implements ThemeHandlerInterface {
     
    +    public function getDefault() {
    ...
    +    public function setDefault($theme) {
    ...
    +
    

    Let's put a {@inheritdoc} here.

  2. +++ b/core/lib/Drupal/Core/Extension/ThemeHandler.php
    @@ -109,6 +109,18 @@ class ThemeHandler implements ThemeHandlerInterface {
    +      $this->configFactory->get('system.theme')->set('default', $theme);
    

    We certainly should also save the config :)

  3. +++ b/core/lib/Drupal/Core/Extension/ThemeHandler.php
    @@ -158,7 +174,11 @@ class ThemeHandler implements ThemeHandlerInterface {
    +        throw new Exception("Theme $key does not exists.");
    

    Please extend the documentation of disable() to include the @throws as well.

  4. +++ b/core/lib/Drupal/Core/Extension/ThemeHandlerInterface.php
    @@ -13,6 +13,18 @@
    +   * @throws Exception
    +   */
    

    It would be kind of cool to know when in which case the Exception is thrown.

  5. +++ b/core/lib/Drupal/Core/Extension/ThemeHandler.php
    @@ -147,7 +163,7 @@ class ThemeHandler implements ThemeHandlerInterface {
    -    if ($pos = array_search($this->configFactory->get('system.theme')->get('default'), $theme_list) !== FALSE) {
    +    if ($pos = array_search($this->getDefault(), $theme_list) !== FALSE) {
    

    nice!

Status: Needs review » Needs work

The last submitted patch, 1: core-theme-default-2167347-1.patch, failed testing.

clemens.tolboom’s picture

Assigned: Unassigned » clemens.tolboom

@dawehner thanks for the quick review ... I'll work on that.

+++ b/core/lib/Drupal/Core/Extension/ThemeHandler.php
@@ -109,6 +109,18 @@ class ThemeHandler implements ThemeHandlerInterface {
+        throw new Exception("Theme $key does not exists.");

Patch failed in finding Exception ... should be \Exception I guess

Happened around

ModuleHandler functionality 1 pass, 0 fails, and 0 exceptions
PHP Fatal error:  Class 'Drupal\Core\Extension\Exception' not found in /var/lib/drupaltestbot/sites/default/files/checkout/core/lib/Drupal/Core/Extension/ThemeHandler.php on line 141
Confirm form helper test 5 passes, 0 fails, and 0 exceptions
clemens.tolboom’s picture

Assigned: clemens.tolboom » Unassigned
Issue summary: View changes
Status: Needs work » Needs review
StatusFileSize
new3.14 KB
new4.24 KB

I've added some comments from #4 but

  1. I've trouble in what to do exactly with the @throw \Exceptions: It's way to broad. Should be kinda ThemeDoesNotExists or something
  2. Changing AjaxBasePageNegotiator makes $configFactory obsolete so this needs a DIC change I guess. Does it?
  3. Both the get | set for $ grep -ir "get('system.theme'" * needs a checkup.

Back to the bot.

Status: Needs review » Needs work

The last submitted patch, 5: core-theme-default-2167347-5.patch, failed testing.

clemens.tolboom’s picture

Status: Needs work » Needs review
StatusFileSize
new6.64 KB

Argh ... Fixed Exception attempt #2

Should one use

use \Exception

Scanning code I find no core usage. Scanning for new Exception( and new \Exception(I find both.

Status: Needs review » Needs work

The last submitted patch, 7: core-theme-default-2167347-7.patch, failed testing.

clemens.tolboom’s picture

Failures are

Drupal\Tests\Core\Extension\ThemeHandlerTest->testThemeEnableWithTooLongName()

It has no assertions!
It now needs a try catch? Does it?

Drupal\Tests\Core\Extension\ThemeHandlerTest->testEnableSingleTheme()

Hmmm ... it's a UnitTestCase ... how can that enable `theme_test' module? Weird.

Drupal\Tests\Core\Extension\ThemeHandlerTest->testEnableAndListInfo()

Tries to enable bartik. Same as the above.

dawehner’s picture

It has no assertions!

A LOT of unit tests don't have ... you work with mocks to check that it works.
In this example it uses the following annotations:

   * @expectedException \Drupal\Core\Extension\ExtensionNameLengthException
   * @expectedExceptionMessage Theme name <em class="placeholder">thisNameIsFarTooLong0000000000000000000000000000051</em> is over the maximum allowed length of 50 characters.

this does basically what you think as try/catch.

Hmmm ... it's a UnitTestCase ... how can that enable `theme_test' module? Weird.

You don't ...

Have a look at

    $this->themeHandler->enable(array('bartik'));
    $this->themeHandler->systemList['bartik'] = (object) array(

... switching this two code blocks should just work.

  1. +++ b/core/lib/Drupal/Core/Theme/AjaxBasePageNegotiator.php
    @@ -81,7 +81,7 @@ class AjaxBasePageNegotiator implements ThemeNegotiatorInterface {
    diff --git a/core/modules/system/system.module b/core/modules/system/system.module
    
    diff --git a/core/modules/system/system.module b/core/modules/system/system.module
    index f9ae056..83cc650 100644
    
    index f9ae056..83cc650 100644
    --- a/core/modules/system/system.module
    
    --- a/core/modules/system/system.module
    +++ b/core/modules/system/system.module
    
    +++ b/core/modules/system/system.module
    +++ b/core/modules/system/system.module
    @@ -419,7 +419,7 @@ function system_element_info() {
    
    @@ -419,7 +419,7 @@ function system_element_info() {
       );
       $types['password_confirm'] = array(
         '#input' => TRUE,
    -    '#process' => array('form_process_password_confirm', 'user_form_process_password_confirm'),
    +    '#process' => array('form_process_password_confirm'),
         '#theme_wrappers' => array('form_element'),
       );
       $types['textarea'] = array(
    diff --git a/core/modules/user/lib/Drupal/user/AccountFormController.php b/core/modules/user/lib/Drupal/user/AccountFormController.php
    
    diff --git a/core/modules/user/lib/Drupal/user/AccountFormController.php b/core/modules/user/lib/Drupal/user/AccountFormController.php
    index 4983b0f..eca2eb2 100644
    
    index 4983b0f..eca2eb2 100644
    --- a/core/modules/user/lib/Drupal/user/AccountFormController.php
    
    --- a/core/modules/user/lib/Drupal/user/AccountFormController.php
    +++ b/core/modules/user/lib/Drupal/user/AccountFormController.php
    
    +++ b/core/modules/user/lib/Drupal/user/AccountFormController.php
    +++ b/core/modules/user/lib/Drupal/user/AccountFormController.php
    @@ -99,6 +99,7 @@
    
    @@ -99,6 +99,7 @@
             '#type' => 'password_confirm',
             '#size' => 25,
             '#description' => $this->t('To change the current user password, enter the new password in both fields.'),
    +        '#pre_render' => array('user_form_pre_render_password_confirm'),
           );
     
           // To skip the current password field, the user must have logged in via a
    @@ -146,6 +147,7 @@
    
    @@ -146,6 +147,7 @@
             '#type' => 'password_confirm',
             '#size' => 25,
             '#description' => $this->t('Provide a password for the new account in both fields.'),
    +        '#pre_render' => array('user_form_pre_render_password_confirm'),
             '#required' => TRUE,
           );
         }
    diff --git a/core/modules/user/user.module b/core/modules/user/user.module
    
    diff --git a/core/modules/user/user.module b/core/modules/user/user.module
    index f6a30cb..f551063 100644
    
    index f6a30cb..f551063 100644
    --- a/core/modules/user/user.module
    
    --- a/core/modules/user/user.module
    +++ b/core/modules/user/user.module
    
    +++ b/core/modules/user/user.module
    +++ b/core/modules/user/user.module
    @@ -1699,13 +1699,10 @@ function _user_mail_notify($op, $account, $langcode = NULL) {
    
    @@ -1699,13 +1699,10 @@ function _user_mail_notify($op, $account, $langcode = NULL) {
     /**
      * Form element process handler for client-side password validation.
      *
    - * This #process handler is automatically invoked for 'password_confirm' form
    - * elements to add the JavaScript and string translations for dynamic password
    - * validation.
    - *
    - * @see system_element_info()
    + * This #pre_render handler is added to the user registration form to add the
    + * JavaScript and string translations for dynamic password validation.
      */
    -function user_form_process_password_confirm($element) {
    +function user_form_pre_render_password_confirm($element) {
       $password_settings = array(
         'confirmTitle' => t('Passwords match:'),
         'confirmSuccess' => t('yes'),
    

    This changes seems 100% out of scope.

  2. +++ b/core/lib/Drupal/Core/Extension/ThemeHandler.php
    @@ -112,10 +112,32 @@ class ThemeHandler implements ThemeHandlerInterface {
    +  public function setDefault($theme) {
    +    $available_themes = $this->listInfo();
    +    if (!isset($available_themes[$key])) {
    +      throw new \Exception("Theme $key does not exists.");
    +    }
    +    $this->configFactory
    +        ->get('system.theme')
    +        ->set('default', $theme)
    +        ->save();
    +  }
    +
    +  /**
    

    Should we also take care of checking whether the default theme is enabled?

clemens.tolboom’s picture

Status: Needs work » Needs review
StatusFileSize
new10.11 KB

I've removed the bad part.

I've added a getMockTheme so the so code reduces some and make it's intent more obvious.

I still failed to get the tests running correctly. That's partly due to the error messages being empty in the test UI giving not much clues (doh).

core$ php vendor/phpunit/phpunit/phpunit.php tests/Drupal/Tests/Core/Extension/ThemeHandlerTest.php gives more hints but I probably made 1 mistake with a refactoring.

PHPUnit 3.7.21 by Sebastian Bergmann.

Configuration read from /Users/clemens/Sites/drupal/d8/www/core/phpunit.xml.dist

FFE.....

Time: 0 seconds, Memory: 6.00Mb

There was 1 error:

1) Drupal\Tests\Core\Extension\ThemeHandlerTest::testEnableAndListInfo
Exception: Theme seven does not exists.

/Users/clemens/Sites/drupal/d8/www/core/lib/Drupal/Core/Extension/ThemeHandler.php:163
/Users/clemens/Sites/drupal/d8/www/core/tests/Drupal/Tests/Core/Extension/ThemeHandlerTest.php:223

--


There were 2 failures:

1) Drupal\Tests\Core\Extension\ThemeHandlerTest::testThemeEnableWithTooLongName
Failed asserting that exception of type "PHPUnit_Framework_Error_Warning" matches expected exception "\Drupal\Core\Extension\ExtensionNameLengthException". Message was: "Invalid argument supplied for foreach()".


2) Drupal\Tests\Core\Extension\ThemeHandlerTest::testEnableSingleTheme
Expectation failed for method name is equal to <string:invokeAll> when invoked at sequence index 0
Parameter 0 for invocation Drupal\Core\Extension\ModuleHandlerInterface::invokeAll('themes_enabled', Array (...)) does not match expected value.
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'system_theme_info'
+'themes_enabled'

/Users/clemens/Sites/drupal/d8/www/core/lib/Drupal/Core/Extension/ThemeHandler.php:180
/Users/clemens/Sites/drupal/d8/www/core/tests/Drupal/Tests/Core/Extension/ThemeHandlerTest.php:168

FAILURES!
Tests: 8, Assertions: 38, Failures: 2, Errors: 1.

Some mocks add stylesheets and other settings which seems not important for the test which I don't get either.

I could use some more help :-/

clemens.tolboom’s picture

@dawehner++

Thanks for your explanation in #10. It's nice to learn everyday a little.

Status: Needs review » Needs work

The last submitted patch, 11: core-theme-default-2167347-11.patch, failed testing.

dawehner’s picture

Status: Needs work » Needs review
StatusFileSize
new11.17 KB
new2.36 KB

Yeah phpunit should just not be runned from the simpletest UI. This integration just exists for the testbot.

clemens.tolboom’s picture

Issue summary: View changes
StatusFileSize
new3.32 KB
new12.67 KB

I've added 2 tests: setDefault to and enable a non-existing theme

We still have two remaining tasks.

clemens.tolboom’s picture

+++ b/core/tests/Drupal/Tests/Core/Extension/ThemeHandlerTest.php
@@ -316,67 +336,40 @@ public function providerTestGetBaseThemes() {
     // Tests a theme with a non existing base theme.

We have no code for missing base theme in ThemeHandler.

This record from the test should fail IMHO.

What do you think?

dawehner’s picture

+ /**
+ * Tries to enable a non existing theme.
+ *
+ * @expectedException \Drupal\Core\Extension\ThemeDoesNotExistException
+ * @expectedExceptionMessage Cannot set the default theme to a non existing theme 'non_existing_theme'.
+ */
+ public function testSetDefaultToNonExistingTheme() {
+ $this->themeHandler->systemList = array();
+ $this->themeHandler->setDefault('non_existing_theme');
+ }

I wonder what should happen if the theme exists but is simply not enabled.

We have no code for missing base theme in ThemeHandler.

All the work you have done is great, also this idea, though we should better stop at some point and continue to work on a follow up?

clemens.tolboom’s picture

I'm not sure whether this issue is complete yet.

In #10 and in #17 we have the 'Check for not enabled'

And is the ThemeHandlerInterface rich enough? Shouldn't we add

function isDefault($theme);

function setAdmin($theme);
function getAdmin();
function isAdmin($theme);

I'm willing to add these but I agree with "Could be done through follow-ups".

clemens.tolboom’s picture

I've added the get|set|is-Admin() and isDefault() to the interface and wrote the implementations.

As we are stalling themers to build sub themes let's get this one in.

The followup for this issue is #2176355: Followup: rewrite remaining code to use themehandler get|set|is for default and admin

dawehner’s picture

+++ b/core/lib/Drupal/Core/Extension/ThemeHandler.php
@@ -125,10 +125,70 @@ class ThemeHandler implements ThemeHandlerInterface {
+  public function setDefault($theme) {
...
+  public function getDefault() {
...
+  public function isDefault($theme) {
...
+  public function getAdmin() {
...
+  public function setAdmin($theme) {
...
+   */
+  public function isAdmin($theme) {

Lets's maybe also step back and think whether the ThemeHandler should contain these values. The default/admin theme is a concept which is just used by theme negotiators but nothing else.

clemens.tolboom’s picture

@dawehner in #2

+++ b/core/lib/Drupal/Core/Extension/ThemeHandler.php
@@ -147,7 +163,7 @@ class ThemeHandler implements ThemeHandlerInterface {
- if ($pos = array_search($this->configFactory->get('system.theme')->get('default'), $theme_list) !== FALSE) {
+ if ($pos = array_search($this->getDefault(), $theme_list) !== FALSE) {

nice!

I disagree with these getting removed as it makes a lot of code cleaner as sketched in #2176355: Followup: rewrite remaining code to use themehandler get|set|is for default and admin

clemens.tolboom’s picture

Issue summary: View changes
dawehner’s picture

It would be great to get some oppinions from other people on that. I don't want to be the bad guy, I was just thinking about it.

Noe_’s picture

+++ b/core/lib/Drupal/Core/Extension/ThemeHandler.php
@@ -171,7 +236,11 @@ class ThemeHandler implements ThemeHandlerInterface {
+        throw new \Exception("Theme $key does not exists.");

Why is this an Exception and not a ThemeDoesNotExistException?

clemens.tolboom’s picture

Status: Needs review » Needs work

@Noe_ thanks for finding. Feel free to patch too :)

joelpittet’s picture

Version: 8.0.x-dev » 8.1.x-dev

This is a bug report and it looks like it's including things that are not necessary to fix the bug.

If we can get back to just the bug fix we may be able to get this in the 8.0.x branch but without that it's going to be pushed to 8.1.x as a feature request.

Also the indent is not following Drupal coding standards for the next one to write a patch for this.

I agree with @dawehner we should step back a bit on this. And leave the stuff from #2176355: Followup: rewrite remaining code to use themehandler get|set|is for default and admin to that issue's scope.

Version: 8.1.x-dev » 8.2.x-dev

Drupal 8.1.0-beta1 was released on March 2, 2016, which means new developments and disruptive changes should now be targeted against the 8.2.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.2.x-dev » 8.3.x-dev

Drupal 8.2.0-beta1 was released on August 3, 2016, which means new developments and disruptive changes should now be targeted against the 8.3.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.3.x-dev » 8.4.x-dev

Drupal 8.3.0-alpha1 will be released the week of January 30, 2017, which means new developments and disruptive changes should now be targeted against the 8.4.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.4.x-dev » 8.5.x-dev

Drupal 8.4.0-alpha1 will be released the week of July 31, 2017, which means new developments and disruptive changes should now be targeted against the 8.5.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.5.x-dev » 8.6.x-dev

Drupal 8.5.0-alpha1 will be released the week of January 17, 2018, which means new developments and disruptive changes should now be targeted against the 8.6.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.6.x-dev » 8.7.x-dev

Drupal 8.6.0-alpha1 will be released the week of July 16, 2018, which means new developments and disruptive changes should now be targeted against the 8.7.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.7.x-dev » 8.8.x-dev

Drupal 8.7.0-alpha1 will be released the week of March 11, 2019, which means new developments and disruptive changes should now be targeted against the 8.8.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.8.x-dev » 8.9.x-dev

Drupal 8.8.0-alpha1 will be released the week of October 14th, 2019, which means new developments and disruptive changes should now be targeted against the 8.9.x-dev branch. (Any changes to 8.9.x will also be committed to 9.0.x in preparation for Drupal 9’s release, but some changes like significant feature additions will be deferred to 9.1.x.). For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

Version: 8.9.x-dev » 9.1.x-dev

Drupal 8.9.0-beta1 was released on March 20, 2020. 8.9.x is the final, long-term support (LTS) minor release of Drupal 8, which means new developments and disruptive changes should now be targeted against the 9.1.x-dev branch. For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

Version: 9.1.x-dev » 9.2.x-dev

Drupal 9.1.0-alpha1 will be released the week of October 19, 2020, which means new developments and disruptive changes should now be targeted for the 9.2.x-dev branch. For more information see the Drupal 9 minor version schedule and the Allowed changes during the Drupal 9 release cycle.

Version: 9.2.x-dev » 9.3.x-dev

Drupal 9.2.0-alpha1 will be released the week of May 3, 2021, which means new developments and disruptive changes should now be targeted for the 9.3.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

quietone’s picture

Version: 9.3.x-dev » 8.0.x-dev
Status: Needs work » Closed (outdated)
Issue tags: +Bug Smash Initiative

This was fixed in 8.x #1067408: Themes do not have an installation status

Therefore, closing as outdated. If this is incorrect reopen the issue, by setting the status to 'Active', and add a comment explaining what still needs to be done.

Thanks!