From ec4186a53d173945ca77485bc352f6949d3735cc Mon Sep 17 00:00:00 2001 From: Ivan Office Date: Sun, 26 Jan 2025 00:42:35 +0800 Subject: [PATCH] =?UTF-8?q?opt=20app=5Fcommon=20=E5=8F=8Aapp=5Fsaas=20?= =?UTF-8?q?=E7=9B=B8=E5=85=B3=EF=BC=8C=E4=BD=86=E6=B2=A1=E6=A3=80=E6=9F=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app_common/models/__init__.py | 2 +- app_common/models/app_import.py | 25 +- app_common/models/base.py | 74 +++++- app_common/models/ir_mail_server.py | 4 +- app_common/models/ir_ui_view.py | 21 +- app_common/models/mail_mail.py | 9 +- app_common/models/res_config_settings.py | 15 ++ .../views/res_config_settings_views.xml | 75 ++++++ app_odoo_customize/__manifest__.py | 3 +- .../views/app_theme_config_settings_views.xml | 231 ------------------ .../views/res_config_settings_views.xml | 227 +++++++++++++++++ app_saas/models/res_config_settings.py | 3 + app_saas/models/res_users.py | 18 +- 13 files changed, 421 insertions(+), 286 deletions(-) create mode 100644 app_common/models/res_config_settings.py create mode 100644 app_common/views/res_config_settings_views.xml delete mode 100644 app_odoo_customize/views/app_theme_config_settings_views.xml diff --git a/app_common/models/__init__.py b/app_common/models/__init__.py index 534ba2b7..690ea6e6 100644 --- a/app_common/models/__init__.py +++ b/app_common/models/__init__.py @@ -25,12 +25,12 @@ from . import base from . import ir_ui_view from . import ir_cron -from . import res_users from . import ir_mail_server from . import mail_mail from . import ir_http from . import app_import from . import res_partner +from . import res_config_settings diff --git a/app_common/models/app_import.py b/app_common/models/app_import.py index 9dff9512..82c936cb 100644 --- a/app_common/models/app_import.py +++ b/app_common/models/app_import.py @@ -1,12 +1,14 @@ # -*- coding: utf-8 -*- +import base64 +import io +import csv import os.path from odoo import api, fields, models, modules, tools, SUPERUSER_ID, _ -from odoo.tests import common -ADMIN_USER_ID = common.ADMIN_USER_ID +from odoo.tools import pycompat -def app_quick_import(env, content_path, sep=None, context={}): +def app_quick_import(env, content_path, sep=None): if not sep: sep = '/' dir_split = content_path.split(sep) @@ -24,8 +26,7 @@ def app_quick_import(env, content_path, sep=None, context={}): file_type = 'text/csv' elif file_type in ['.xls', '.xlsx']: file_type = 'application/vnd.ms-excel' - import_wizard = env['base_import.import'].with_context(context) - import_wizard = import_wizard.create({ + import_wizard = env['base_import.import'].create({ 'res_model': model_name, 'file_name': file_name, 'file_type': file_type, @@ -41,12 +42,8 @@ def app_quick_import(env, content_path, sep=None, context={}): preview = import_wizard.parse_preview({ 'has_headers': True, }) - else: - preview = False - - if preview: - import_wizard.execute_import( - preview["headers"], - preview["headers"], - preview["options"] - ) + result = import_wizard.execute_import( + preview["headers"], + preview["headers"], + preview["options"] + ) diff --git a/app_common/models/base.py b/app_common/models/base.py index f9aa597c..adde02be 100644 --- a/app_common/models/base.py +++ b/app_common/models/base.py @@ -1,18 +1,19 @@ # -*- coding: utf-8 -*- -from odoo import models, fields, api, _ -from odoo.tools import DEFAULT_SERVER_DATE_FORMAT, DEFAULT_SERVER_DATETIME_FORMAT -from odoo.http import request import requests import base64 from io import BytesIO import uuid - +from PIL import Image from datetime import date, datetime, time import pytz import logging +from odoo import models, fields, api, _ +from odoo.tools import DEFAULT_SERVER_DATE_FORMAT, DEFAULT_SERVER_DATETIME_FORMAT +from odoo.http import request + _logger = logging.getLogger(__name__) # 常规的排除的fields @@ -52,7 +53,7 @@ EXCLU_FIELDS = [ class Base(models.AbstractModel): _inherit = 'base' - + @api.model def _app_check_sys_op(self): if self.env.user.has_group('base.group_erp_manager'): @@ -75,7 +76,10 @@ class Base(models.AbstractModel): else: if not domain: domain = self._fields[fieldname].domain or [] - rec = self.env[self._fields[fieldname].comodel_name].sudo().search(domain, limit=1) + try: + rec = self.env[self._fields[fieldname].comodel_name].search(domain, limit=1) + except Exception as e: + rec = self.env[self._fields[fieldname].comodel_name].search([], limit=1) return rec.id if rec else False return False @@ -127,6 +131,7 @@ class Base(models.AbstractModel): 'website_id': False, 'res_model': self._name, 'res_id': self.id, + 'public': True, }) attachment.generate_access_token() return attachment @@ -149,6 +154,7 @@ class Base(models.AbstractModel): 'website_id': False, 'res_model': self._name, 'res_id': self.id, + 'public': True, }) attachment.generate_access_token() return attachment @@ -157,11 +163,33 @@ class Base(models.AbstractModel): return False else: return False + + @api.model + def _get_video_url2attachment(self, url): + if not self._app_check_sys_op(): + return False + video, file_name = get_video_url2attachment(url) + if video and file_name: + try: + attachment = self.env['ir.attachment'].create({ + 'datas': video, + 'name': file_name, + 'website_id': False, + 'res_model': self._name, + 'res_id': self.id, + 'public': True, + }) + attachment.generate_access_token() + return attachment + except Exception as e: + _logger.error('get_video_url2attachment error: %s' % str(e)) + return False + else: + return False def get_ua_type(self): return get_ua_type() - def get_image_from_url(url): if not url: return None @@ -179,7 +207,7 @@ def get_image_url2attachment(url): try: if url.startswith('//'): url = 'https:%s' % url - response = requests.get(url, timeout=30) + response = requests.get(url, timeout=90) except Exception as e: return None, None # 返回这个图片的base64编码 @@ -193,11 +221,31 @@ def get_image_base642attachment(data): return None try: image_data = data.split(',')[1] - file_name = str(uuid.uuid4()) + '.png' - return image_data, file_name + img = Image.open(BytesIO(base64.b64decode(image_data))) + img = img.convert('RGB') + output = BytesIO() + img.save(output, format='JPEG') + file_name = str(uuid.uuid4()) + '.jpeg' + jpeg_data = output.getvalue() + jpeg_base64 = base64.b64encode(jpeg_data) + return jpeg_base64, file_name except Exception as e: return None, None - + +def get_video_url2attachment(url): + if not url: + return None + try: + if url.startswith('//'): + url = 'https:%s' % url + response = requests.get(url, timeout=90) + video_content = response.content + except Exception as e: + return None, None + # return this video in base64 + base64_video = base64.b64encode(video_content) + file_name = url.split('/')[-1] + return base64_video, file_name def get_ua_type(): ua = request.httprequest.headers.get('User-Agent') @@ -232,11 +280,15 @@ def get_ua_type(): and ('miniProgram' in ua or 'MiniProgram' in ua or 'MiniProgramEnv' in ua or 'wechatdevtools' in ua): # 微信小程序及开发者工具 utype = 'wxapp' + elif 'wxwork' in ua: + utype = 'qwapp' elif 'MicroMessenger' in ua: # 微信浏览器 utype = 'wxweb' elif 'cn.erpapp.o20sticks.App' in ua: # 安卓app utype = 'native_android' + elif 'BytedanceWebview' in ua: + utype = 'dyweb' # _logger.warning('=========get ua %s,%s' % (utype, ua)) return utype diff --git a/app_common/models/ir_mail_server.py b/app_common/models/ir_mail_server.py index 2321f0d0..741b9d7a 100644 --- a/app_common/models/ir_mail_server.py +++ b/app_common/models/ir_mail_server.py @@ -22,9 +22,9 @@ class IrMailServer(models.Model): if email_to.find('no-reply@odooai.cn') != -1 or email_to.find('postmaster-odoo@odooai.cn') != -1: pass elif email_to.find('example.com') != -1 or email_to.find('@sunpop.cn') != -1 or email_to.find('@odooapp.cn') != -1: - _logger.error(_("=================Email to ignore: %s") % email_to) + _logger.warning(_("=================Email to ignore: %s") % email_to) raise AssertionError(_("Email to ignore: %s") % email_to) return super(IrMailServer, self).send_email(message, mail_server_id, smtp_server, smtp_port, - smtp_user, smtp_password, smtp_encryption, smtp_ssl_certificate, smtp_ssl_private_key, + smtp_user, smtp_password, smtp_encryption, smtp_ssl_certificate, smtp_ssl_private_key, smtp_debug, smtp_session) diff --git a/app_common/models/ir_ui_view.py b/app_common/models/ir_ui_view.py index ce410ba8..16dfb9d5 100644 --- a/app_common/models/ir_ui_view.py +++ b/app_common/models/ir_ui_view.py @@ -1,7 +1,8 @@ # -*- coding: utf-8 -*- from odoo import api, models, tools, SUPERUSER_ID -from odoo.modules.module import get_resource_path +from odoo.tools import file_open +from odoo.tools import misc from odoo.tools import view_validation from odoo.tools.view_validation import _relaxng_cache, validate, _validators from odoo.tools.safe_eval import safe_eval @@ -14,10 +15,10 @@ def app_relaxng(view_type): """ Return a validator for the given view type, or None. """ if view_type not in _relaxng_cache: # tree, search 特殊 - if view_type in ['tree', 'search']: - _file = get_resource_path('app_common', 'rng', '%s_view.rng' % view_type) + if view_type in ['list', 'search']: + _file = misc.file_path('app_common/rng/%s_view.rng' % view_type) else: - _file = get_resource_path('base', 'rng', '%s_view.rng' % view_type) + _file = misc.file_path('base/rng/%s_view.rng' % view_type) with tools.file_open(_file) as frng: try: relaxng_doc = etree.parse(frng) @@ -28,15 +29,15 @@ def app_relaxng(view_type): return _relaxng_cache[view_type] view_validation.relaxng = app_relaxng -# + # class View(models.Model): # _inherit = 'ir.ui.view' -# + # def __init__(self, env, ids, prefetch_ids): # # 这里应该是无必要,但为了更安全 # super(View, self).__init__(env, ids, prefetch_ids) # view_validation.relaxng = app_relaxng -# -# # todo: 有可能需要处理增加的 header等标签 -# # 直接重写原生方法 -# # def transfer_node_to_modifiers(node, modifiers, context=None, in_tree_view=False): + + # todo: 有可能需要处理增加的 header等标签 + # 直接重写原生方法 + # def transfer_node_to_modifiers(node, modifiers, context=None, in_tree_view=False): diff --git a/app_common/models/mail_mail.py b/app_common/models/mail_mail.py index 1783926c..9c67f6d0 100644 --- a/app_common/models/mail_mail.py +++ b/app_common/models/mail_mail.py @@ -7,9 +7,10 @@ _logger = logging.getLogger(__name__) class MailMail(models.Model): _inherit = "mail.mail" - + # 猴子补丁模式,改默认发邮件逻辑 - def _send(self, auto_commit=False, raise_exception=False, smtp_session=None, alias_domain_id=False): + def _send(self, auto_commit=False, raise_exception=False, smtp_session=None, alias_domain_id=False, + mail_server=False, post_send_callback=None): for m in self: email_to = m.email_to # 忽略掉无效email,避免被ban @@ -17,8 +18,8 @@ class MailMail(models.Model): if email_to.find('no-reply@odooai.cn') != -1 or email_to.find('postmaster-odoo@odooai.cn') != -1: pass elif email_to.find('example.com') != -1 or email_to.find('@sunpop.cn') != -1 or email_to.find('@odooapp.cn') != -1: - _logger.error(_("=================Email to ignore: %s") % email_to) + _logger.warning(_("=================Email to ignore: %s") % email_to) self = self - m if not self: return True - return super(MailMail, self)._send(auto_commit, raise_exception, smtp_session, alias_domain_id) + return super(MailMail, self)._send(auto_commit, raise_exception, smtp_session, alias_domain_id, mail_server, post_send_callback) diff --git a/app_common/models/res_config_settings.py b/app_common/models/res_config_settings.py new file mode 100644 index 00000000..7f4129d7 --- /dev/null +++ b/app_common/models/res_config_settings.py @@ -0,0 +1,15 @@ +# -*- coding: utf-8 -*- + +import logging + +from odoo import api, fields, models, _ +from odoo.exceptions import UserError, ValidationError + +_logger = logging.getLogger(__name__) + + +class ResConfigSettings(models.TransientModel): + _inherit = 'res.config.settings' + + app_saas_ok = fields.Boolean('Enable CN SaaS', help="Checked to Enable www.odooapp.cn cloud service.", default=True, config_parameter='app_saas_ok') + diff --git a/app_common/views/res_config_settings_views.xml b/app_common/views/res_config_settings_views.xml new file mode 100644 index 00000000..55247c94 --- /dev/null +++ b/app_common/views/res_config_settings_views.xml @@ -0,0 +1,75 @@ + + + + + res.config.settings.view.form.inherit.app_common + res.config.settings + + + + +
+
+ +

