Problem/Motivation

Enabling user overrides enables a lot of options which may not always be desired. Perhaps e.g. only dark mode selection is desired, or all except control of the toolbar.

Steps to reproduce

  1. Enable overrides from the Gin theme settings
  2. All options are given to the user

Proposed resolution

  1. Add a secondary field 'Enabled user override options'
  2. Default existing sites that have overrides enabled to have all options enabled to maintain status quo

Remaining tasks

  1. Merge request
  2. Update hook to maintain status quo
  3. Test coverage update

User interface changes

Gin settings gets a secondary checkboxes field to control which options are possible to override per user.

Gin theme settings:

Screenshot of Gin admin theme settings affected

User override options shown:

Screenshot of user overrides screen affect

API changes

N/A

Data model changes

N/A

CommentFileSizeAuthor
#4 user-overrides.png43.03 KBscott_euser
#4 overrides.png41.08 KBscott_euser

Issue fork gin-3505686

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Comments

scott_euser created an issue. See original summary.

scott_euser’s picture

Assigned: Unassigned » scott_euser
Status: Active » Needs work

Need to update test coverage still but out of time this morning, will finish later

scott_euser’s picture

Issue summary: View changes
StatusFileSize
new41.08 KB
new43.03 KB
scott_euser’s picture

Issue summary: View changes
Status: Needs work » Needs review

Okay added test coverage, added update hook to maintain status quo for existing sites.

jurgenhaas’s picture

Status: Needs review » Needs work

Hey @scott_euser, thanks for your contribution. I've only had a quick look into the MR and it looks really comprehensive. Just one major gap, I think hasn't been covered:

If one of the settings has been enabled and a user has altered the setting in their profile, it will be stored for them. Now, if that setting gets disabled for user overrides later on, the setting will still be stored with those users. As a result, \Drupal\gin\GinSettings::get may return an overridden value, although that's no longer allowed. That method needs to check the enabled override settings at that point.

And a minor missing tweak is in the update hook. The enabled_user_theme_settings needs to be initialized with an empty array if enabled_user_theme_settings is turned off.

I'll go for a more comprehensive review when we've sorted those out.

saschaeggi’s picture

Status: Needs work » Postponed (maintainer needs more info)
Related issues: +#3138529: Add permissions for User settings

Thank you for your contribution here.

I have one question: Could this be solved with permissions? We've started https://git.drupalcode.org/project/gin_permissions a long time ago but it got never fully being worked on

See also #3138529: Add permissions for User settings

I'm asking as there could be a use case that you want to expose more settings to admins and less settings to content editors. But this solution seems to generally limit the settings no matter their role.

scott_euser’s picture

Assigned: scott_euser » Unassigned
Status: Postponed (maintainer needs more info) » Needs review

Up to you of course. Looks like you closed that issue #3138529: Add permissions for User settings years ago as adding too much complexity; I tend to agree; not sure there is a case e.g. where you'd like admin role control dark mode but not let editors do that. This is much simpler.

I could create a new module, or put in a request to co-maintain that module if you aren't interested in having it in here.

For now I addressed the feedback in #6 + added further test coverage.

saschaeggi’s picture

@scott_euser we closed it in favor of https://git.drupalcode.org/project/gin_permissions

So it could be worked on in gin permissions and I could add you as a maintainer if you'd like to contribute to that ☺️

But would also love to hear @jurgenhaas opinion first about a permission approach 👀

scott_euser’s picture

gin_permissions seems like it never had any attempt at making it work though.

Personally I think better to start simple and gin_permissions can then leverage this to control per role for someone needing that complexity. I could open an issue there like 'Future of this module' and request people comment if they do have a need for different permissions per role. We could then gauge whether there is an actual demand for that option rather than building in just in case?

scott_euser’s picture

So it could be worked on in gin permissions and I could add you as a maintainer if you'd like to contribute to that ☺️

But if ultimately the two of you decide you prefer that approach, happy to take over development and maintenance of it and shift this code over to there + extend for permissions layer

jurgenhaas’s picture

Status: Needs review » Needs work

I really like the idea of making this permission based, but I don't like the idea of a separate module. I guess, it felt necessary because a them can't provide permissions? If that's the case, I'd rather put that into gin_toolbar as it is already a hard dependency for Gin and we could rely on it to be present.

