Merge pull request #6317 from expsa/std-dynamic-bg-selection
add option to select the /web page bg
This commit is contained in:
commit
0eae7d55e1
|
|
@ -1 +1,3 @@
|
|||
from .hooks import test_pre_init_hook, test_post_init_hook
|
||||
from .hooks import test_pre_init_hook, test_post_init_hook
|
||||
from . import models
|
||||
from . import controllers
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from . import main
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
import base64
|
||||
from odoo import http
|
||||
from odoo.http import request
|
||||
|
||||
|
||||
class BackgroundImageController(http.Controller):
|
||||
|
||||
@http.route('/expert_std_backend_theme/get_background_image', type='json', auth='user')
|
||||
def get_background_image(self):
|
||||
"""Return the background image URL or False if using default"""
|
||||
try:
|
||||
attachment_id = request.env['ir.config_parameter'].sudo().get_param(
|
||||
'expert_std_backend_theme.home_menu_background_image_id', False
|
||||
)
|
||||
if attachment_id:
|
||||
attachment = request.env['ir.attachment'].sudo().browse(int(attachment_id))
|
||||
if attachment.exists() and attachment.datas:
|
||||
# Add write_date as cache buster to force reload when image changes
|
||||
timestamp = attachment.write_date.timestamp() if attachment.write_date else 0
|
||||
return {
|
||||
'url': '/web/image/%s?t=%s' % (attachment_id, int(timestamp)),
|
||||
'has_custom': True
|
||||
}
|
||||
except Exception:
|
||||
pass
|
||||
return {
|
||||
'url': '/expert_std_backend_theme/static/src/img/bg_app_drawer.png',
|
||||
'has_custom': False
|
||||
}
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from . import res_config_settings
|
||||
|
|
@ -0,0 +1,86 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from odoo import api, fields, models
|
||||
|
||||
|
||||
class ResConfigSettings(models.TransientModel):
|
||||
_inherit = 'res.config.settings'
|
||||
|
||||
home_menu_background_image = fields.Binary(
|
||||
string='Home Menu Background Image',
|
||||
help='Upload a custom background image for the home menu page. If not set, the default image will be used.'
|
||||
)
|
||||
|
||||
@api.model
|
||||
def get_values(self):
|
||||
res = super(ResConfigSettings, self).get_values()
|
||||
attachment_id = self.env['ir.config_parameter'].sudo().get_param(
|
||||
'expert_std_backend_theme.home_menu_background_image_id', False
|
||||
)
|
||||
if attachment_id:
|
||||
attachment = self.env['ir.attachment'].sudo().browse(int(attachment_id))
|
||||
if attachment.exists():
|
||||
res.update(home_menu_background_image=attachment.datas)
|
||||
return res
|
||||
|
||||
def set_values(self):
|
||||
super(ResConfigSettings, self).set_values()
|
||||
param = self.env['ir.config_parameter'].sudo()
|
||||
|
||||
if self.home_menu_background_image:
|
||||
# Detect MIME type from image data
|
||||
import base64
|
||||
try:
|
||||
image_data = base64.b64decode(self.home_menu_background_image)
|
||||
# Detect image type from magic bytes
|
||||
if image_data.startswith(b'\x89PNG'):
|
||||
mimetype = 'image/png'
|
||||
extension = 'png'
|
||||
elif image_data.startswith(b'\xFF\xD8\xFF'):
|
||||
mimetype = 'image/jpeg'
|
||||
extension = 'jpg'
|
||||
elif image_data.startswith(b'GIF'):
|
||||
mimetype = 'image/gif'
|
||||
extension = 'gif'
|
||||
else:
|
||||
mimetype = 'image/png' # Default fallback
|
||||
extension = 'png'
|
||||
except Exception:
|
||||
mimetype = 'image/png'
|
||||
extension = 'png'
|
||||
|
||||
# Create or update attachment
|
||||
attachment_id = param.get_param('expert_std_backend_theme.home_menu_background_image_id', False)
|
||||
if attachment_id:
|
||||
attachment = self.env['ir.attachment'].sudo().browse(int(attachment_id))
|
||||
if attachment.exists():
|
||||
attachment.write({
|
||||
'datas': self.home_menu_background_image,
|
||||
'name': 'home_menu_background_image.%s' % extension,
|
||||
'mimetype': mimetype,
|
||||
})
|
||||
else:
|
||||
attachment_id = False
|
||||
else:
|
||||
attachment_id = False
|
||||
|
||||
if not attachment_id:
|
||||
# Create new attachment
|
||||
attachment = self.env['ir.attachment'].sudo().create({
|
||||
'name': 'home_menu_background_image.%s' % extension,
|
||||
'type': 'binary',
|
||||
'datas': self.home_menu_background_image,
|
||||
'mimetype': mimetype,
|
||||
'public': True, # Make it accessible without authentication
|
||||
'res_model': 'res.config.settings',
|
||||
'res_field': 'home_menu_background_image',
|
||||
})
|
||||
param.set_param('expert_std_backend_theme.home_menu_background_image_id', attachment.id)
|
||||
else:
|
||||
# Remove the image - delete attachment and parameter
|
||||
attachment_id = param.get_param('expert_std_backend_theme.home_menu_background_image_id', False)
|
||||
if attachment_id:
|
||||
attachment = self.env['ir.attachment'].sudo().browse(int(attachment_id))
|
||||
# Only delete if it exists and is owned by this module
|
||||
if attachment.exists() and attachment.res_model == 'res.config.settings':
|
||||
attachment.unlink()
|
||||
param.set_param('expert_std_backend_theme.home_menu_background_image_id', False)
|
||||
|
|
@ -0,0 +1,186 @@
|
|||
odoo.define('expert_std_backend_theme.background', function (require) {
|
||||
"use strict";
|
||||
|
||||
var rpc = require('web.rpc');
|
||||
var core = require('web.core');
|
||||
|
||||
// Cache for background image URL to prevent flash
|
||||
var cachedBackgroundUrl = null;
|
||||
var isFetching = false;
|
||||
|
||||
/**
|
||||
* Check if we're on the home menu page
|
||||
* Home menu is shown when URL hash contains 'home' or is empty/just '#'
|
||||
*/
|
||||
function isHomeMenuPage() {
|
||||
var hash = window.location.hash || '';
|
||||
// Check if hash contains 'home' or is empty/just '#'
|
||||
var isHome = hash.indexOf('home') !== -1 || hash === '' || hash === '#';
|
||||
// Also check if home menu element exists
|
||||
var hasHomeMenu = document.querySelector('.o_home_menu') !== null;
|
||||
return isHome && hasHomeMenu;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch and cache the background image URL
|
||||
*/
|
||||
function fetchBackgroundUrl() {
|
||||
if (cachedBackgroundUrl !== null) {
|
||||
return Promise.resolve(cachedBackgroundUrl);
|
||||
}
|
||||
if (isFetching) {
|
||||
// If already fetching, wait for it
|
||||
return new Promise(function(resolve) {
|
||||
var checkInterval = setInterval(function() {
|
||||
if (cachedBackgroundUrl !== null) {
|
||||
clearInterval(checkInterval);
|
||||
resolve(cachedBackgroundUrl);
|
||||
}
|
||||
}, 50);
|
||||
});
|
||||
}
|
||||
isFetching = true;
|
||||
return rpc.query({
|
||||
route: '/expert_std_backend_theme/get_background_image',
|
||||
}).then(function (result) {
|
||||
isFetching = false;
|
||||
if (result && result.url) {
|
||||
cachedBackgroundUrl = result.url;
|
||||
} else {
|
||||
cachedBackgroundUrl = '/expert_std_backend_theme/static/src/img/bg_app_drawer.png';
|
||||
}
|
||||
return cachedBackgroundUrl;
|
||||
}).catch(function () {
|
||||
isFetching = false;
|
||||
cachedBackgroundUrl = '/expert_std_backend_theme/static/src/img/bg_app_drawer.png';
|
||||
return cachedBackgroundUrl;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the background image for the home menu
|
||||
* Checks for custom uploaded image, falls back to default
|
||||
* Only applies on the actual home menu page
|
||||
*/
|
||||
function setHomeMenuBackground() {
|
||||
var body = document.body;
|
||||
if (!body) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Only apply background if we're on the home menu page
|
||||
if (!isHomeMenuPage()) {
|
||||
// Remove background if we're not on home menu
|
||||
if (body.classList.contains('o_home_menu_background')) {
|
||||
body.style.backgroundImage = '';
|
||||
body.style.backgroundPosition = '';
|
||||
body.style.backgroundRepeat = '';
|
||||
body.style.backgroundSize = '';
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Only proceed if body has the home menu background class
|
||||
if (!body.classList.contains('o_home_menu_background')) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Prevent CSS default from showing - clear it first
|
||||
body.style.backgroundImage = 'none';
|
||||
|
||||
// Use cached URL if available, otherwise fetch
|
||||
if (cachedBackgroundUrl !== null) {
|
||||
// Apply immediately if cached
|
||||
body.style.backgroundImage = 'url(' + cachedBackgroundUrl + ')';
|
||||
body.style.backgroundPosition = 'center';
|
||||
body.style.backgroundRepeat = 'no-repeat';
|
||||
body.style.backgroundSize = 'cover';
|
||||
} else {
|
||||
// Fetch and apply (background is already cleared to prevent flash)
|
||||
fetchBackgroundUrl().then(function(url) {
|
||||
if (isHomeMenuPage() && body.classList.contains('o_home_menu_background')) {
|
||||
body.style.backgroundImage = 'url(' + url + ')';
|
||||
body.style.backgroundPosition = 'center';
|
||||
body.style.backgroundRepeat = 'no-repeat';
|
||||
body.style.backgroundSize = 'cover';
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Pre-fetch background URL early to prevent flash
|
||||
fetchBackgroundUrl();
|
||||
|
||||
// Listen for home menu events - pre-fetch BEFORE showing to prevent flash
|
||||
core.bus.on('will_show_home_menu', null, function () {
|
||||
// Pre-fetch the background URL before home menu is shown
|
||||
fetchBackgroundUrl();
|
||||
});
|
||||
|
||||
core.bus.on('show_home_menu', null, function () {
|
||||
// Apply background immediately when home menu shows
|
||||
setTimeout(setHomeMenuBackground, 10);
|
||||
});
|
||||
|
||||
// Set background when DOM is ready
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', setHomeMenuBackground);
|
||||
} else {
|
||||
setHomeMenuBackground();
|
||||
}
|
||||
|
||||
core.bus.on('hide_home_menu', null, function () {
|
||||
// Remove background when leaving home menu
|
||||
var body = document.body;
|
||||
if (body) {
|
||||
body.style.backgroundImage = '';
|
||||
body.style.backgroundPosition = '';
|
||||
body.style.backgroundRepeat = '';
|
||||
body.style.backgroundSize = '';
|
||||
}
|
||||
});
|
||||
|
||||
// Also set it when navigating (for SPA behavior)
|
||||
core.bus.on('web_client_ready', null, function () {
|
||||
setTimeout(setHomeMenuBackground, 100);
|
||||
});
|
||||
|
||||
// Watch for URL hash changes (Odoo navigation) - using hashchange event instead of interval
|
||||
window.addEventListener('hashchange', function() {
|
||||
setTimeout(setHomeMenuBackground, 100);
|
||||
});
|
||||
|
||||
// Watch for body class changes (Odoo navigation)
|
||||
var bodyObserver = new MutationObserver(function(mutations) {
|
||||
mutations.forEach(function(mutation) {
|
||||
if (mutation.type === 'attributes' && mutation.attributeName === 'class') {
|
||||
setTimeout(setHomeMenuBackground, 100);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
if (document.body) {
|
||||
bodyObserver.observe(document.body, {
|
||||
attributes: true,
|
||||
attributeFilter: ['class']
|
||||
});
|
||||
}
|
||||
|
||||
// Cleanup observer and clear cache when page unloads
|
||||
window.addEventListener('beforeunload', function() {
|
||||
if (bodyObserver) {
|
||||
bodyObserver.disconnect();
|
||||
}
|
||||
cachedBackgroundUrl = null;
|
||||
isFetching = false;
|
||||
});
|
||||
|
||||
return {
|
||||
setHomeMenuBackground: setHomeMenuBackground,
|
||||
clearCache: function() {
|
||||
cachedBackgroundUrl = null;
|
||||
isFetching = false;
|
||||
fetchBackgroundUrl().then(setHomeMenuBackground);
|
||||
}
|
||||
};
|
||||
});
|
||||
|
|
@ -10,6 +10,7 @@
|
|||
<link rel="stylesheet" type="text/scss" href="/expert_std_backend_theme/static/src/scss/theme.scss"/>
|
||||
<link rel="stylesheet" type="text/scss" href="/expert_std_backend_theme/static/src/scss/web.scss"/>
|
||||
<script src="/expert_std_backend_theme/static/src/js/menu.js"></script>
|
||||
<script src="/expert_std_backend_theme/static/src/js/background.js"></script>
|
||||
</xpath>
|
||||
</template>
|
||||
</data>
|
||||
|
|
|
|||
|
|
@ -42,6 +42,26 @@
|
|||
</div>
|
||||
</div>
|
||||
</xpath>
|
||||
<xpath expr="//div[hasclass('settings')]" position="inside">
|
||||
<div class="app_settings_block" data-string="Theme" data-key="expert_std_backend_theme">
|
||||
<h2>Theme Customization</h2>
|
||||
<div class="row mt16 o_settings_container" name="theme_background_setting_container">
|
||||
<div class="col-12 col-lg-6 o_setting_box">
|
||||
<div class="o_setting_left_pane"/>
|
||||
<div class="o_setting_right_pane">
|
||||
<label for="home_menu_background_image" string="Home Menu Background Image"/>
|
||||
<div class="text-muted mb16">
|
||||
Upload a custom background image for the home menu page. If not set, the default image will be used.
|
||||
</div>
|
||||
<field name="home_menu_background_image" widget="image" class="o_field_image" options="{'preview_image': 'home_menu_background_image', 'size': [1024, 1024]}"/>
|
||||
<div class="text-muted mt8">
|
||||
<small>Recommended size: 1920x1080 or higher. Supported formats: PNG, JPG, JPEG</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</xpath>
|
||||
</data>
|
||||
</field>
|
||||
</record>
|
||||
|
|
|
|||
Loading…
Reference in New Issue