Setup the communication to odooAi Cloud

+
+ + + +
+
+ +
+
+
+
+
+
+
+
+
+
+ + + odooAi Cloud + ir.actions.act_window + res.config.settings + form + inline + {'module' : 'app_common', 'bin_size': False} + +
+
diff --git a/app_odoo_customize/__manifest__.py b/app_odoo_customize/__manifest__.py index cf239572..e97c6124 100644 --- a/app_odoo_customize/__manifest__.py +++ b/app_odoo_customize/__manifest__.py @@ -23,7 +23,7 @@ { 'name': 'odoo Tweak OEM Development Enhance.Boost,Customize,Ai Employee,UI,Security,Remove Data All in One', - 'version': '24.09.03', + 'version': '17.0.25.01.26', 'author': 'odooai.cn', 'category': 'Extra Tools', 'website': 'https://www.odooai.cn', @@ -49,7 +49,6 @@ 'security/res_groups.xml', 'security/ir.model.access.csv', 'views/app_odoo_customize_views.xml', - 'views/app_theme_config_settings_views.xml', 'views/res_config_settings_views.xml', 'views/ir_views.xml', 'views/ir_module_module_views.xml', diff --git a/app_odoo_customize/views/app_theme_config_settings_views.xml b/app_odoo_customize/views/app_theme_config_settings_views.xml deleted file mode 100644 index d0ffb17e..00000000 --- a/app_odoo_customize/views/app_theme_config_settings_views.xml +++ /dev/null @@ -1,231 +0,0 @@ - - - - - res.config.settings.view.form.inherit.app_odoo_customize - res.config.settings - 20 - - - - > - -

Security and Boost

-
- - - - - - - - -
-

Extra Feature

- - -

UI Config

-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -

Data Cleaning (Be careful to do that!)

-
-
- Sale -
-
- POS -
-
- Purchase -
-
- Expense -
-
- MRP -
-
- Inventory -
-
- - Accounting - -
-
- Project -
-
- Quality -
-
- Website And Blog -
-
- Base Models -
-
- All Business -
-
-
-
-
-
- - - OEM and Boost - ir.actions.act_window - res.config.settings - form - inline - {'module' : 'app_odoo_customize', 'bin_size': False} - -
-
diff --git a/app_odoo_customize/views/res_config_settings_views.xml b/app_odoo_customize/views/res_config_settings_views.xml index c0e9e0be..2a949b19 100644 --- a/app_odoo_customize/views/res_config_settings_views.xml +++ b/app_odoo_customize/views/res_config_settings_views.xml @@ -1,6 +1,224 @@ + + res.config.settings.view.form.inherit.app_odoo_customize + res.config.settings + 20 + + + + > +
+ +
+

Security and Boost

+
+ + + + + + + + +
+

Extra Feature

+
+ + +
+ +

UI Config

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +

Data Cleaning (Be careful to do that!)

+
+
+ Sale +
+
+ POS +
+
+ Purchase +
+
+ Expense +
+
+ MRP +
+
+ Inventory +
+
+ + Accounting + +
+
+ Project +
+
+ Quality +
+
+ Website And Blog +
+
+ Base Models +
+
+ All Business +
+
+ + + + + app.res.config.settings.view.form res.config.settings @@ -12,4 +230,13 @@ + + + OEM and Boost + ir.actions.act_window + res.config.settings + form + inline + {'module' : 'app_odoo_customize', 'bin_size': False} + diff --git a/app_saas/models/res_config_settings.py b/app_saas/models/res_config_settings.py index 01c8ed94..0f9f2582 100644 --- a/app_saas/models/res_config_settings.py +++ b/app_saas/models/res_config_settings.py @@ -12,3 +12,6 @@ class ResConfigSettings(models.TransientModel): _inherit = 'res.config.settings' module_app_cn_po = fields.Boolean('SaaS Chinese PO', help="Checked to Sync Odoo Chinese from www.odooapp.cn") + app_saas_db_token = fields.Char('Cloud DB Token', default=True, config_parameter='app_saas_db_token', + help="The odooapp SaaS Token for this Odoo Database. You can reset in https://www.odooapp.cn") + \ No newline at end of file diff --git a/app_saas/models/res_users.py b/app_saas/models/res_users.py index 3da6fab7..7edcbcf2 100644 --- a/app_saas/models/res_users.py +++ b/app_saas/models/res_users.py @@ -12,15 +12,7 @@ except: from odoo import api, fields, models, _ -from odoo.exceptions import AccessDenied, UserError -from odoo.addons.auth_signup.models.res_users import SignupError -from odoo.http import request, Response - -from ast import literal_eval -import json import requests -from datetime import timedelta -import random import logging _logger = logging.getLogger(__name__) @@ -50,9 +42,13 @@ class ResUsers(models.Model): response = requests.get(oauth_provider.code_endpoint, params=params, timeout=30) if response.ok: ret = response.json() - # todo: 客户机首次连接时,取到的 server 端 key 写入 provider 的 client_secret - if ret.get('push_client_secret') and hasattr(oauth_provider, 'client_secret'): - oauth_provider.write({'client_secret': ret.get('push_client_secret')}) + # 客户机首次连接时,取到的 server 端 key 写入 provider 的 client_secret + push_client_secret = ret.pop('push_client_secret', False) + if push_client_secret: + ICP = self.env['ir.config_parameter'].sudo() + ICP.set_param('app_saas_db_token', push_client_secret) + if hasattr(oauth_provider, 'client_secret'): + oauth_provider.write({'client_secret': push_client_secret}) self._cr.commit() return ret return {}