But to do this step by step, I think the approach in MR!575 is a great start and could afterwards be extended with a permission controlled follow-up.

I just don't believe that the latest merge in helper.theme is appropriate. This should be handled in \Drupal\gin\GinSettings::get so that this service is the one central place which determines the correct value for each settings. Whatever feature in Gin needs to know about the setting, they should all just call that service to always receive the correct value.

scott_euser’s picture

Status: Needs work » Needs review

I just don't believe that the latest merge in helper.theme is appropriate. This should be handled in \Drupal\gin\GinSettings::get so that this service is the one central place which determines the correct value for each settings.

Can you have another look please? That is already the case, see https://git.drupalcode.org/project/gin/-/merge_requests/575/diffs#411dd7... + the test converage checking for 'Increase contrast' not shown, then config update only (no user data change) 'Increase contrast' being shown also validates that that is the case.

The helper.theme only addresses this comment from you earlier:

Now, if that setting gets disabled for user overrides later on, the setting will still be stored with those users.

Well sort of addresses it; it maintains the same problem as Gin theme currently has - that user data settings are only removed on resave (which is fine since the GinSettings::get() respects the enabled overrides as per above right?).

saschaeggi’s picture

I'm relying on Jürgen's expertise here, but if we can move complexity out of Gin or avoid adding it by adding it to a helper module I'm usually very happy as Gin is already quite complex on it's own. My 5 cent:

I guess, it felt necessary because a them can't provide permissions?

100%, themes can't declare permissions. So a helper module is needed

If that's the case, I'd rather put that into gin_toolbar as it is already a hard dependency for Gin and we could rely on it to be present.

That is currently the case, but once navigation is stable and takes over and we deprecate toolbar we have to evaluate the need for gin_toolbar again. So if this specific use case is handled in it's separated module it might make more sense [at least to me]

scott_euser’s picture

Yeah if separate module is the route personally would prefer gin permissions since it doesn't really have much to do with toolbar

jurgenhaas’s picture

Sorry for the delay. The gin_toolbar only came to mind as it is already an existing dependency. But @saschaeggi is right, of course, if the toolbar becomes deprecated, that wouldn't be a smart move.

If we go for another helper module instead, it should be declared as a hard dependency because the code in Gin would rely on those permissions being declared.

scott_euser’s picture

An alternative is I reduce the scope of this MR to just refactoring the code enough to make it easy to extend.

Then e.g. with a route subscriber or something like that the gin_permissions module can be a progressive enhancement.

In which case here it can be in 'suggest' within composer.json and possibly a helptext in the current form like "Looking for more fine-grained permissions around user overrides..." type thing.

Would that work?

If so, did either of you want to be the owner of gin_permissions and make me a maintainer? Happy to make it independently too if you prefer and add you both as co-maintainers.

jurgenhaas’s picture

The https://www.drupal.org/project/gin_permissions already exists, hope that @saschaeggi can maintainers there as @dermario is the owner.

Simplifying the MR sounds perfect. But I'm not sure what you mean by "progressive enhancement". So you want to alter route permissions? That wouldn't be sufficient, I guess, as we need access to the permissions when building the user profile form and when preprocessing elements, don't we?

saschaeggi’s picture

@Jürgen @Scott I've added you both as maintainers to gin_permissions

scott_euser’s picture

Status: Needs review » Closed (works as designed)

Thanks @sashaeggi

