You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
585 lines
21 KiB
585 lines
21 KiB
# -*- coding: utf-8 -*-
|
|
#############################################################################
|
|
#
|
|
# Cybrosys Technologies Pvt. Ltd.
|
|
#
|
|
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
|
|
# Author: Cybrosys Techno Solutions(<https://www.cybrosys.com>)
|
|
#
|
|
# You can modify it under the terms of the GNU LESSER
|
|
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details.
|
|
#
|
|
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE
|
|
# (LGPL v3) along with this program.
|
|
# If not, see <http://www.gnu.org/licenses/>.
|
|
#
|
|
#############################################################################
|
|
import json
|
|
import os
|
|
import re
|
|
|
|
from odoo import http
|
|
from odoo.http import request
|
|
|
|
|
|
def minify_css(path):
|
|
"""
|
|
Minify CSS file located at the specified path.
|
|
|
|
Parameters:
|
|
path (str): The file path to the CSS file to be minified.
|
|
|
|
Returns:
|
|
None
|
|
|
|
This function reads the CSS file specified by the given path, removes
|
|
comments,excess whitespace, and redundant CSS properties. It then writes
|
|
the minified CSS back to the same file, overwriting its previous contents.
|
|
|
|
Note:
|
|
This function modifies the CSS file in place.
|
|
|
|
Example:
|
|
minify_css('/path/to/style.css')
|
|
"""
|
|
with open(path, 'r') as f:
|
|
css = f.read()
|
|
css = re.sub(r'/\*[\s\S]*?\*/', "", css)
|
|
css = re.sub(r'url\((["\'])([^)]*)\1\)', r'url(\2)', css)
|
|
css = re.sub(r'\s+', ' ', css)
|
|
css = re.sub(r'#([0-9a-f])\1([0-9a-f])\2([0-9a-f])\3(\s|;)', r'#\1\2\3\4',
|
|
css)
|
|
css = re.sub(r':\s*0(\.\d+([cm]m|e[mx]|in|p[ctx]))\s*;', r':\1;', css)
|
|
rules = re.findall(r'([^{]+){([^}]*)}', css)
|
|
selectors_list = []
|
|
css_values = {}
|
|
for rule in rules:
|
|
selector = rule[0]
|
|
content = rule[1]
|
|
if selector not in selectors_list:
|
|
selectors_list.append(selector)
|
|
css_values[selector] = content
|
|
else:
|
|
css_values[selector] = css_values[selector] + content
|
|
with open(path, 'w') as f:
|
|
selector_dict = {}
|
|
for selector in selectors_list:
|
|
rule = css_values[selector].split(';')
|
|
dict_rule = {}
|
|
for r in rule:
|
|
if r:
|
|
split_rule = r.split(':')
|
|
if len(split_rule) == 2:
|
|
dict_rule[split_rule[0].strip()] = split_rule[1]
|
|
selector_dict[selector] = dict_rule
|
|
f.write('/* This Styles are generated automatically by Theme Studio'
|
|
' */\n')
|
|
for selector in selector_dict:
|
|
f.write(selector + '{')
|
|
for rule_data in selector_dict[selector]:
|
|
if rule_data != 'pointer-events':
|
|
if selector_dict[selector][rule_data].find(
|
|
'!important') == -1:
|
|
f.write(rule_data + ':' +
|
|
selector_dict[selector][rule_data] +
|
|
' !important;')
|
|
else:
|
|
f.write(rule_data + ':' +
|
|
selector_dict[selector][rule_data] + ';')
|
|
f.write('}')
|
|
|
|
|
|
class ThemeStudio(http.Controller):
|
|
"""
|
|
Controller class for managing themes in a web application.
|
|
|
|
This controller handles requests related to managing themes, such as
|
|
applying themes, customizing themes, and saving theme settings.
|
|
"""
|
|
|
|
@http.route(['/theme_studio/save_styles'], type="json")
|
|
def save_styles(self, kwargs):
|
|
"""
|
|
Save dynamic styles to a CSS file.
|
|
|
|
Parameters:
|
|
kwargs (dict): A dictionary containing the changed_styles and
|
|
object_class.
|
|
|
|
Returns:
|
|
bool: True if the styles are successfully saved, otherwise False.
|
|
|
|
This function takes the changed_styles and object_class from the
|
|
kwargs dictionary and saves them to a CSS file. It appends the styles
|
|
to an existing file or creates a new file if it doesn't exist.
|
|
After saving the styles, it minifies the CSS file.
|
|
|
|
Example:
|
|
To save styles for a specific object class:
|
|
|
|
```python
|
|
from my_theme_module import save_styles
|
|
|
|
save_styles({
|
|
'changed_styles': {
|
|
'color': 'red',
|
|
'font-size': '16px'
|
|
},
|
|
'object_class': 'my-object'
|
|
})
|
|
```
|
|
"""
|
|
changed_styles_str = kwargs.get('changed_styles', '{}')
|
|
object_class = kwargs.get('object_class', '')
|
|
changed_styles = json.loads(changed_styles_str)
|
|
working_dir = os.path.dirname(os.path.realpath(__file__))
|
|
working_dir = working_dir.replace('/controllers', '')
|
|
file_path = working_dir + '/static/src/css/dynamic_styles.css'
|
|
style_file = open(file_path, 'a')
|
|
|
|
if os.stat(file_path).st_size == 0:
|
|
style_file.write('/* This file is generated automatically by '
|
|
'Theme Infinito */\n')
|
|
|
|
style_file.write('\n.' + object_class + ' {\n')
|
|
|
|
for style in changed_styles:
|
|
style_file.write(
|
|
'\t' + style + ': ' + changed_styles[style] + ';\n')
|
|
|
|
style_file.write('}\n')
|
|
style_file.close()
|
|
minify_css(file_path)
|
|
return True
|
|
|
|
@http.route(['/theme_studio/get_current_style'], type="json")
|
|
def get_current_style(self, kwargs):
|
|
"""
|
|
Retrieve the current styles for a given CSS selector.
|
|
|
|
Parameters:
|
|
kwargs (dict): A dictionary containing the 'selector' key
|
|
specifying the CSS selector.
|
|
|
|
Returns:
|
|
list or bool: A list of style properties and values for the
|
|
specified selector, or False if the selector is not found.
|
|
|
|
This function reads the CSS file containing dynamic styles and
|
|
searches for the specified CSS selector. If the selector is found,
|
|
it returns a list of style properties and values associated with that
|
|
selector. Each style property-value pair is represented as a list
|
|
containing the property and its corresponding value.
|
|
If the selector is not found,it returns False.
|
|
|
|
Example:
|
|
To retrieve the current styles for a specific CSS selector:
|
|
|
|
```python
|
|
from my_theme_module import get_current_style
|
|
|
|
current_styles = get_current_style({
|
|
'selector': '.my-selector'
|
|
})
|
|
print(current_styles)
|
|
# Output: [['color', 'red'], ['font-size', '16px']]
|
|
```
|
|
"""
|
|
selector = kwargs.get('selector', '')
|
|
working_dir = os.path.dirname(os.path.realpath(__file__))
|
|
file_path = working_dir.replace('controllers',
|
|
'static/src/css/dynamic_styles.css')
|
|
style_file = open(file_path, 'r')
|
|
css = style_file.read()
|
|
css = re.sub(r'/\*[\s\S]*?\*/', "", css)
|
|
css = re.sub(r'url\((["\'])([^)]*)\1\)', r'url(\2)', css)
|
|
css = re.sub(r'\s+', ' ', css)
|
|
css = re.sub(r'#([0-9a-f])\1([\da-f])\2([0-9a-f])\3(\s|;)',
|
|
r'#\1\2\3\4', css)
|
|
css = re.sub(r':\s*0(\.\d+([cm]m|e[mx]|in|p[ctx]))\s*;', r':\1;', css)
|
|
rules = re.findall(r'([^{]+){([^}]*)}', css)
|
|
for rule in rules:
|
|
selector_now = rule[0]
|
|
content = rule[1]
|
|
if selector == selector_now.strip():
|
|
contents = content.split(';')
|
|
content = []
|
|
for c in contents:
|
|
c = c.split(':')
|
|
if c[0] != '' and len(c) > 1:
|
|
content.append(
|
|
[c[0], c[1].strip().replace('!important', '')])
|
|
return content
|
|
|
|
return False
|
|
|
|
@http.route(['/theme_studio/reset_to_default'], type="json")
|
|
def reset_to_default(self):
|
|
"""
|
|
Reset dynamic styles to default.
|
|
|
|
Returns:
|
|
bool: True if the styles are successfully reset, otherwise False.
|
|
|
|
This function clears the content of the CSS file containing
|
|
dynamic styles, effectively resetting all styles to their default
|
|
values.
|
|
|
|
Example:
|
|
To reset dynamic styles to default:
|
|
|
|
```python
|
|
from my_theme_module import reset_to_default
|
|
|
|
reset_to_default()
|
|
```
|
|
"""
|
|
working_dir = os.path.dirname(os.path.realpath(__file__))
|
|
file_path = working_dir.replace('controllers',
|
|
'static/src/css/dynamic_styles.css')
|
|
style_file = open(file_path, 'w')
|
|
style_file.write('')
|
|
return True
|
|
|
|
@http.route(['/theme_studio/set_advanced_data'], type="json")
|
|
def set_advanced_data(self, args):
|
|
"""
|
|
Set advanced theme configuration data.
|
|
|
|
Parameters:
|
|
args (list): A list containing a dictionary with the advanced
|
|
theme configuration values.
|
|
|
|
Returns:
|
|
bool: True if the configuration data is successfully set,
|
|
otherwise False.
|
|
|
|
This function sets advanced theme configuration data based on the
|
|
provided dictionary of values. It updates the configuration
|
|
parameters in the database accordingly.
|
|
|
|
Example:
|
|
To set advanced theme configuration data:
|
|
|
|
```python
|
|
from my_theme_module import set_advanced_data
|
|
|
|
data = [{
|
|
'vals': {
|
|
'sidebar': True,
|
|
'fullscreen': False,
|
|
'sidebarIcon': True,
|
|
'sidebarName': True,
|
|
'sidebarCompany': False,
|
|
'sidebarUser': True,
|
|
'recentApps': False,
|
|
'fullScreenApp': True,
|
|
'infinitoRtl': False,
|
|
'infinitoDark': True,
|
|
'infinitoDarkMode': 'dark',
|
|
'infinitoBookmark': True
|
|
}
|
|
}]
|
|
|
|
set_advanced_data(data)
|
|
```
|
|
"""
|
|
if args and 'vals' in args[0]:
|
|
vals = args[0]['vals']
|
|
set_param = request.env['ir.config_parameter'].sudo().set_param
|
|
set_param('backend_theme_infinito.is_sidebar_enabled',
|
|
vals['sidebar'])
|
|
set_param('backend_theme_infinito.is_fullscreen_enabled',
|
|
vals['fullscreen'])
|
|
set_param('backend_theme_infinito.is_sidebar_icon',
|
|
vals['sidebarIcon'])
|
|
set_param('backend_theme_infinito.is_sidebar_name',
|
|
vals['sidebarName'])
|
|
set_param('backend_theme_infinito.is_sidebar_company',
|
|
vals['sidebarCompany'])
|
|
set_param('backend_theme_infinito.is_sidebar_user',
|
|
vals['sidebarUser'])
|
|
set_param('backend_theme_infinito.is_recent_apps',
|
|
vals['recentApps'])
|
|
set_param('backend_theme_infinito.is_fullscreen_app',
|
|
vals['fullScreenApp'])
|
|
set_param('backend_theme_infinito.is_rtl', vals['infinitoRtl'])
|
|
set_param('backend_theme_infinito.is_dark', vals['infinitoDark'])
|
|
set_param('backend_theme_infinito.dark_mode',
|
|
vals['infinitoDarkMode'])
|
|
set_param('backend_theme_infinito.is_menu_bookmark',
|
|
vals['infinitoBookmark'])
|
|
|
|
@http.route(['/theme_studio/set_advanced_data_user'], type="json")
|
|
def set_advanced_data_user(self, args):
|
|
"""
|
|
Set advanced theme configuration data for the current user.
|
|
|
|
Parameters:
|
|
args (list): A list containing a dictionary with the advanced
|
|
theme configuration values.
|
|
|
|
Returns:
|
|
bool: True if the configuration data is successfully set for the
|
|
user, otherwise False.
|
|
|
|
This function sets advanced theme configuration data for the current
|
|
user based on the provided dictionary of values. It updates the
|
|
corresponding fields in the user record accordingly.
|
|
|
|
Example:
|
|
To set advanced theme configuration data for the current user:
|
|
|
|
```python
|
|
from my_theme_module import set_advanced_data_user
|
|
|
|
data = [{
|
|
'vals': {
|
|
'sidebar': True,
|
|
'fullscreen': False,
|
|
'sidebarIcon': True,
|
|
'sidebarName': True,
|
|
'sidebarCompany': False,
|
|
'sidebarUser': True,
|
|
'recentApps': False,
|
|
'fullScreenApp': True,
|
|
'infinitoRtl': False,
|
|
'infinitoDark': True,
|
|
'infinitoDarkMode': 'dark',
|
|
'infinitoBookmark': True,
|
|
'loaderClass': 'custom-loader'
|
|
}
|
|
}]
|
|
|
|
set_advanced_data_user(data)
|
|
```
|
|
"""
|
|
if args and 'vals' in args[0]:
|
|
vals = args[0]['vals']
|
|
request.env.user.write({
|
|
'is_sidebar_enabled': vals['sidebar'],
|
|
'is_fullscreen_enabled': vals['fullscreen'],
|
|
'is_sidebar_icon': vals['sidebarIcon'],
|
|
'is_sidebar_name': vals['sidebarName'],
|
|
'is_sidebar_company': vals['sidebarCompany'],
|
|
'is_sidebar_user': vals['sidebarUser'],
|
|
'is_recent_apps': vals['recentApps'],
|
|
'is_fullscreen_app': vals['fullScreenApp'],
|
|
'is_rtl': vals['infinitoRtl'],
|
|
'is_dark': vals['infinitoDark'],
|
|
'dark_mode': vals['infinitoDarkMode'],
|
|
'is_menu_bookmark': vals['infinitoBookmark'],
|
|
'loader_class': vals['loaderClass']
|
|
})
|
|
|
|
return True
|
|
|
|
@http.route(['/theme_studio/add_recent_app'], type="json")
|
|
def add_recent_app(self, args):
|
|
"""
|
|
Add a recent application to the user's recent apps list.
|
|
|
|
Parameters:
|
|
args (list): A list containing a dictionary with the 'appId' key
|
|
specifying the ID of the application.
|
|
|
|
Returns:
|
|
bool: True if the recent app is successfully added, otherwise False.
|
|
|
|
This function adds a recent application to the user's recent apps list
|
|
based on the provided application ID. It checks if the provided
|
|
arguments are valid and if the user has already reached the maximum
|
|
limit of recent apps (5), it removes the oldest one before adding the
|
|
new one.
|
|
|
|
Example:
|
|
To add a recent application with ID 123 to the user's recent apps
|
|
list:
|
|
|
|
```python
|
|
from my_theme_module import add_recent_app
|
|
|
|
add_recent_app([{'appId': 123}])
|
|
```
|
|
"""
|
|
if args and isinstance(args, list) and len(args) > 0:
|
|
app_id = args[0].get('appId')
|
|
if app_id is not None: # Check if appId exists
|
|
app_id = int(app_id)
|
|
else:
|
|
# Handle the case where appId is not provided
|
|
# You might want to raise an error, log a message, or handle
|
|
# it differently based on your requirements
|
|
# For now, I'll assign a default value of 0
|
|
app_id = 0
|
|
else:
|
|
# Handle the case where args is empty or not a list
|
|
# You might want to raise an error or handle it differently based
|
|
# on your requirements For now, I'll assign a default value of 0
|
|
app_id = 0
|
|
|
|
recent_app = request.env['recent.apps'].sudo()
|
|
exist = recent_app.search([
|
|
('app_id', '=', app_id),
|
|
('user_id', '=', request.env.user.id)
|
|
])
|
|
exist.unlink() if exist else None
|
|
total_recent = recent_app.search(
|
|
[('user_id', '=', request.env.user.id)])
|
|
if len(total_recent) > 4:
|
|
total_recent[0].unlink()
|
|
recent_app.create({
|
|
'app_id': app_id,
|
|
'user_id': request.env.user.id
|
|
})
|
|
|
|
@http.route(['/theme_studio/get_recent_apps'], type="json")
|
|
def get_recent_apps(self):
|
|
"""
|
|
Retrieve the list of recent applications for the current user.
|
|
|
|
Returns:
|
|
list: A list of dictionaries containing the recent applications'
|
|
information,
|
|
or an empty list if no recent apps are found.
|
|
|
|
This function retrieves the list of recent applications for the current
|
|
user from the database. It returns a list of dictionaries containing the
|
|
information of each recent application, such as its ID, name, and other
|
|
relevant details.
|
|
|
|
Example:
|
|
To retrieve the list of recent applications for the current user:
|
|
|
|
```python
|
|
from my_theme_module import get_recent_apps
|
|
|
|
recent_apps = get_recent_apps()
|
|
print(recent_apps)
|
|
```
|
|
"""
|
|
recent_app = request.env['recent.apps'].sudo()
|
|
return recent_app.search_read([
|
|
('user_id', '=', request.env.user.id)
|
|
])
|
|
|
|
@http.route(['/theme_studio/add_menu_bookmarks'], type="json")
|
|
def add_menu_bookmarks(self, args):
|
|
"""
|
|
Add a menu bookmark for the current user.
|
|
|
|
Parameters:
|
|
args (dict): A dictionary containing the menu data including
|
|
'actionId' and 'menuUrl'.
|
|
|
|
Returns:
|
|
bool: True if the menu bookmark is successfully added, otherwise
|
|
False.
|
|
|
|
This function adds a menu bookmark for the current user based on the
|
|
provided
|
|
menu data. It extracts the action ID and URL from the menu data and
|
|
creates a
|
|
new menu bookmark record in the database.
|
|
|
|
Example:
|
|
To add a menu bookmark with action ID 123 and URL '/dashboard' for
|
|
the current user:
|
|
|
|
```python
|
|
from my_theme_module import add_menu_bookmarks
|
|
|
|
menu_data = {
|
|
'actionId': 123,
|
|
'menuUrl': '/dashboard'
|
|
}
|
|
add_menu_bookmarks({'menu': menu_data})
|
|
```
|
|
"""
|
|
menu_data = args.get('menu', {})
|
|
action_id = menu_data.get('actionId')
|
|
user_id = request.env.user.id
|
|
url = menu_data.get('menuUrl')
|
|
menu_bookmark = request.env['infinito.menu.bookmark'].sudo()
|
|
menu_bookmark.create({
|
|
'action_id': int((action_id)),
|
|
'user_id': user_id,
|
|
'url': url,
|
|
})
|
|
|
|
@http.route(['/theme_studio/remove_menu_bookmarks'], type="json")
|
|
def remove_menu_bookmarks(self, args):
|
|
"""
|
|
Remove a menu bookmark for the current user.
|
|
|
|
Parameters:
|
|
args (dict): A dictionary containing the menu data including
|
|
'actionId'.
|
|
|
|
Returns:
|
|
bool: True if the menu bookmark is successfully removed,
|
|
otherwise False.
|
|
|
|
This function removes a menu bookmark for the current user based on
|
|
the provided
|
|
action ID. It searches for the menu bookmark record in the database and
|
|
deletes
|
|
it if found.
|
|
|
|
Example:
|
|
To remove a menu bookmark with action ID 123 for the current user:
|
|
|
|
```python
|
|
from my_theme_module import remove_menu_bookmarks
|
|
|
|
menu_data = {
|
|
'actionId': 123
|
|
}
|
|
remove_menu_bookmarks({'menu': menu_data})
|
|
```
|
|
"""
|
|
menu_data = args.get('menu', {})
|
|
action_id = menu_data.get('actionId')
|
|
user_id = request.env.user.id
|
|
menu_bookmark = request.env['infinito.menu.bookmark'].sudo().search([
|
|
('action_id', '=', int(action_id)),
|
|
('user_id', '=', user_id)
|
|
])
|
|
if menu_bookmark:
|
|
menu_bookmark.unlink()
|
|
|
|
@http.route(['/theme_studio/get_presets'], type="json")
|
|
def get_presets(self):
|
|
"""
|
|
Retrieve the list of available presets.
|
|
|
|
Returns:
|
|
list: A list of dictionaries containing the available presets.
|
|
|
|
This function reads the presets data from a JSON file and returns
|
|
it as a list of dictionaries.
|
|
Each dictionary in the list represents a preset with
|
|
its configuration details.
|
|
|
|
Example:
|
|
To retrieve the list of available presets:
|
|
|
|
```python
|
|
from my_theme_module import get_presets
|
|
|
|
presets = get_presets()
|
|
print(presets)
|
|
```
|
|
"""
|
|
working_dir = os.path.dirname(os.path.realpath(__file__))
|
|
working_dir = working_dir.replace('/controllers', '')
|
|
file_path = working_dir + '/static/src/json/presets.json'
|
|
file = open(file_path, 'r')
|
|
presets = json.load(file)
|
|
|
|
return presets
|
|
|