-
-
Notifications
You must be signed in to change notification settings - Fork 2.1k
[18.0][IMP] web_theme_classic Add ability to toggle theme #3394
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: 18.0
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| from . import models |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| from . import ir_http, res_users, res_users_settings |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| # © 2022 Florian Kantelberg - initOS GmbH | ||
| # © 2025 Liam Noonan - Pyxiris | ||
| # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). | ||
|
|
||
| from odoo import models | ||
| from odoo.http import request | ||
|
|
||
|
|
||
| class IrHttp(models.AbstractModel): | ||
| _inherit = "ir.http" | ||
|
|
||
| @classmethod | ||
| def _set_classic_theme(cls, response): | ||
| user = request.env.user | ||
| if user and user._is_internal(): | ||
| existing_transient_theme = request.httprequest.cookies.get( | ||
| "transient_classic_theme_cookie" | ||
| ) | ||
| persistent_theme = getattr(user, "persistent_classic_theme", None) | ||
| # Delete the cookie so that when persistent gets turned off the user | ||
| # will not be left wondering why nothing changed | ||
| if persistent_theme and existing_transient_theme: | ||
| response.delete_cookie("transient_classic_theme_cookie") | ||
|
|
||
| @classmethod | ||
| def _post_dispatch(cls, response): | ||
| cls._set_classic_theme(response) | ||
| return super()._post_dispatch(response) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| # © 2022 Florian Kantelberg - initOS GmbH | ||
| # © 2025 Liam Noonan - Pyxiris | ||
| # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). | ||
|
|
||
| from odoo import fields, models | ||
|
|
||
|
|
||
| class ResUsers(models.Model): | ||
| _inherit = "res.users" | ||
|
|
||
| persistent_classic_theme = fields.Boolean( | ||
| related="res_users_settings_id.persistent_classic_theme", | ||
| readonly=False, | ||
| string="Classic Theme Persistent", | ||
| help="This enables Classic Theme on this user's account across all devices. \n " | ||
| "Disabling it will will alow you to to use the toggle in the user burger menu " | ||
| "in the navbar to enable Classic Mode on a specific session/device \n" | ||
| "The toggle is not visible while Persistent Classic Theme is enabled", | ||
| ) | ||
|
|
||
| @property | ||
| def SELF_READABLE_FIELDS(self): | ||
| return super().SELF_READABLE_FIELDS + [ | ||
| "persistent_classic_theme", | ||
| ] | ||
|
|
||
| @property | ||
| def SELF_WRITEABLE_FIELDS(self): | ||
| return super().SELF_WRITEABLE_FIELDS + [ | ||
| "persistent_classic_theme", | ||
| ] | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| # © 2026 Liam Noonan - Pyxiris | ||
| # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). | ||
|
|
||
| from odoo import fields, models | ||
|
|
||
|
|
||
| class ResUsersSettings(models.Model): | ||
| _inherit = "res.users.settings" | ||
|
|
||
| # These fields should be here in order to be accessible via in js | ||
| # as user.settings.persistent_classic_theme | ||
| persistent_classic_theme = fields.Boolean(default=True) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nitpick: maybe add help for the note from the configure.md, something like: "When switching the theme all users need to reload the page to have a effect."
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There is already a pretty verbose tooltip defined in res_users.py, although it does not mention the need to reload. I'm hesitant to add this though as pressing "Save" from the
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @ljmnoonan oh, clear! |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| This module allows each user to choose whether they would like input fields to be displayed the "classic" way or the new, standard way (as if this module were not installed) | ||
|
|
||
| To do this you can either: | ||
| + Check "Classic Theme Persistent" in user preferences. This will enable the classic theme for that user across all devices. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think you should write "Uncheck" since you enabled the theme by default if the conversation in the pr is right?
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ofc: nitpick
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it should stay "check" as writing "uncheck" would necessitate inverting the whole sentence. It's kind of odd describing a feature by what it doesn't do when it is turned off. The next line describes behavior when unchecked too. |
||
| + Check the "Classic Theme" toggle in the popover menu triggered bu clicking on the user icon in the navbar. This toggle is only visible when "Classic Theme Persistent" is disabled. | ||
|
|
||
ljmnoonan marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| Please note that when disabling "Classic Theme Persistent" the style will not change until the page is reloaded. | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,62 @@ | ||
| // © 2022 Florian Kantelberg - initOS GmbH | ||
| // © 2025 Liam Noonan - Pyxiris | ||
| // License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). | ||
|
|
||
| import {_t} from "@web/core/l10n/translation"; | ||
| import {browser} from "@web/core/browser/browser"; | ||
| import {cookie} from "@web/core/browser/cookie"; | ||
| import {registry} from "@web/core/registry"; | ||
| import {user} from "@web/core/user"; | ||
|
|
||
| /** | ||
| * @param {import("@web/env").OdooEnv} env | ||
| */ | ||
| function classicThemeSwitchItem(env) { | ||
| return { | ||
| type: "switch", | ||
| id: "classic_theme.switch", | ||
| description: _t("Classic Theme"), | ||
| callback: () => { | ||
| env.services.classic_theme.switchTheme(); | ||
| }, | ||
| isChecked: cookie.get("transient_classic_theme_cookie") === "classic", | ||
| sequence: 43, | ||
| }; | ||
| } | ||
|
|
||
| export const classicThemeService = { | ||
| dependencies: ["ui"], | ||
|
|
||
| start(env, {ui}) { | ||
| // Apply theme on load | ||
| if ( | ||
| cookie.get("transient_classic_theme_cookie") === "classic" || | ||
| user.settings.persistent_classic_theme | ||
| ) { | ||
| document.body.classList.add("classic-theme"); | ||
| } | ||
|
|
||
| if (!user.settings.persistent_classic_theme) { | ||
| registry | ||
| .category("user_menuitems") | ||
| .add("classic_theme.switch", classicThemeSwitchItem); | ||
| } | ||
|
|
||
| return { | ||
| async switchTheme() { | ||
| const newValue = | ||
| cookie.get("transient_classic_theme_cookie") === "classic" | ||
| ? "pure" | ||
| : "classic"; | ||
| cookie.set("transient_classic_theme_cookie", newValue); | ||
| document.body.classList.toggle("classic-theme", newValue === "classic"); | ||
|
|
||
| // We do not actually need a reload, but it does get rid of some style glitches | ||
| ui.block(); | ||
| browser.location.reload(); | ||
| }, | ||
| }; | ||
| }, | ||
| }; | ||
|
|
||
| registry.category("services").add("classic_theme", classicThemeService); |

Uh oh!
There was an error while loading. Please reload this page.