@jurgenhaas - sorry hadn't gone through the code very far, was more hypothetical, but basically with this understanding of your goals (do say if I got this wrong):

  1. You want default Gin theme to be untouched (ie, you aren't happy with current merge request, e.g. comment #13 in this thread)
  2. You want it to be separate permission based, and therefore in gin_permissions module instead

So in the current merge request, its not permissions based because permissions aren't available.

  1. Current state: site builder can decide which options user can override. All roles get the same permissions. Example: editor, author, etc all can change only dark mode and contrast (if those two are selected as overrides).
  2. State I believe you are after via Gin permissions: site builder can decide which roles have access to which permissions. Each role can have different permissions. Example: Author can change contrast only. Editor can change contrast and dark mode.

So I will work on changing to route 2 and close the current merge request I believe. Is that correct? So I think I should close this one then.

jurgenhaas’s picture

Status: Closed (works as designed) » Needs work

I describe the architecture once again with my own words, maybe that makes it easier to understand:

  • There should be fine-grained controls over which of the theme settings are allowed to be changed by the user and which aren't. Difference to what we have today: there is only one switch that either allows users to override theme settings or not, i.e. all or nothing.
  • This part should be implemented in Gin.
  • Following my comment #6 this needs some attention as there could always be a user setting around which isn't allowed to be made any longer, so then the global theme setting has to be applied.
  • Getting the current values for each setting and getting the setting if a setting field is available to the current user or not, both should be implemented in a service, and all code in Gin needs to call that service to get the values and whether the user can alter the setting.
  • For Gin to allow this fine-grained control, it needs extra settings where the admin can decide which settings is accessible by users. This is done without permissions because Gin should work even without the gin_permission module being installed. But those settings are only available, if the gin_permission module is not available, otherwise that module takes over.
  • The second step is then to make this permission based in the gin_permission module. Here is where the service comes in, that I mentioned above: in this central place, every time Gin needs to know if the user has access to override a setting, that service calls an alter hook with which the gin_permission module can override the default Gin decision with its own permission-based algorithm.

That way, Gin can work on its own just with settings on which setting can be overridden by users. And the optional gin_permission module can hook into that decision and overrule this settings-based decision by a permission-based decision.

Re-opening the issue as it still needs some work to be done.

scott_euser’s picture

Assigned: Unassigned » scott_euser

Thanks for providing more detail.

Already completed

There should be fine-grained controls over which of the theme settings are allowed to be changed by the user and which aren't. Difference to what we have today: there is only one switch that either allows users to override theme settings or not, i.e. all or nothing.
This part should be implemented in Gin.
Following my comment #6 this needs some attention as there could always be a user setting around which isn't allowed to be made any longer, so then the global theme setting has to be applied.

Yes that's the case already with the MR + has test coverage here which I tried to explain in #13.

Here is the test coverage proving this case:
https://git.drupalcode.org/project/gin/-/merge_requests/575/diffs#586603... where:

Here is the code that ensures that only enabled settings will use the user override:
https://git.drupalcode.org/project/gin/-/merge_requests/575/diffs#411dd7...

Already completed

For Gin to allow this fine-grained control, it needs extra settings where the admin can decide which settings is accessible by users. This is done without permissions because Gin should work even without the gin_permission module being installed. But those settings are only available, if the gin_permission module is not available, otherwise that module takes over.

Yes that is the case, per screenshot of the UI:

Screenshot of Gin admin theme settings affected

To do here

Getting the current values for each setting and getting the setting if a setting field is available to the current user or not, both should be implemented in a service, and all code in Gin needs to call that service to get the values and whether the user can alter the setting.

Okay understood will refactor the existing code in this MR to cover this.

To do in Gin Permissions

The second step is then to make this permission based in the gin_permission module. Here is where the service comes in, that I mentioned above: in this central place, every time Gin needs to know if the user has access to override a setting, that service calls an alter hook with which the gin_permission module can override the default Gin decision with its own permission-based algorithm.

Okay created follow-up here: #3512885: Create permissions per option that user can override

scott_euser’s picture

Assigned: scott_euser » Unassigned
Status: Needs work » Needs review
  • Okay I refactored to add GinSettings::userOverrideAccess() and updated the code to make use of that in GinSettings::get()
  • GinSettings::get() continues to be used in gin_page_attachments_alter()
  • I added an alter hook in there
  • I added test coverage to ensure the alter hook works
jurgenhaas’s picture

That looks great, thank you @scott_euser

Only one (final) consideration: should we move the _gin_user_form_submit into GinSettings as well? Then we have the settings related functionality all in one place.

scott_euser’s picture

Sure @jurgenhaas I have moved the contents of it over, I kept as public function, not public static function so $this can be used, figured that's better than a static callback and removing altogether, but fine to change to that if you prefer of course.

Thanks

jurgenhaas’s picture

Looks much better thank you. Any reason why we want to keep _gin_user_form_submit around? Couldn't the form builder refer to the submit method in the service directly? Just one fewer step that might otherwise be confusing at some point.

scott_euser’s picture

That's what I meant by #25, callback is static so removes function but then loses ability to use $this so ends up statically loading itself - matter of preference which you think is the lesser of two evils...

jurgenhaas’s picture

Ah, sorry, then I misread that.

But I guess we should be able to get both. See this change record which comes with an option "Object method, instantiated by ClassResolver, container aware using ContainerInjectionInterface", the third from the bottom of the list. That should allow us to use the method in GinSettings with a non-static method and dependency injection.

scott_euser’s picture

Ooh nice TIL, will update to use that then :)

