@ -0,0 +1,45 @@ |
|||
.. image:: https://img.shields.io/badge/license-LGPL--3-green.svg |
|||
:target: https://www.gnu.org/licenses/lgpl-3.0-standalone.html |
|||
:alt: License: LGPL-3 |
|||
|
|||
Backend Theme Infinito |
|||
====================== |
|||
* Backend Theme Infinito module for Odoo 17 community editions |
|||
|
|||
Installation |
|||
============ |
|||
- www.odoo.com/documentation/17.0/setup/install.html |
|||
- Install our custom addon |
|||
|
|||
License |
|||
------- |
|||
General Public License, Version 3 (LGPL v3). |
|||
(https://www.odoo.com/documentation/user/16.0/legal/licenses/licenses.html) |
|||
|
|||
Company |
|||
------- |
|||
* 'Cybrosys Techno Solutions <https://cybrosys.com/>'__ |
|||
|
|||
Credits |
|||
------- |
|||
* 'Cybrosys Techno Solutions <https://cybrosys.com/>'__ |
|||
Author : Farseen, Musthafa , Gokul , Ajith @Cybrosys |
|||
|
|||
Contacts |
|||
-------- |
|||
* Mail Contact : odoo@cybrosys.com |
|||
|
|||
Bug Tracker |
|||
----------- |
|||
Bugs are tracked on GitHub Issues. In case of trouble, please check there if your issue has already been reported. |
|||
|
|||
Maintainer |
|||
========== |
|||
This module is maintained by Cybrosys Technologies. |
|||
|
|||
For support and more information, please visit https://www.cybrosys.com |
|||
|
|||
Further information |
|||
=================== |
|||
HTML Description: `<static/description/index.html>`__ |
|||
|
@ -0,0 +1,24 @@ |
|||
# -*- 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/>. |
|||
# |
|||
############################################################################# |
|||
from .icon_hooks import icons_post_init_hook |
|||
from . import controllers |
|||
from . import models |
@ -0,0 +1,98 @@ |
|||
# -*- 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/>. |
|||
# |
|||
############################################################################# |
|||
{ |
|||
"name": "Infinito Backend Theme", |
|||
"description": """Utmost and dynamic backend theme for Odoo 17""", |
|||
"summary": """The Backend Theme Infinito Is A Dynamic And Ultimate Theme |
|||
For Your Odoo V17. This Theme Will Give You A New Experience With Odoo. |
|||
Main Highlight Of The Theme Is You Can Dynamically Change The Colors, |
|||
Views, Buttons, Different Types Sidebar...Etc""", |
|||
"category": "Themes/Backend", |
|||
"version": "17.0.1.0.0", |
|||
'author': 'Cybrosys Techno Solutions', |
|||
'company': 'Cybrosys Techno Solutions', |
|||
'maintainer': 'Cybrosys Techno Solutions', |
|||
'website': "https://www.cybrosys.com", |
|||
"depends": ['web', 'mail'], |
|||
"data": [ |
|||
'views/assets.xml', |
|||
'views/layout.xml', |
|||
'views/base_pwa.xml', |
|||
'views/icons.xml', |
|||
'views/editor_client_action.xml', |
|||
], |
|||
'assets': { |
|||
'web._assets_primary_variables': { |
|||
'backend_theme_infinito/static/src/scss/theme_variables.scss', |
|||
}, |
|||
'web.assets_backend': { |
|||
'/backend_theme_infinito/static/src/xml/systray.xml', |
|||
'/backend_theme_infinito/static/src/xml/views.xml', |
|||
'/backend_theme_infinito/static/src/xml/theme_editor.xml', |
|||
'/backend_theme_infinito/static/src/xml/studio_elements.xml', |
|||
'/backend_theme_infinito/static/src/xml/ThemeStudioMenu.xml', |
|||
'/backend_theme_infinito/static/src/xml/style_add.xml', |
|||
'/backend_theme_infinito/static/src/xml/sidebar.xml', |
|||
'/backend_theme_infinito/static/src/xml/sidebar_simple_editor.xml', |
|||
'/backend_theme_infinito/static/src/xml/MenuBookmark.xml', |
|||
'https://fonts.googleapis.com/css2?family=Poppins:wght@100;200;300;400;500;600;700;800;900&display=swap', |
|||
'/backend_theme_infinito/static/src/css/style.css', |
|||
'/backend_theme_infinito/static/src/css/loaders.css', |
|||
'backend_theme_infinito/static/src/scss/sidebar.scss', |
|||
'backend_theme_infinito/static/src/scss/responsive.scss', |
|||
'backend_theme_infinito/static/src/scss/theme_date_picker.scss', |
|||
'backend_theme_infinito/static/src/scss/theme_styles.scss', |
|||
'backend_theme_infinito/static/src/scss/theme_rtl.scss', |
|||
'backend_theme_infinito/static/src/scss/app_menu.scss', |
|||
'backend_theme_infinito/static/src/scss/extra_styles.scss', |
|||
'backend_theme_infinito/static/src/scss/views.scss', |
|||
'/backend_theme_infinito/static/src/js/checkService.js', |
|||
'/backend_theme_infinito/static/src/js/systray.js', |
|||
'/backend_theme_infinito/static/src/js/loaders.js', |
|||
'/backend_theme_infinito/static/src/js/editor_client_action.js', |
|||
'/backend_theme_infinito/static/src/js/ThemeStudioWidget.js', |
|||
'/backend_theme_infinito/static/src/js/Tool.js', |
|||
'/backend_theme_infinito/static/src/js/VisualEditor.js', |
|||
'/backend_theme_infinito/static/src/js/change.js', |
|||
'/backend_theme_infinito/static/src/js/style_add.js', |
|||
'/backend_theme_infinito/static/src/js/sidebar.js', |
|||
'/backend_theme_infinito/static/src/js/navbar.js', |
|||
'/backend_theme_infinito/static/src/js/theme_editor_sidebar.js', |
|||
'/backend_theme_infinito/static/src/js/recentApps.js', |
|||
'/backend_theme_infinito/static/src/js/editor_menu.js', |
|||
'/backend_theme_infinito/static/src/js/timepicker.js', |
|||
'/backend_theme_infinito/static/src/js/MenuBookmark.js', |
|||
'/backend_theme_infinito/static/src/js/SaveChanges.js', |
|||
'/backend_theme_infinito/static/src/js/ThemeStudioMenu.js', |
|||
'/backend_theme_infinito/static/src/js/variables.js', |
|||
}, |
|||
}, |
|||
'post_init_hook': 'icons_post_init_hook', |
|||
'images': [ |
|||
'static/description/banner.jpg', |
|||
'static/description/theme_screenshot.jpg', |
|||
], |
|||
'license': 'LGPL-3', |
|||
'installable': True, |
|||
'application': False, |
|||
'auto_install': False, |
|||
} |
@ -0,0 +1,23 @@ |
|||
# -*- 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/>. |
|||
# |
|||
############################################################################# |
|||
from . import base_pwa |
|||
from . import main |
@ -0,0 +1,68 @@ |
|||
# -*- 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 |
|||
from odoo import http |
|||
from odoo.http import request |
|||
|
|||
|
|||
class BasePwa(http.Controller): |
|||
""" |
|||
Controller class for handling Progressive Web App (PWA) related functionalities. |
|||
""" |
|||
|
|||
def pwa_data(self): |
|||
""" |
|||
Define the data required for the PWA manifest file. |
|||
|
|||
Returns: |
|||
dict: Dictionary containing PWA manifest data. |
|||
""" |
|||
return { |
|||
'short_name': 'Odoo', |
|||
'name': 'Odoo-infinito', |
|||
'description': 'PWA provided by backend theme infinito', |
|||
'icons': [ |
|||
{ |
|||
'src': '/backend_theme_infinito/static/src/img/menu.png', |
|||
'type': 'image/png', |
|||
'sizes': '144x144', |
|||
'purpose': 'any maskable' |
|||
}, |
|||
], |
|||
'start_url': 'http://cybrosys:8015/web', |
|||
'background_color': 'white', |
|||
'display': 'standalone', |
|||
'theme_color': 'white', |
|||
} |
|||
|
|||
@http.route('/manifest/webmanifest', type='http', |
|||
auth='public', website=True, sitemap=False) |
|||
def base_pwa_data(self): |
|||
""" |
|||
Endpoint for serving the PWA manifest file. |
|||
|
|||
Returns: |
|||
odoo.http.Response: HTTP response containing the PWA manifest data. |
|||
""" |
|||
return request.make_response(json.dumps(self.pwa_data()), |
|||
headers=[('Content-Type', |
|||
'application/json;charset=utf-8')]) |
@ -0,0 +1,585 @@ |
|||
# -*- 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 |
@ -0,0 +1,6 @@ |
|||
## Module <backend_theme_infinito> |
|||
|
|||
#### 03.10.2024 |
|||
#### Version 17.0.1.0.0 |
|||
#### ADD |
|||
Initial Commit for Infinito Backend Theme |
@ -0,0 +1,158 @@ |
|||
"""Hooks for Changing Menu Web_icon""" |
|||
# -*- 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 base64 |
|||
|
|||
from odoo import api, SUPERUSER_ID |
|||
from odoo.modules import get_module_resource |
|||
|
|||
|
|||
def icons_post_init_hook(cr): |
|||
"""post init hook for changing module icons""" |
|||
# env = api.Environment(cr, SUPERUSER_ID, {}) |
|||
menu_item = cr['ir.ui.menu'].search([('parent_id', '=', False)]) |
|||
|
|||
for menu in menu_item: |
|||
if menu.name == 'Contacts': |
|||
img_path = get_module_resource( |
|||
'backend_theme_infinito', 'static', 'src', 'img', 'icons', 'contact.png') |
|||
menu.write({'web_icon_data': base64.b64encode(open(img_path, "rb").read())}) |
|||
if menu.name == 'Link Tracker': |
|||
img_path = get_module_resource( |
|||
'backend_theme_infinito', 'static', 'src', 'img', 'icons', 'link-tracker.png') |
|||
menu.write({'web_icon_data': base64.b64encode(open(img_path, "rb").read())}) |
|||
if menu.name == 'Dashboards': |
|||
img_path = get_module_resource( |
|||
'backend_theme_infinito', 'static', 'src', 'img', 'icons', 'dashboard.png') |
|||
menu.write({'web_icon_data': base64.b64encode(open(img_path, "rb").read())}) |
|||
if menu.name == 'Sales': |
|||
img_path = get_module_resource( |
|||
'backend_theme_infinito', 'static', 'src', 'img', 'icons', 'sales.png') |
|||
menu.write({'web_icon_data': base64.b64encode(open(img_path, "rb").read())}) |
|||
if menu.name == 'Invoicing': |
|||
img_path = get_module_resource( |
|||
'backend_theme_infinito', 'static', 'src', 'img', 'icons', 'invoice.png') |
|||
menu.write({'web_icon_data': base64.b64encode(open(img_path, "rb").read())}) |
|||
if menu.name == 'Inventory': |
|||
img_path = get_module_resource( |
|||
'backend_theme_infinito', 'static', 'src', 'img', 'icons', 'inventory.png') |
|||
menu.write({'web_icon_data': base64.b64encode(open(img_path, "rb").read())}) |
|||
if menu.name == 'Purchase': |
|||
img_path = get_module_resource( |
|||
'backend_theme_infinito', 'static', 'src', 'img', 'icons', 'purchase.png') |
|||
menu.write({'web_icon_data': base64.b64encode(open(img_path, "rb").read())}) |
|||
if menu.name == 'Calendar': |
|||
img_path = get_module_resource( |
|||
'backend_theme_infinito', 'static', 'src', 'img', 'icons', 'calendar.png') |
|||
menu.write({'web_icon_data': base64.b64encode(open(img_path, "rb").read())}) |
|||
if menu.name == 'CRM': |
|||
img_path = get_module_resource( |
|||
'backend_theme_infinito', 'static', 'src', 'img', 'icons', 'crm.png') |
|||
menu.write({'web_icon_data': base64.b64encode(open(img_path, "rb").read())}) |
|||
if menu.name == 'Notes': |
|||
img_path = get_module_resource( |
|||
'backend_theme_infinito', 'static', 'src', 'img', 'icons', 'notes.png') |
|||
menu.write({'web_icon_data': base64.b64encode(open(img_path, "rb").read())}) |
|||
if menu.name == 'Website': |
|||
img_path = get_module_resource( |
|||
'backend_theme_infinito', 'static', 'src', 'img', 'icons', 'website.png') |
|||
menu.write({'web_icon_data': base64.b64encode(open(img_path, "rb").read())}) |
|||
if menu.name == 'Point of Sale': |
|||
img_path = get_module_resource( |
|||
'backend_theme_infinito', 'static', 'src', 'img', 'icons', 'pos.png') |
|||
menu.write({'web_icon_data': base64.b64encode(open(img_path, "rb").read())}) |
|||
if menu.name == 'Manufacturing': |
|||
img_path = get_module_resource( |
|||
'backend_theme_infinito', 'static', 'src', 'img', 'icons', 'manufacturing.png') |
|||
menu.write({'web_icon_data': base64.b64encode(open(img_path, "rb").read())}) |
|||
if menu.name == 'Repairs': |
|||
img_path = get_module_resource( |
|||
'backend_theme_infinito', 'static', 'src', 'img', 'icons', 'repairs.png') |
|||
menu.write({'web_icon_data': base64.b64encode(open(img_path, "rb").read())}) |
|||
if menu.name == 'Email Marketing': |
|||
img_path = get_module_resource( |
|||
'backend_theme_infinito', 'static', 'src', 'img', 'icons', 'marketing.png') |
|||
menu.write({'web_icon_data': base64.b64encode(open(img_path, "rb").read())}) |
|||
if menu.name == 'SMS Marketing': |
|||
img_path = get_module_resource( |
|||
'backend_theme_infinito', 'static', 'src', 'img', 'icons', 'sms-marketing.png') |
|||
menu.write({'web_icon_data': base64.b64encode(open(img_path, "rb").read())}) |
|||
if menu.name == 'Project': |
|||
img_path = get_module_resource( |
|||
'backend_theme_infinito', 'static', 'src', 'img', 'icons', 'project.png') |
|||
menu.write({'web_icon_data': base64.b64encode(open(img_path, "rb").read())}) |
|||
if menu.name == 'Surveys': |
|||
img_path = get_module_resource( |
|||
'backend_theme_infinito', 'static', 'src', 'img', 'icons', 'surveys.png') |
|||
menu.write({'web_icon_data': base64.b64encode(open(img_path, "rb").read())}) |
|||
if menu.name == 'Employees': |
|||
img_path = get_module_resource( |
|||
'backend_theme_infinito', 'static', 'src', 'img', 'icons', 'employees.png') |
|||
menu.write({'web_icon_data': base64.b64encode(open(img_path, "rb").read())}) |
|||
if menu.name == 'Recruitment': |
|||
img_path = get_module_resource( |
|||
'backend_theme_infinito', 'static', 'src', 'img', 'icons', 'recruitment.png') |
|||
menu.write({'web_icon_data': base64.b64encode(open(img_path, "rb").read())}) |
|||
if menu.name == 'Attendances': |
|||
img_path = get_module_resource( |
|||
'backend_theme_infinito', 'static', 'src', 'img', 'icons', 'attendance.png') |
|||
menu.write({'web_icon_data': base64.b64encode(open(img_path, "rb").read())}) |
|||
if menu.name == 'Time Off': |
|||
img_path = get_module_resource( |
|||
'backend_theme_infinito', 'static', 'src', 'img', 'icons', 'time-off.png') |
|||
menu.write({'web_icon_data': base64.b64encode(open(img_path, "rb").read())}) |
|||
if menu.name == 'Expenses': |
|||
img_path = get_module_resource( |
|||
'backend_theme_infinito', 'static', 'src', 'img', 'icons', 'expense.png') |
|||
menu.write({'web_icon_data': base64.b64encode(open(img_path, "rb").read())}) |
|||
if menu.name == 'Maintenance': |
|||
img_path = get_module_resource( |
|||
'backend_theme_infinito', 'static', 'src', 'img', 'icons', 'maintenance.png') |
|||
menu.write({'web_icon_data': base64.b64encode(open(img_path, "rb").read())}) |
|||
if menu.name == 'Live Chat': |
|||
img_path = get_module_resource( |
|||
'backend_theme_infinito', 'static', 'src', 'img', 'icons', 'live-chat.png') |
|||
menu.write({'web_icon_data': base64.b64encode(open(img_path, "rb").read())}) |
|||
if menu.name == 'Lunch': |
|||
img_path = get_module_resource( |
|||
'backend_theme_infinito', 'static', 'src', 'img', 'icons', 'lunch.png') |
|||
menu.write({'web_icon_data': base64.b64encode(open(img_path, "rb").read())}) |
|||
if menu.name == 'Fleet': |
|||
img_path = get_module_resource( |
|||
'backend_theme_infinito', 'static', 'src', 'img', 'icons', 'fleet.png') |
|||
menu.write({'web_icon_data': base64.b64encode(open(img_path, "rb").read())}) |
|||
if menu.name == 'Timesheets': |
|||
img_path = get_module_resource( |
|||
'backend_theme_infinito', 'static', 'src', 'img', 'icons', 'timesheets.png') |
|||
menu.write({'web_icon_data': base64.b64encode(open(img_path, "rb").read())}) |
|||
if menu.name == 'Events': |
|||
img_path = get_module_resource( |
|||
'backend_theme_infinito', 'static', 'src', 'img', 'icons', 'events.png') |
|||
menu.write({'web_icon_data': base64.b64encode(open(img_path, "rb").read())}) |
|||
if menu.name == 'eLearning': |
|||
img_path = get_module_resource( |
|||
'backend_theme_infinito', 'static', 'src', 'img', 'icons', 'elearning.png') |
|||
menu.write({'web_icon_data': base64.b64encode(open(img_path, "rb").read())}) |
|||
if menu.name == 'Members': |
|||
img_path = get_module_resource( |
|||
'backend_theme_infinito', 'static', 'src', 'img', 'icons', 'members.png') |
|||
menu.write({'web_icon_data': base64.b64encode(open(img_path, "rb").read())}) |
@ -0,0 +1,26 @@ |
|||
# -*- 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/>. |
|||
# |
|||
############################################################################# |
|||
from . import ir_http |
|||
from . import menu_bookmark |
|||
from . import recent_apps |
|||
from . import res_config_settings |
|||
from . import res_users |
@ -0,0 +1,120 @@ |
|||
# -*- 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/>. |
|||
# |
|||
############################################################################# |
|||
from odoo import models |
|||
|
|||
|
|||
def float_to_time(time): |
|||
""" |
|||
Convert a floating-point number representing time in hours to a string |
|||
formatted as 'HH:MM'. |
|||
|
|||
Parameters: |
|||
- time (float): The time in hours to be converted. |
|||
|
|||
Returns: |
|||
- str: A string representing the time in the format 'HH:MM'. |
|||
""" |
|||
return '{0:02.0f}:{1:02.0f}'.format(*divmod(float(time) * 60, 60)) |
|||
|
|||
|
|||
class IrHttp(models.AbstractModel): |
|||
""" |
|||
Extends the 'ir.http' model to customize session information for the |
|||
web client. |
|||
""" |
|||
_inherit = 'ir.http' |
|||
|
|||
def session_info(self): |
|||
""" |
|||
Overrides the default session_info method to customize session |
|||
information based on user preferences. |
|||
|
|||
Returns: |
|||
- dict: A dictionary containing session information customized for |
|||
the current user. |
|||
""" |
|||
res = super(IrHttp, self).session_info() |
|||
get_param = self.env['ir.config_parameter'].sudo().get_param |
|||
if self.env.user.has_group('base.group_user'): |
|||
user_edit = get_param( |
|||
'backend_theme_infinito.is_user_edit', default=False) |
|||
res['userEdit'] = user_edit |
|||
if user_edit: |
|||
res['sidebar'] = self.env.user.is_sidebar_enabled |
|||
res['fullscreen'] = self.env.user.is_fullscreen_enabled |
|||
res['sidebarIcon'] = self.env.user.is_sidebar_icon |
|||
res['sidebarName'] = self.env.user.is_sidebar_name |
|||
res['sidebarCompany'] = self.env.user.is_sidebar_company |
|||
res['sidebarUser'] = self.env.user.is_sidebar_user |
|||
res['recentApps'] = self.env.user.is_recent_apps |
|||
res['fullScreenApp'] = self.env.user.is_fullscreen_app |
|||
res['infinitoRtl'] = self.env.user.is_rtl |
|||
res['infinitoDark'] = self.env.user.is_dark |
|||
res['infinitoDarkMode'] = self.env.user.dark_mode |
|||
res['infinitoDarkStart'] = float_to_time( |
|||
self.env.user.dark_start) |
|||
res['infinitoDarkEnd'] = float_to_time(self.env.user.dark_end) |
|||
res['infinitoBookmark'] = self.env.user.is_menu_bookmark |
|||
res['loaderClass'] = self.env.user.loader_class |
|||
else: |
|||
res['sidebar'] = get_param( |
|||
'backend_theme_infinito.is_sidebar_enabled', default=False) |
|||
res['fullscreen'] = get_param( |
|||
'backend_theme_infinito.is_fullscreen_enabled', |
|||
default=False) |
|||
res['sidebarIcon'] = get_param( |
|||
'backend_theme_infinito.is_sidebar_icon', default=False) |
|||
res['sidebarName'] = get_param( |
|||
'backend_theme_infinito.is_sidebar_name', default=False) |
|||
res['sidebarCompany'] = get_param( |
|||
'backend_theme_infinito.is_sidebar_company', default=False) |
|||
res['sidebarUser'] = get_param( |
|||
'backend_theme_infinito.is_sidebar_user', default=False) |
|||
res['recentApps'] = get_param( |
|||
'backend_theme_infinito.is_recent_apps', default=False) |
|||
res['fullScreenApp'] = get_param( |
|||
'backend_theme_infinito.is_fullscreen_app', default=False) |
|||
res['infinitoRtl'] = get_param( |
|||
'backend_theme_infinito.is_rtl', default=False) |
|||
res['infinitoDark'] = get_param( |
|||
'backend_theme_infinito.is_dark', default=False) |
|||
res['infinitoDarkMode'] = get_param( |
|||
'backend_theme_infinito.dark_mode', default=False) |
|||
res['infinitoDarkStart'] = float_to_time(get_param( |
|||
'backend_theme_infinito.dark_start', default=19.0)) |
|||
res['infinitoDarkEnd'] = float_to_time(get_param( |
|||
'backend_theme_infinito.dark_end', default=5.0)) |
|||
res['infinitoBookmark'] = get_param( |
|||
'backend_theme_infinito.is_menu_bookmark', default=False) |
|||
res['loaderClass'] = get_param( |
|||
'backend_theme_infinito.loader_class', default=False) |
|||
menu_bookmark = self.env['infinito.menu.bookmark'].sudo(). \ |
|||
search([('user_id', '=', self.env.user.id)]) |
|||
list_bookmark = [] |
|||
for bookmark in menu_bookmark: |
|||
bkm = bookmark.read(['action_id', 'url', 'name'])[0] |
|||
bkm['short_name'] = bkm['name'][:2].upper() |
|||
list_bookmark.append(bkm) |
|||
res['infinitoBookmarks'] = menu_bookmark.action_id.ids |
|||
res['infinitoMenuBookmarks'] = list_bookmark |
|||
|
|||
return res |
@ -0,0 +1,35 @@ |
|||
# -*- 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/>. |
|||
# |
|||
############################################################################# |
|||
from odoo import fields, models |
|||
|
|||
|
|||
class InfinitoMenuBookmark(models.Model): |
|||
""" |
|||
Model representing bookmarks for menu items in the Infinito system. |
|||
""" |
|||
_name = 'infinito.menu.bookmark' |
|||
_description = 'Menu Bookmark' |
|||
|
|||
name = fields.Char(related='action_id.name') |
|||
action_id = fields.Many2one('ir.actions.act_window') |
|||
url = fields.Text('Url') |
|||
user_id = fields.Many2one('res.users') |
@ -0,0 +1,81 @@ |
|||
# -*- 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/>. |
|||
# |
|||
############################################################################# |
|||
from odoo import api,fields, models |
|||
|
|||
|
|||
class RecentApps(models.Model): |
|||
""" |
|||
Model representing recent applications accessed by users in the system. |
|||
""" |
|||
|
|||
_name = 'recent.apps' |
|||
_description = 'Recent Apps' |
|||
|
|||
name = fields.Char(compute='_compute_icon', store=True) |
|||
app_id = fields.Integer() |
|||
icon = fields.Binary(compute='_compute_icon', store=True) |
|||
type = fields.Char(compute='_compute_type', store=True) |
|||
user_id = fields.Many2one('res.users') |
|||
|
|||
@api.depends('app_id') |
|||
def _compute_type(self): |
|||
""" |
|||
Compute method to determine the type of the recent app based on its |
|||
associated menu. |
|||
|
|||
This method computes the type of the recent app by retrieving its |
|||
associated menu, |
|||
extracting the type information from the menu's web icon, and |
|||
updating the 'type' |
|||
field accordingly. |
|||
|
|||
""" |
|||
menu_ui = self.env['ir.ui.menu'] |
|||
for rec in self: |
|||
app = menu_ui.browse(rec.app_id) |
|||
la = str(app.web_icon) |
|||
spl_word = '.' |
|||
res = la.split(spl_word, 1) |
|||
if len(res) > 1: |
|||
splitString = res[1] |
|||
rec.type = splitString |
|||
else: |
|||
# Handle the case where splitting doesn't result in two parts |
|||
# You might want to set a default value or raise an error, depending on your requirements |
|||
# For now, I'll set rec.type to an empty string |
|||
rec.type = "" |
|||
|
|||
@api.depends('app_id') |
|||
def _compute_icon(self): |
|||
""" |
|||
Compute method to determine the icon for the recent app based on its associated menu. |
|||
|
|||
This method computes the icon for the recent app by retrieving its associated menu, |
|||
extracting the icon information from the menu's web icon, and updating the 'icon' |
|||
field accordingly. It also updates the 'name' field with the name of the associated menu. |
|||
|
|||
""" |
|||
menu_ui = self.env['ir.ui.menu'] |
|||
for rec in self: |
|||
app = menu_ui.browse(rec.app_id) |
|||
rec.icon = app._compute_web_icon_data(app.web_icon) |
|||
rec.name = app.name |
@ -0,0 +1,151 @@ |
|||
# -*- 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/>. |
|||
# |
|||
############################################################################# |
|||
from odoo import api, fields, models |
|||
|
|||
|
|||
class ResConfigSettings(models.TransientModel): |
|||
""" |
|||
Model representing configuration settings for the system. |
|||
|
|||
This model allows the administrator to configure various settings |
|||
related to the user interface and system behavior. |
|||
""" |
|||
|
|||
_inherit = 'res.config.settings' |
|||
|
|||
is_user_edit = fields.Boolean('User edit', default=False) |
|||
is_sidebar_enabled = fields.Boolean('Sidebar Enabled', default=False) |
|||
is_fullscreen_enabled = fields.Boolean('Full screen Enabled', default=False) |
|||
is_sidebar_icon = fields.Boolean('Sidebar icon Enabled', default=True) |
|||
is_sidebar_name = fields.Boolean('Sidebar name Enabled', default=True) |
|||
is_sidebar_company = fields.Boolean('Sidebar Company Enabled', |
|||
default=False) |
|||
is_sidebar_user = fields.Boolean('Sidebar User Enabled', default=False) |
|||
is_recent_apps = fields.Boolean('Recent Apps Enabled', default=False) |
|||
is_fullscreen_app = fields.Boolean('Full screen Apps Enabled', |
|||
default=False) |
|||
is_rtl = fields.Boolean('Rtl Enabled', default=False) |
|||
is_dark = fields.Boolean('Dark mode Enabled', default=False) |
|||
is_menu_bookmark = fields.Boolean('Menu Bookmark mode Enabled', |
|||
default=False) |
|||
is_chameleon = fields.Boolean('Chameleon mode Enabled', default=False) |
|||
dark_mode = fields.Selection([ |
|||
('all', 'All'), |
|||
('schedule', 'Schedule'), |
|||
('auto', 'Automatic'), |
|||
], default='all') |
|||
dark_start = fields.Float('Dark Start', default=19.0) |
|||
dark_end = fields.Float('Dark End', default=5.0) |
|||
loader_class = fields.Char('Loader', default='default') |
|||
|
|||
@api.model |
|||
def get_values(self): |
|||
""" |
|||
Retrieve the current configuration values for the system. |
|||
|
|||
This method retrieves the current configuration values for the system, |
|||
including settings related to the user interface and system behavior. |
|||
|
|||
Returns: |
|||
- dict: A dictionary containing the current configuration values. |
|||
""" |
|||
res = super(ResConfigSettings, self).get_values() |
|||
get_param = self.env['ir.config_parameter'].sudo().get_param |
|||
res['is_user_edit'] = get_param('backend_theme_infinito.is_user_edit', |
|||
default=False) |
|||
res['is_sidebar_enabled'] = get_param( |
|||
'backend_theme_infinito.is_sidebar_enabled', default=False) |
|||
res['is_fullscreen_enabled'] = get_param( |
|||
'backend_theme_infinito.is_fullscreen_enabled', default=False) |
|||
res['is_sidebar_icon'] = get_param( |
|||
'backend_theme_infinito.is_sidebar_icon', default=False) |
|||
res['is_sidebar_name'] = get_param( |
|||
'backend_theme_infinito.is_sidebar_name', default=False) |
|||
res['is_sidebar_company'] = get_param( |
|||
'backend_theme_infinito.is_sidebar_company', default=False) |
|||
res['is_sidebar_user'] = get_param( |
|||
'backend_theme_infinito.is_sidebar_user', default=False) |
|||
res['is_recent_apps'] = get_param( |
|||
'backend_theme_infinito.is_recent_apps', default=False) |
|||
res['is_rtl'] = get_param('backend_theme_infinito.is_rtl', |
|||
default=False) |
|||
res['is_dark'] = get_param('backend_theme_infinito.is_dark', |
|||
default=False) |
|||
res['is_menu_bookmark'] = get_param( |
|||
'backend_theme_infinito.is_menu_bookmark', default=False) |
|||
res['dark_mode'] = get_param('backend_theme_infinito.dark_mode', |
|||
default='all') |
|||
res['dark_start'] = get_param('backend_theme_infinito.dark_start', |
|||
default=19.0) |
|||
res['dark_end'] = get_param('backend_theme_infinito.dark_end', |
|||
default=5.0) |
|||
res['loader_class'] = get_param('backend_theme_infinito.loader_class', |
|||
default=5.0) |
|||
res['is_chameleon'] = get_param('backend_theme_infinito.is_chameleon', |
|||
default=False) |
|||
|
|||
return res |
|||
|
|||
@api.model |
|||
def set_values(self): |
|||
""" |
|||
Update the configuration values for the system. |
|||
|
|||
This method updates the configuration values for the system based on the current |
|||
settings provided by the administrator. |
|||
|
|||
""" |
|||
set_param = self.env['ir.config_parameter'].sudo().set_param |
|||
set_param('backend_theme_infinito.is_user_edit', |
|||
self.is_user_edit) |
|||
set_param('backend_theme_infinito.is_sidebar_enabled', |
|||
self.is_sidebar_enabled) |
|||
set_param('backend_theme_infinito.is_fullscreen_enabled', |
|||
self.is_fullscreen_enabled) |
|||
set_param('backend_theme_infinito.is_sidebar_icon', |
|||
self.is_sidebar_icon) |
|||
set_param('backend_theme_infinito.is_sidebar_name', |
|||
self.is_sidebar_name) |
|||
set_param('backend_theme_infinito.is_sidebar_company', |
|||
self.is_sidebar_company) |
|||
set_param('backend_theme_infinito.is_sidebar_user', |
|||
self.is_sidebar_user) |
|||
set_param('backend_theme_infinito.is_recent_apps', |
|||
self.is_recent_apps) |
|||
set_param('backend_theme_infinito.is_rtl', |
|||
self.is_rtl) |
|||
set_param('backend_theme_infinito.is_dark', |
|||
self.is_dark) |
|||
set_param('backend_theme_infinito.dark_mode', |
|||
self.dark_mode) |
|||
set_param('backend_theme_infinito.dark_start', |
|||
self.dark_start) |
|||
set_param('backend_theme_infinito.dark_end', |
|||
self.dark_end) |
|||
set_param('backend_theme_infinito.is_menu_bookmark', |
|||
self.is_menu_bookmark) |
|||
set_param('backend_theme_infinito.loader_class', |
|||
self.loader_class) |
|||
set_param('backend_theme_infinito.is_chameleon', |
|||
self.is_chameleon) |
|||
|
|||
super(ResConfigSettings, self).set_values() |
@ -0,0 +1,56 @@ |
|||
# -*- 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/>. |
|||
# |
|||
############################################################################# |
|||
from odoo import fields, models |
|||
|
|||
|
|||
class ResUser(models.Model): |
|||
""" |
|||
Model representing system users with additional customization options. |
|||
|
|||
This model extends the base 'res.users' model to include additional fields |
|||
for customizing user interface preferences and system behavior. |
|||
""" |
|||
_inherit = 'res.users' |
|||
|
|||
is_sidebar_enabled = fields.Boolean('Sidebar Enabled', default=False) |
|||
is_fullscreen_enabled = fields.Boolean('Full screen Enabled', default=False) |
|||
is_sidebar_icon = fields.Boolean('Sidebar icon Enabled', default=True) |
|||
is_sidebar_name = fields.Boolean('Sidebar name Enabled', default=True) |
|||
is_sidebar_company = fields.Boolean('Sidebar Company Enabled', |
|||
default=False) |
|||
is_sidebar_user = fields.Boolean('Sidebar User Enabled', default=False) |
|||
is_recent_apps = fields.Boolean('Recent Apps Enabled', default=False) |
|||
is_fullscreen_app = fields.Boolean('Full screen Apps Enabled', |
|||
default=False) |
|||
is_rtl = fields.Boolean('Rtl Enabled', default=False) |
|||
is_dark = fields.Boolean('Dark mode Enabled', default=False) |
|||
is_menu_bookmark = fields.Boolean('Menu Bookmark mode Enabled', |
|||
default=False) |
|||
is_chameleon = fields.Boolean('Chameleon mode Enabled', default=False) |
|||
dark_mode = fields.Selection([ |
|||
('all', 'All'), |
|||
('schedule', 'Schedule'), |
|||
('auto', 'Automatic'), |
|||
], default='all') |
|||
dark_start = fields.Float('Dark Start', default=19.0) |
|||
dark_end = fields.Float('Dark End', default=5.0) |
|||
loader_class = fields.Char('Loader', default='default') |
After Width: | Height: | Size: 349 KiB |
After Width: | Height: | Size: 3.6 KiB |
After Width: | Height: | Size: 310 B |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 576 B |
After Width: | Height: | Size: 733 B |
After Width: | Height: | Size: 911 B |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 673 B |
After Width: | Height: | Size: 878 B |
After Width: | Height: | Size: 653 B |
After Width: | Height: | Size: 905 B |
After Width: | Height: | Size: 839 B |
After Width: | Height: | Size: 427 B |
After Width: | Height: | Size: 627 B |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 988 B |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 258 KiB |
After Width: | Height: | Size: 214 KiB |
After Width: | Height: | Size: 197 KiB |
After Width: | Height: | Size: 16 MiB |
After Width: | Height: | Size: 11 MiB |
After Width: | Height: | Size: 6.6 MiB |
After Width: | Height: | Size: 3.4 MiB |
After Width: | Height: | Size: 2.1 MiB |
After Width: | Height: | Size: 8.8 MiB |
After Width: | Height: | Size: 10 MiB |
After Width: | Height: | Size: 27 MiB |
After Width: | Height: | Size: 4.9 MiB |
After Width: | Height: | Size: 2.8 MiB |
After Width: | Height: | Size: 6.6 MiB |
After Width: | Height: | Size: 174 KiB |
After Width: | Height: | Size: 153 KiB |
After Width: | Height: | Size: 174 KiB |
After Width: | Height: | Size: 239 KiB |
After Width: | Height: | Size: 10 KiB |
After Width: | Height: | Size: 15 KiB |
After Width: | Height: | Size: 266 KiB |
After Width: | Height: | Size: 10 KiB |
After Width: | Height: | Size: 197 KiB |
After Width: | Height: | Size: 221 KiB |
After Width: | Height: | Size: 187 KiB |
After Width: | Height: | Size: 1.7 MiB |
After Width: | Height: | Size: 1012 KiB |
After Width: | Height: | Size: 202 KiB |
After Width: | Height: | Size: 149 KiB |
After Width: | Height: | Size: 197 KiB |
After Width: | Height: | Size: 166 KiB |
After Width: | Height: | Size: 179 KiB |
After Width: | Height: | Size: 22 KiB |
After Width: | Height: | Size: 406 KiB |
@ -0,0 +1,2 @@ |
|||
/* This Styles are generated automatically by Theme Studio */ |
|||
. {background-color: rgb(250, 0, 0) !important;} |
@ -0,0 +1,34 @@ |
|||
.o_web_client { |
|||
background-color: var(--bg_white); |
|||
} |
|||
|
|||
.o_web_client > header { |
|||
margin: 10px 10px 0px 10px; |
|||
border-radius: 10px; |
|||
box-shadow: 1px 1px 15px 0; |
|||
} |
|||
|
|||
.o_main_navbar { |
|||
border-radius: 10px; |
|||
} |
|||
|
|||
.o_web_client > .o_action_manager, #sidebar_panel, .oe_kanban_card { |
|||
margin: 15px; |
|||
border-radius: 10px; |
|||
box-shadow: 1px 1px 15px 0; |
|||
padding: 15px; |
|||
background-color: var(--bg_white); |
|||
} |
|||
|
|||
#sidebar_panel { |
|||
margin-left: 0px; |
|||
background-color: var(--nav_bar_color); |
|||
} |
|||
|
|||
.o_main_navbar .o_navbar_apps_menu.show .o-dropdown--menu dropdown-menu { |
|||
border-radius: 10px 0 10px 10px; |
|||
} |
|||
|
|||
.oe_kanban_card { |
|||
box-shadow: none; |
|||
} |
@ -0,0 +1,128 @@ |
|||
/* Rotation */ |
|||
|
|||
.rotating::before{ |
|||
content: " "; |
|||
position: absolute; |
|||
top: calc(50% - 25px); |
|||
left: calc(50% - 25px); |
|||
height: 50px; |
|||
width: 50px; |
|||
background-image: linear-gradient(to bottom,black 50%, white 50%); |
|||
border-radius: 50%; |
|||
animation: rotate 1s; |
|||
animation-iteration-count: infinite; |
|||
} |
|||
|
|||
.rotating::after{ |
|||
content: " "; |
|||
position: absolute; |
|||
top: calc(50% - 20px); |
|||
left: calc(50% - 20px); |
|||
height: 40px; |
|||
width: 40px; |
|||
background-color: #020202; |
|||
border-radius: 50%; |
|||
} |
|||
|
|||
/* Blinking */ |
|||
|
|||
.blinking::after{ |
|||
content: " "; |
|||
position: absolute; |
|||
top: calc(50% - 25px); |
|||
left: calc(50% - 25px); |
|||
height: 50px; |
|||
width: 50px; |
|||
border-radius: 50%; |
|||
background-color:#fff; |
|||
animation: blink 2000ms ease infinite; |
|||
} |
|||
|
|||
.blinking-2::after{ |
|||
background-color: transparent; |
|||
border: 2px solid white; |
|||
transform: scale(0); |
|||
animation: blink-2 2000ms ease-out infinite,blink-2a 2s ease-out infinite; |
|||
animation-fill-mode: forwards; |
|||
} |
|||
|
|||
.bounce::after{ |
|||
content: " "; |
|||
position: absolute; |
|||
top: calc(50% - 50px); |
|||
left: calc(50% - 25px); |
|||
height: 50px; |
|||
width: 50px; |
|||
box-sizing: border-box; |
|||
background-image: linear-gradient(to bottom,rgb(255, 255, 255) 60%, rgb(235, 235, 235) 80%, rgb(210, 210, 210)); |
|||
border-radius: 50%; |
|||
animation: bounce 2s; |
|||
animation-iteration-count: infinite; |
|||
} |
|||
|
|||
.ring:after { |
|||
content: " "; |
|||
display: block; |
|||
width: 64px; |
|||
height: 64px; |
|||
margin: 8px; |
|||
border-radius: 50%; |
|||
border: 6px solid #fff; |
|||
border-color: #fff transparent #fff transparent; |
|||
animation: ring 1.2s linear infinite; |
|||
} |
|||
|
|||
|
|||
/* Key frames */ |
|||
|
|||
@keyframes rotate{ |
|||
from{ |
|||
transform: rotate(0deg); |
|||
} |
|||
to{ |
|||
transform: rotate(360deg); |
|||
} |
|||
} |
|||
|
|||
@keyframes blink{ |
|||
0%{ |
|||
transform: scale(1); |
|||
} |
|||
50%{ |
|||
transform: scale(1.5); |
|||
} |
|||
100%{ |
|||
transform: scale(1); |
|||
} |
|||
} |
|||
|
|||
@keyframes blink-2{ |
|||
0%{ |
|||
transform: scale(0.2); |
|||
} |
|||
100%{ |
|||
transform: scale(1.5); |
|||
} |
|||
} |
|||
|
|||
@keyframes bounce{ |
|||
0%,100%{ |
|||
transform: translateY(0); |
|||
height: 50px; |
|||
width: 50px; |
|||
} |
|||
50%{ |
|||
transform: translateY(50px); |
|||
height: 45px; |
|||
width: 55px; |
|||
} |
|||
} |
|||
|
|||
@keyframes ring { |
|||
0% { |
|||
transform: rotate(0deg); |
|||
} |
|||
100% { |
|||
transform: rotate(360deg); |
|||
} |
|||
} |
After Width: | Height: | Size: 274 B |
After Width: | Height: | Size: 260 B |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 324 B |
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 2.4 KiB |
After Width: | Height: | Size: 4.9 KiB |
After Width: | Height: | Size: 527 B |
After Width: | Height: | Size: 10 KiB |
After Width: | Height: | Size: 506 B |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 3.7 KiB |
After Width: | Height: | Size: 29 KiB |
After Width: | Height: | Size: 30 KiB |
After Width: | Height: | Size: 35 KiB |
After Width: | Height: | Size: 40 KiB |
After Width: | Height: | Size: 44 KiB |
After Width: | Height: | Size: 42 KiB |
After Width: | Height: | Size: 25 KiB |
After Width: | Height: | Size: 28 KiB |
After Width: | Height: | Size: 48 KiB |
After Width: | Height: | Size: 25 KiB |