scott_euser’s picture

Okay that works & tests pass locally on supported Drupal BUT I attempted to update gitlab-ci.yml because its not supported by Drupal 9 (and therefore also had to update PHP version because Drupal 10.3 (lowest supported version) does not support PHP 7.4). The error in current Gitlab CI setup is "You have requested a non-existent service "callable_resolver"" because it does not exist until Drupal 10.2.

So I reverted the change and left _gin_user_form_submit in.

I think there are two routes forward:

  1. Postpone then and create a separate issue to update Gitlab CI template to latest from the DA applying your eslint/stylelint
  2. Resolve any issues that reveals in further follow-ups (or potentially within Gitlab CI template issue if small)
  3. Re-apply the reverted change and set back to Needs Review here

Or I could create two follow-up issues to avoid this getting blocked for a longer time:

  1. Update gitlab CI template
  2. Convert _gin_user_form_submit to call GinSettings directly
scott_euser’s picture

For now started #3516730: Gitlab CI template update as I think its first step either way, whether you decide its a hard blocker to do callable or not

jurgenhaas’s picture

Great work @scott_euser. I'd say we go for the modern approach, so please bring back the last 3 reverted commits. There is just a tiny issue in there:

    /** @var \Drupal\Core\Utility\CallableResolver $submit_handler */

The variable should be $class_resolver instead of $submit_handler.

And then we should raise the core requirement in the info file to ^10.3 || ^11. But that can be done in the related issue. I'll review #3516730: Gitlab CI template update now, so that we can commit them both together and all should be great.

scott_euser’s picture

Assigned: Unassigned » scott_euser
Status: Needs review » Needs work

Sure sounds good, if its okay I will wait until you have merged #3516730: Gitlab CI template update since this will have some conflicts to resolve + can then double check that tests subsequently stay green.

jurgenhaas’s picture

Sure, let's do that.

scott_euser’s picture

Assigned: scott_euser » Unassigned
Status: Needs work » Needs review

Okay ready again, thanks for merging in the gitlab ci one!

scott_euser’s picture

Hi! Anything I can do here to help get this over the line? Thank you!

jurgenhaas’s picture

Excuse me @scott_euser, didn't mean to ignore this issue for such a long time. It's just that we're currently heads-down with the Gin migration into core, and we've stalled literally everything in the issue queue for the time being. Give us a bit more time until the dust settled on this major step, and we'll then get back into these topics that we'll want to get into Gin and move them forward in that new context.

scott_euser’s picture

No problem thanks for the update!

jwilson3’s picture

I'm here to report a related issue I'm having whereby the "Admin theme settings" wrapper element gets added to every user edit form mode. I don't know if this makes sense to add here or not. This does have to do with "fine-grained control" over display admin theme user settings.

Steps to reproduce:

  1. Install a standard D10/11 site with Gin Admin Theme installed and "Users can override Gin settings" option enabled.
  2. Visit /admin/config/people/accounts/form-display, expand "Custom display settings" and click "Manage Form Modes".
  3. Create a new User Form Mode call it anything you want, eg "Password reset" (machine name user.password_reset)
  4. Return to /admin/config/people/accounts/form-display and configure the new form mode to only display a single field: "User name and password"
  5. Visit /user/1/edit?display=password_reset
  6. See that the "Account" fieldset is visible, and the "Admin theme settings" details element is also shown below.

Proposed resolution:

Make "Admin theme settings" available for placement via drag and drop on Manage Form Display settings on /admin/config/people/accounts/form-display

If this can be tackled independently of the awesome overhaul going on in this issue, I'm happy to split this off to a separate issue, but I don't know enough about the plans here or whether it makes sense to roll this into this feature request or not. Thanks.

jwilson3’s picture

In addition to #39, I found the terminology of the form elements on the user edit form a bit confusing.

Currently, the UI looks like this:

› Admin theme settings
[ ] Enable overrides
    Enables default admin theme overrides.

I’d propose the following improvements:

  • Expose the field to Manage Form Display so it can be drag-n-dropped/enabled/disabled in different form modes. (comment #39)
  • Update the Details element label from “Admin theme settings” to “Administrative theme preferences”.
  • Revise the toggle field label from “Enable overrides” to “Personalize the administrative interface appearance”, and remove the redundant description text.
  • Implement a proper Field API widget with display options similar to the Address field widget, so users can select a Wrapper type (Container, Details, Fieldset) and override the default label text for Details/Fieldset.

The benefit of this approach is flexibility: if a frontend theme also provides customization options, both admin UI and frontend UI preferences could be grouped together under one field group. For example, by setting the wrapper to Container (invisible), you could present two checkboxes side by side inside a custom field group:

› Appearance Settings (custom field group)
[ ] Personalize the administrative interface appearance.
[ ] Personalize the website appearance.
scott_euser’s picture

Personally would much prefer to keep to the issue summary here as we have spent a lot of time having a back and forth to agree approach and get test coverage in. The goal here is to break up the options so it's not an all or nothing but otherwise leave the status quo to keep the scope limited and in a place that is realistically mergeable.

Changing the UX and what the options are do sound like you're raising seperate issues that are related to the user controls in general. I'm not a maintainer here of course so would suggest waiting until they chime in :)

scott_euser’s picture

Version: 4.0.x-dev » 5.0.x-dev

Resolved conflicts with 5.0.x branch and retargeted the MR to there, the phpstan issues are unrelated and exist on 5.0.x main as can be seen here https://git.drupalcode.org/project/gin/-/jobs/7291859

What can we do to get this in to avoid having to do a big conflict resolve again? Anything I can help with? Thanks!

jurgenhaas’s picture

I know this is very unfortunate. There is a lot urgent staff in 5.x for Drupal 11, and there's the ongoing effort to merge Gin into core. But codebases have diverged from each other a lot. So, I'm unsure how to approach this now. Doing the work twice isn't what we want to do, I guess. For the version that goes into core, the approach would have to be revisited.

saschaeggi’s picture

Status: Needs review » Postponed (maintainer needs more info)

What's the status here? Should this be closed for the contributed version of Gin and moved to Gin's Core version (admin)?

scott_euser’s picture

Status: Postponed (maintainer needs more info) » Needs review

We had resolved all issues after a fair amount of back and forth and it was ready to merge as final bits from #32 were addressed (the latest comments from others were hijacks/unrelated issues).

For the version that goes into core, the approach would have to be revisited.

Obviously would not like to have to redo all the work but sounds like that's the path sadly. I would rather not spend the time unless I have a new steer though because the above seems to indicate that a different approach might be needed for Core. Who would decide that? Or is that up to you and Jurgen still? E.g does this need to be targeted to 6x now? It's unclear what's happening in #3530849: [META] Gin 6.x: Preparation for merging into core.

jurgenhaas’s picture

That would have to happen in the main branch of core. And the php part of the theme is nothing lime it was before, everything is living in oop hooks now.

We could merge this into 5, though. But we would have to make it very clear that it won't be available afterwards. And Gin in contrib will probably not support Drupal 12.

As for starting the process for core, an issue would be the best starting point. And then getting sign off to do it before actually starting the code migration.

scott_euser’s picture

I think your comment that you are happy to merge it is sufficient; actually merging it and regressing after is probably too confusing. For now its not in Drupal Core yet is it? I tried at https://git.drupalcode.org/project/drupal/-/tree/main/core/themes?ref_ty... but don't see Gin there at this stage. So at the moment I should wait before opening or moving this issue project to core I think?

Not a problem converting to OOP hooks of course.

Thanks!

jurgenhaas’s picture

Haha, it's there, but it's not called Gin anymore. Go to https://git.drupalcode.org/project/drupal/-/tree/main/core/themes/defaul... and that's what used to be Gin.

scott_euser’s picture

Aha! I see, thank you!