diff --git a/app_ai_bard/__init__.py b/app_ai_bard/__init__.py new file mode 100644 index 00000000..d6948c82 --- /dev/null +++ b/app_ai_bard/__init__.py @@ -0,0 +1,4 @@ +# -*- coding: utf-8 -*- + +# from . import controllers +from . import models diff --git a/app_ai_bard/__manifest__.py b/app_ai_bard/__manifest__.py new file mode 100644 index 00000000..7ca2f3e4 --- /dev/null +++ b/app_ai_bard/__manifest__.py @@ -0,0 +1,58 @@ +# -*- coding: utf-8 -*- + +# Created on 2023-02-016 +# author: 欧度智能,https://www.odooai.cn +# email: 300883@qq.com +# resource of odooai +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +{ + 'name': 'Google Bard Ai for odoo ai center, 谷歌Ai支持', + 'version': '24.11.06', + 'author': 'odooai.cn', + 'company': 'odooai.cn', + 'maintainer': 'odooai.cn', + 'category': 'Website/Website', + 'website': 'https://www.odooai.cn', + 'live_test_url': 'https://demo.odooapp.cn', + 'license': 'LGPL-3', + 'sequence': 10, + 'images': ['static/description/banner.gif'], + 'summary': ''' + Google Bard Ai for Odoo AI Center. Ai Aigc Center including Google Bard Ai, Azure Ai, Baidu Ai. + Support chatgpt 4 image. DALLE, Integration All ChatGpt Api and Azure OpenAI Service. + Easy Chat channel with several ChatGPT Robots and train. + ''', + 'description': ''' + Chat with google bard ai with odoo. + Allows the application to leverage the capabilities of the GPT language model to generate human-like responses, + providing a more natural and intuitive user experience. + odoo bard connector. + 1. Multi ChatGpt openAI robot Connector. Chat and train. + 2. Multi Ai support including Google Bard Ai, Azure Ai, Chatgpt 4, Chatgpt 3.5 Turbo, Chatgpt 3 Davinci, Chatgpt 2 Code Optimized, 'Dall-E Image. + 3. Bind ChatGpt Api to user. So we can chat to robot user or use ChatGpt Channel for Group Chat. + 4. White and black List for ChatGpt. + 5. Setup Demo Chat time for every new user. + 6. Easy Start and Stop ChatGpt. + 7. Evaluation the ai robot to make better response. This training. + 8. Add api support Connect the Microsoft Azure OpenAI Service. + 9. Can set Synchronous or Asynchronous mode for Ai response. + 10.Filter Sensitive Words Setup. + 11. Multi-language Support. Multi-Company Support. + 12. Support Odoo 18,17,16,15,14,13,12, Enterprise and Community and odoo.sh Edition. + 13. Full Open Source. + ''', + 'depends': [ + 'app_chatgpt', + ], + 'data': [ + 'data/ai_robot_data.xml', + 'data/user_partner_data.xml', + ], + 'assets': { + }, + 'external_dependencies': {'python': ['bardapi']}, + 'installable': True, + 'application': True, + 'auto_install': False, +} diff --git a/app_ai_bard/controllers/__init__.py b/app_ai_bard/controllers/__init__.py new file mode 100644 index 00000000..65a8c120 --- /dev/null +++ b/app_ai_bard/controllers/__init__.py @@ -0,0 +1,3 @@ +# -*- coding: utf-8 -*- + +from . import main diff --git a/app_ai_bard/controllers/main.py b/app_ai_bard/controllers/main.py new file mode 100644 index 00000000..217c4d8b --- /dev/null +++ b/app_ai_bard/controllers/main.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- + +from odoo import http + + +class ChatgptController(http.Controller): + @http.route(['/chatgpt_form'], type='http', auth="public", csrf=False, + website=True) + def question_submit(self): + return http.request.render('app_chatgpt.connector') diff --git a/app_ai_bard/data/ai_robot_data.xml b/app_ai_bard/data/ai_robot_data.xml new file mode 100644 index 00000000..ad4ac00b --- /dev/null +++ b/app_ai_bard/data/ai_robot_data.xml @@ -0,0 +1,13 @@ + + + + + Google Bard + google + google-bard + + https://api.bard.ai/v1/text/generate + 9 + + + diff --git a/app_ai_bard/data/user_partner_data.xml b/app_ai_bard/data/user_partner_data.xml new file mode 100644 index 00000000..4e3f2910 --- /dev/null +++ b/app_ai_bard/data/user_partner_data.xml @@ -0,0 +1,19 @@ + + + + + Google Bard + + + + + ai_bard@example.com + ai_bard@example.com + + + + + + + + diff --git a/app_ai_bard/i18n/zh_CN.po b/app_ai_bard/i18n/zh_CN.po new file mode 100644 index 00000000..e69de29b diff --git a/app_ai_bard/models/__init__.py b/app_ai_bard/models/__init__.py new file mode 100644 index 00000000..a1e55a68 --- /dev/null +++ b/app_ai_bard/models/__init__.py @@ -0,0 +1,3 @@ +# -*- coding: utf-8 -*- + +from . import ai_robot diff --git a/app_ai_bard/models/ai_robot.py b/app_ai_bard/models/ai_robot.py new file mode 100644 index 00000000..d1622b77 --- /dev/null +++ b/app_ai_bard/models/ai_robot.py @@ -0,0 +1,59 @@ +# -*- coding: utf-8 -*- + +import os +import logging +import requests, json +from odoo import api, fields, models, _ +from odoo.exceptions import UserError +# todo: 暂时直接 requests +# from bardapi import Bard + + +_logger = logging.getLogger(__name__) + + +class AiRobot(models.Model): + _inherit = 'ai.robot' + + provider = fields.Selection( + selection_add=[('google', 'Google Ai')], + ondelete={'google': 'set default'} + ) + set_ai_model = fields.Selection( + selection_add=[('google-bard', 'Google Bard')], + ondelete={'google-bard': 'set default'}) + + @api.onchange('provider') + def _onchange_provider(self): + if self.provider == 'google': + self.endpoint = 'https://api.bard.ai/v1/text/generate' + return super()._onchange_provider() + + def get_google(self, data, author_id, answer_id, param={}): + self.ensure_one() + headers = {"Content-Type": "application/json", "Authorization": f"Bearer {self.openapi_api_key}"} + R_TIMEOUT = self.ai_timeout or 120 + o_url = self.endpoint or "https://api.bard.ai/v1/text/generate" + + # todo: 更多参数如 prompt, max_length + max_tokens = param.get('max_tokens') if param.get('max_tokens') else self.max_tokens + temperature = param.get('temperature') if param.get('temperature') else self.temperature + pdata = { + "text": data, + "max_length": max_tokens, + "temperature": temperature, + } + response = requests.post(o_url, data=json.dumps(pdata), headers=headers, timeout=R_TIMEOUT) + response.raise_for_status() + try: + res = response.json()['text'] + return res + except Exception as e: + _logger.warning("Get Response Json failed: %s", e) + else: + _logger.warning('=====================Openai output data: %s' % response.json()) + + def get_google_post(self, res, author_id=False, answer_id=False, param={}): + if self.provider == 'google': + content = res['text'] + return content, False, True diff --git a/app_ai_bard/security/odooai.cn b/app_ai_bard/security/odooai.cn new file mode 100644 index 00000000..e69de29b diff --git a/app_ai_bard/static/description/banner.gif b/app_ai_bard/static/description/banner.gif new file mode 100644 index 00000000..93134017 Binary files /dev/null and b/app_ai_bard/static/description/banner.gif differ diff --git a/app_ai_bard/static/description/banner.png b/app_ai_bard/static/description/banner.png new file mode 100644 index 00000000..958454d3 Binary files /dev/null and b/app_ai_bard/static/description/banner.png differ diff --git a/app_ai_bard/static/description/bard.gif b/app_ai_bard/static/description/bard.gif new file mode 100644 index 00000000..78de3734 Binary files /dev/null and b/app_ai_bard/static/description/bard.gif differ diff --git a/app_ai_bard/static/description/chatgpt.png b/app_ai_bard/static/description/chatgpt.png new file mode 100644 index 00000000..20a03f9d Binary files /dev/null and b/app_ai_bard/static/description/chatgpt.png differ diff --git a/app_ai_bard/static/description/chatgpt4_azure.png b/app_ai_bard/static/description/chatgpt4_azure.png new file mode 100644 index 00000000..8f24124c Binary files /dev/null and b/app_ai_bard/static/description/chatgpt4_azure.png differ diff --git a/app_ai_bard/static/description/chatgpt_blue.png b/app_ai_bard/static/description/chatgpt_blue.png new file mode 100644 index 00000000..1e94178f Binary files /dev/null and b/app_ai_bard/static/description/chatgpt_blue.png differ diff --git a/app_ai_bard/static/description/chatgpt_green.jpg b/app_ai_bard/static/description/chatgpt_green.jpg new file mode 100644 index 00000000..f3350c62 Binary files /dev/null and b/app_ai_bard/static/description/chatgpt_green.jpg differ diff --git a/app_ai_bard/static/description/demo01.jpg b/app_ai_bard/static/description/demo01.jpg new file mode 100644 index 00000000..dd014ea8 Binary files /dev/null and b/app_ai_bard/static/description/demo01.jpg differ diff --git a/app_ai_bard/static/description/demo02.jpg b/app_ai_bard/static/description/demo02.jpg new file mode 100644 index 00000000..7e0c8c39 Binary files /dev/null and b/app_ai_bard/static/description/demo02.jpg differ diff --git a/app_ai_bard/static/description/demo03.jpg b/app_ai_bard/static/description/demo03.jpg new file mode 100644 index 00000000..f8f05ac6 Binary files /dev/null and b/app_ai_bard/static/description/demo03.jpg differ diff --git a/app_ai_bard/static/description/demo04.jpg b/app_ai_bard/static/description/demo04.jpg new file mode 100644 index 00000000..0051ee1d Binary files /dev/null and b/app_ai_bard/static/description/demo04.jpg differ diff --git a/app_ai_bard/static/description/demo1.jpg b/app_ai_bard/static/description/demo1.jpg new file mode 100644 index 00000000..ff7e66d4 Binary files /dev/null and b/app_ai_bard/static/description/demo1.jpg differ diff --git a/app_ai_bard/static/description/demo2.jpg b/app_ai_bard/static/description/demo2.jpg new file mode 100644 index 00000000..c1fad2d0 Binary files /dev/null and b/app_ai_bard/static/description/demo2.jpg differ diff --git a/app_ai_bard/static/description/demo3.jpg b/app_ai_bard/static/description/demo3.jpg new file mode 100644 index 00000000..427f8b90 Binary files /dev/null and b/app_ai_bard/static/description/demo3.jpg differ diff --git a/app_ai_bard/static/description/demo4.jpg b/app_ai_bard/static/description/demo4.jpg new file mode 100644 index 00000000..b83102fa Binary files /dev/null and b/app_ai_bard/static/description/demo4.jpg differ diff --git a/app_ai_bard/static/description/demo5.jpg b/app_ai_bard/static/description/demo5.jpg new file mode 100644 index 00000000..c8f3ffd6 Binary files /dev/null and b/app_ai_bard/static/description/demo5.jpg differ diff --git a/app_ai_bard/static/description/demo6.jpg b/app_ai_bard/static/description/demo6.jpg new file mode 100644 index 00000000..af62be26 Binary files /dev/null and b/app_ai_bard/static/description/demo6.jpg differ diff --git a/app_ai_bard/static/description/demo7.jpg b/app_ai_bard/static/description/demo7.jpg new file mode 100644 index 00000000..d65a8b2a Binary files /dev/null and b/app_ai_bard/static/description/demo7.jpg differ diff --git a/app_ai_bard/static/description/demo71.jpg b/app_ai_bard/static/description/demo71.jpg new file mode 100644 index 00000000..a5c83ab2 Binary files /dev/null and b/app_ai_bard/static/description/demo71.jpg differ diff --git a/app_ai_bard/static/description/demo8.jpg b/app_ai_bard/static/description/demo8.jpg new file mode 100644 index 00000000..e17bab42 Binary files /dev/null and b/app_ai_bard/static/description/demo8.jpg differ diff --git a/app_ai_bard/static/description/demo81.jpg b/app_ai_bard/static/description/demo81.jpg new file mode 100644 index 00000000..cf73a540 Binary files /dev/null and b/app_ai_bard/static/description/demo81.jpg differ diff --git a/app_ai_bard/static/description/demo9.jpg b/app_ai_bard/static/description/demo9.jpg new file mode 100644 index 00000000..e8760f93 Binary files /dev/null and b/app_ai_bard/static/description/demo9.jpg differ diff --git a/app_ai_bard/static/description/demo91.jpg b/app_ai_bard/static/description/demo91.jpg new file mode 100644 index 00000000..ccad6d14 Binary files /dev/null and b/app_ai_bard/static/description/demo91.jpg differ diff --git a/app_ai_bard/static/description/demoa.jpg b/app_ai_bard/static/description/demoa.jpg new file mode 100644 index 00000000..02e4e950 Binary files /dev/null and b/app_ai_bard/static/description/demoa.jpg differ diff --git a/app_ai_bard/static/description/demob.jpg b/app_ai_bard/static/description/demob.jpg new file mode 100644 index 00000000..9d845f51 Binary files /dev/null and b/app_ai_bard/static/description/demob.jpg differ diff --git a/app_ai_bard/static/description/icon.png b/app_ai_bard/static/description/icon.png new file mode 100644 index 00000000..5392d797 Binary files /dev/null and b/app_ai_bard/static/description/icon.png differ diff --git a/app_ai_bard/static/description/index.html b/app_ai_bard/static/description/index.html new file mode 100644 index 00000000..3003f48d --- /dev/null +++ b/app_ai_bard/static/description/index.html @@ -0,0 +1,263 @@ +
+
+
+

Google Bard Ai for odoo ai center

+

Ai center addons. all aigc in one.

+
+

Latest update: v18.24.11.06

+
+
+ + Add google bard support, update chatgpt api
+ +
+
+ +
+
+
+ Key features: +
    +
  • + + 1. Multi ChatGpt openAI robot Connector. Chat and train. +
  • +
  • + + 2. Multi Ai support including Google Bard Ai, Azure Ai, Chatgpt 4, Chatgpt 3.5 Turbo, Chatgpt 3 Davinci, Chatgpt 2 Code Optimized, 'Dall-E Image. +
  • +
  • + + 3. Bind ChatGpt Api to user. So we can chat to robot user or use ChatGpt Channel for Group Chat. +
  • +
  • + + 4. White and black List for ChatGpt. +
  • +
  • + + 5. Setup Demo Chat time for every new user. +
  • +
  • + + 6. Easy Start and Stop ChatGpt. +
  • +
  • + + 7. Evaluation the ai robot to make better response. This training. +
  • +
  • + + 8. Add api support Connect the Microsoft Azure OpenAI Service. +
  • +
  • + + 9. Can set Synchronous or Asynchronous mode for Ai response. +
  • +
  • + + 10.Filter Sensitive Words Setup. +
  • +
  • + + 11. Multi-language Support. Multi-Company Support. +
  • +
  • + + 12. Support Odoo 18,17,16,15,14,13,12, Enterprise and Community and odoo.sh Edition. +
  • +
  • + + 13. Full Open Source. +
  • +
+
+
+
+
+
+
+ +
+
+

Add more Ai support like google bard, chatgpt 4, baidu china

+

Need to navigate to odoo app store to buy and install addons

+
+ +
+

Please apply for the bard api first from google

+
+ +
+

Setup for your own key

+
+ +
+
+
+ +
+
+

Easy to use Ai Robot with multi Provider. Easy chat, easy help

+

Open Ai for more smart. Microsoft Azure chatgpt for china user.

+
+ +
+
+
+ +
+
+

1. Multi ChatGpt openAI robot Connector. Chat and train.

+

Goto Setting--> GPT Robot to setup your robot api.

+

Input your api key, And Select the api model you need to use.

+
+ +
+

You can set the Temperature higer for more creative answer.

+
+ +
+
+
+ + +
+
+

2. Multi Api support, Chatgpt 3.5 Turbo, Chatgpt 3 Davinci, Chatgpt 2 Code Optimized, 'Dall-E Image.

+

Choose the model you want to use

+
+ +
+

You can set the Temperature higer for more creative answer.

+
+ +
+
+
+ +
+
+

3. Bind ChatGpt Api to user. So we can chat to robot user or use ChatGpt Channel for Group Chat.

+

Go Settings ->users, bind chatgpt to some user.

+ +
+

So you can have many user, and many chatgpt robot. This provide you an Ai pool.

+
+ +
+

You can set the blacklist to this chatgpt robot to limit request. Also you can setup Demo time for every normal user..

+
+ +
+ +
+ +
+
+

4. White and black List for ChatGpt.

+

5. Setup Demo Chat time for every new user.

+

You can set the blacklist to this chatgpt robot to limit request. Also you can setup Demo time for every normal user..

+
+ +
+
+
+ +
+
+

6. Easy Start and Stop ChatGpt..

+

You can easy chat with the apt robot with odoo IM

+
+ +
+

You can chat with several robot in the same time

+
+ +
+

If you have more than 1 robot in the group. you can @ the specify robot.

+
+ +
+
+
+ + +
+
+

7. Evaluation the ai robot to make better response. This training.

+

You can Evaluation chatgpt's answer. Mark as good for good answer. Mark as back for bad answer.

+

With Evaluation, you can make your ai robot more smart. +

+ +
+
+
+ +
+
+

8. Add api support Connect the Microsoft Azure OpenAI Service.

+

Azure openai add. It is for china and other country which no chatgpt service.

+
+ +
+
+
+ +
+
+

9. Can set Synchronous or Asynchronous mode for Ai response.

+

Synchronous(default) mode can get response then ask question again. Asynchronous mode would make you do other thing when waiting for response.

+
+ +
+
+
+ +
+
+

Multi-language Support..

+

+
+ +
+
+
+ +
+
+
+

Technical Help & Support

+
+
+
+

+ For any type of technical help & support requests, Feel free to contact us

+ + odoo@china.com +

+ Via QQ: 300883 (App user would not get QQ or any other IM support. Only for odoo project customize.)

+ + 300883@qq.com +
+
+

+ Visit our website for more support.

+ + https://www.odooai.cn +
+
+
+
+

More Powerful addons, Make your odoo very easy to use, easy customize: + odooai.cn Odoo Addons +

+
+
+ diff --git a/app_ai_bard/static/src/img/bard.gif b/app_ai_bard/static/src/img/bard.gif new file mode 100644 index 00000000..78de3734 Binary files /dev/null and b/app_ai_bard/static/src/img/bard.gif differ diff --git a/app_ai_bard/static/src/img/google.png b/app_ai_bard/static/src/img/google.png new file mode 100644 index 00000000..ed8841bd Binary files /dev/null and b/app_ai_bard/static/src/img/google.png differ diff --git a/app_ai_bard/static/src/odooai.cn b/app_ai_bard/static/src/odooai.cn new file mode 100644 index 00000000..e69de29b diff --git a/app_ai_bard/views/ai_robot_views.xml b/app_ai_bard/views/ai_robot_views.xml new file mode 100644 index 00000000..05a6f03f --- /dev/null +++ b/app_ai_bard/views/ai_robot_views.xml @@ -0,0 +1,100 @@ + + + + ai.robot.tree + ai.robot + + + + + + + + + + + + + + + + ai.robot.form + ai.robot + +
+
+
+ +
+
+ + + + + + + + + + + + + +
+ + +
+ + + + + + +
+ + + + +
+
+
+
+
+ + + GPT Robot + ai.robot + list,form + +

+ Let's create a GPT Robot. +

+
+
+ + + Disconnect + + + list,form + code + action = records.action_disconnect() + + + + +
diff --git a/app_ai_bard/views/res_partner_ai_use_views.xml b/app_ai_bard/views/res_partner_ai_use_views.xml new file mode 100644 index 00000000..716a8acb --- /dev/null +++ b/app_ai_bard/views/res_partner_ai_use_views.xml @@ -0,0 +1,94 @@ + + + + res.partner.ai.use.tree + res.partner.ai.use + + + + + + + + + + + + + + + + + + + + + res.partner.ai.use.form + res.partner.ai.use + +
+ + +
+
+
+ + + res.partner.ai.use.search + res.partner.ai.use + + + + + + + + + + + + + Partner Ai Use + res.partner.ai.use + list,form + {'create': 0, 'delete': 0} + + + + + Partner Ai Use + res.partner.ai.use + list,form + [('ai_user_id', 'in', active_ids)] + {'default_ai_user_id':active_id,} + + + +
diff --git a/app_ai_bard/views/res_users_views.xml b/app_ai_bard/views/res_users_views.xml new file mode 100644 index 00000000..ad87f8b8 --- /dev/null +++ b/app_ai_bard/views/res_users_views.xml @@ -0,0 +1,39 @@ + + + + app.chatgpt.res.users.form + res.users + + + + + + + + + + + + + + + + + + + + + + app.res.users.search + res.users + + + + + + + + + + diff --git a/app_auto_backup/__init__.py b/app_auto_backup/__init__.py new file mode 100644 index 00000000..aa4d0fd6 --- /dev/null +++ b/app_auto_backup/__init__.py @@ -0,0 +1,4 @@ +# -*- coding: utf-8 -*- + +from . import controllers +from . import models diff --git a/app_auto_backup/__manifest__.py b/app_auto_backup/__manifest__.py new file mode 100644 index 00000000..f9cf5e0f --- /dev/null +++ b/app_auto_backup/__manifest__.py @@ -0,0 +1,60 @@ +# -*- coding: utf-8 -*- +# 'author': "Yenthe Van Ginneken", +# 'website': "http://www.odoo.yenthevg.com", +# 'author': "guohuadeng@hotmail.com", +# 'website': "https://www.odooai.cn", + +{ + 'name': "Database auto backup and Download,数据库自动备份", + 'version': '24.11.06', + + 'summary': 'Automated and odoo database backups. easy download and manage database file. optimized from auto_backup of oca Yenthe Van Ginneken', + + 'description': """ + 1. Easy schedule database backup + 2. Set remote backup and cron schedule + 3. Manual backup database in one click + 4. Easy download backup file or remove file for System user + 11. Multi-language Support. Multi-Company Support. + 12. Support Odoo 18,17,16,15,14,13,12, Enterprise and Community and odoo.sh Edition. + 13. Full Open Source. + The Database Auto-Backup module enables the user to make configurations for the automatic backup of the database. + Backups can be taken on the local system or on a remote server, through SFTP. + You only have to specify the hostname, port, backup location and databasename (all will be pre-filled by default with correct data. + If you want to write to an external server with SFTP you will need to provide the IP, username and password for the remote backups. + The base of this module is taken from Odoo SA V6.1 (https://www.odoo.com/apps/modules/6.0/auto_backup/) and then upgraded and heavily expanded. + This module is made and provided by Yenthe Van Ginneken (Oocademy). + Automatic backup for all such configured databases can then be scheduled as follows: + + 1) Go to Settings / Technical / Automation / Scheduled actions. + 2) Search the action 'Backup scheduler'. + 3) Set it active and choose how often you wish to take backups. + 4) If you want to write backups to a remote location you should fill in the SFTP details. + """, + + 'author': 'odooai.cn', + 'website': "http://www.odooai.cn", + 'category': 'Extra tools', + 'installable': True, + 'license': 'LGPL-3', + 'price': 38.00, + 'currency': 'EUR', + + # any module necessary for this one to work correctly + 'depends': [ + 'base', + 'app_odoo_customize' + ], + 'external_dependencies': { + 'python': ['paramiko'], + }, + + # always loaded + 'data': [ + 'security/user_groups.xml', + 'security/ir.model.access.csv', + 'views/backup_view.xml', + 'data/backup_data.xml', + 'views/db_backup_details.xml', + ], +} diff --git a/app_auto_backup/controllers/__init__.py b/app_auto_backup/controllers/__init__.py new file mode 100644 index 00000000..65a8c120 --- /dev/null +++ b/app_auto_backup/controllers/__init__.py @@ -0,0 +1,3 @@ +# -*- coding: utf-8 -*- + +from . import main diff --git a/app_auto_backup/controllers/main.py b/app_auto_backup/controllers/main.py new file mode 100644 index 00000000..bd672f4b --- /dev/null +++ b/app_auto_backup/controllers/main.py @@ -0,0 +1,33 @@ +# -*- coding: utf-8 -*- + +import logging +import os + +from odoo import http, _ +from odoo.http import request, content_disposition +from odoo.exceptions import AccessError, UserError + +_logger = logging.getLogger(__name__) + + +class AppAutoBackup(http.Controller): + + @http.route("/dbbackup/download/", type="http", auth="user") + def download_backupfile(self, file_path, **kw): + _logger.warning('download_backupfile: %s', file_path) + if not self.env.user.has_group('base.group_system'): + raise UserError(_('File not found for user.')) + if os.path.exists(file_path): + try: + with open(file_path, 'rb') as file: + file_content = file.read() + file_name = file_path.split("/")[-1] + headers = [ + ('Content-Type', 'application/octet-stream'), + ('Content-Disposition', content_disposition(file_name)), + ] + return request.make_response(file_content, headers) + except Exception as e: + raise UserError(e) + else: + return 'File not found' diff --git a/app_auto_backup/data/backup_data.xml b/app_auto_backup/data/backup_data.xml new file mode 100644 index 00000000..4d061b59 --- /dev/null +++ b/app_auto_backup/data/backup_data.xml @@ -0,0 +1,15 @@ + + + + + hours + Backup scheduler + 5 + True + 12 + + code + model.schedule_backup() + + + diff --git a/app_auto_backup/i18n/zh_CN.po b/app_auto_backup/i18n/zh_CN.po new file mode 100644 index 00000000..f06e7d85 --- /dev/null +++ b/app_auto_backup/i18n/zh_CN.po @@ -0,0 +1,449 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * app_auto_backup +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 16.0+e-20231112\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2024-07-23 08:20+0000\n" +"PO-Revision-Date: 2024-07-23 08:20+0000\n" +"Last-Translator: \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: app_auto_backup +#: model_terms:ir.ui.view,arch_db:app_auto_backup.view_backup_config_form +msgid "" +"Warning:\n" +" Use SFTP with caution! This writes files to external servers under the path you specify." +msgstr "" +"警告:\n" +" 使用 SFTP 要注意! 写入至外部Server的文件要在你指定的路径下。" + +#. module: app_auto_backup +#: model:ir.model.fields,help:app_auto_backup.field_db_backup__folder +msgid "Absolute path for storing the backups" +msgstr "备份绝对路径" + +#. module: app_auto_backup +#: model:ir.module.category,name:app_auto_backup.module_management +msgid "Auto backup access" +msgstr "备份的权限" + +#. module: app_auto_backup +#: model:ir.model.fields,field_description:app_auto_backup.field_db_backup__send_mail_sftp_fail +msgid "Auto. E-mail on backup fail" +msgstr "FTP备份失败自动邮件通知你" + +#. module: app_auto_backup +#: model:ir.model.fields,field_description:app_auto_backup.field_db_backup__autoremove +msgid "Auto. Remove Backups" +msgstr "自动删除备份" + +#. module: app_auto_backup +#: model_terms:ir.ui.view,arch_db:app_auto_backup.view_backup_config_form +msgid "Back-up view" +msgstr "备份视图" + +#. module: app_auto_backup +#: model:ir.ui.menu,name:app_auto_backup.auto_backup_menu +msgid "Back-ups" +msgstr "备份" + +#. module: app_auto_backup +#: model:ir.model.fields,field_description:app_auto_backup.field_db_backup__backup_details_ids +msgid "Backup Details" +msgstr "备份明细" + +#. module: app_auto_backup +#: model:ir.model.fields,field_description:app_auto_backup.field_db_backup__folder +msgid "Backup Directory" +msgstr "备份目录" + +#. module: app_auto_backup +#: model:ir.model.fields,field_description:app_auto_backup.field_db_backup__backup_type +msgid "Backup Type" +msgstr "备份类型" + +#. module: app_auto_backup +#: model:ir.model,name:app_auto_backup.model_db_backup +msgid "Backup configuration record" +msgstr "备份配置记录" + +#. module: app_auto_backup +#: model_terms:ir.ui.view,arch_db:app_auto_backup.view_backup_config_form +msgid "Backup records" +msgstr "备份记录" + +#. module: app_auto_backup +#: model:ir.actions.server,name:app_auto_backup.backup_scheduler_ir_actions_server +#: model:ir.cron,cron_name:app_auto_backup.backup_scheduler +msgid "Backup scheduler" +msgstr "数据库备份计划" + +#. module: app_auto_backup +#: model_terms:ir.ui.view,arch_db:app_auto_backup.view_backup_config_tree +msgid "Backups" +msgstr "备份" + +#. module: app_auto_backup +#: model:ir.model.fields,help:app_auto_backup.field_db_backup__days_to_keep_sftp +msgid "" +"Choose after how many days the backup should be deleted from the FTP server. For example:\n" +"If you fill in 5 the backups will be removed after 5 days from the FTP server." +msgstr "" +"选择后多少天备份应被删除从 FTP 服务器。例如: \n" +"如果你填写 5, 将5 天后 从FTP 服务器 删除备份文件。" + +#. module: app_auto_backup +#: model:ir.model.fields,help:app_auto_backup.field_db_backup__days_to_keep +msgid "" +"Choose after how many days the backup should be deleted. For example:\n" +"If you fill in 5 the backups will be removed after 5 days." +msgstr "" +"选择多少天后将会删除历史备份文件。如:\n" +"填入 5 ,则5天前的备份文件将自动删除。" + +#. module: app_auto_backup +#: model:ir.actions.act_window,name:app_auto_backup.action_backup +#: model:ir.ui.menu,name:app_auto_backup.backup_conf_menu +msgid "Configure backups" +msgstr "配置备份" + +#. module: app_auto_backup +#. odoo-python +#: code:addons/app_auto_backup/models/db_backup.py:0 +#, python-format +msgid "Connection Test Failed!" +msgstr "连接测试失败!" + +#. module: app_auto_backup +#. odoo-python +#: code:addons/app_auto_backup/models/db_backup.py:0 +#, python-format +msgid "" +"Connection Test Succeeded!\n" +"Everything seems properly set up for FTP back-ups!" +msgstr "" +"连接测试成功!\n" +"所有的FTP备份设置正常" + +#. module: app_auto_backup +#: model_terms:ir.ui.view,arch_db:app_auto_backup.view_backup_config_form +msgid "Contact odooai.cn!" +msgstr "联系 odooai.cn" + +#. module: app_auto_backup +#: model:ir.model.fields,field_description:app_auto_backup.field_db_backup__create_uid +#: model:ir.model.fields,field_description:app_auto_backup.field_db_backup_details__create_uid +msgid "Created by" +msgstr "创建者" + +#. module: app_auto_backup +#: model:ir.model.fields,field_description:app_auto_backup.field_db_backup__create_date +#: model:ir.model.fields,field_description:app_auto_backup.field_db_backup_details__create_date +msgid "Created on" +msgstr "创建时间" + +#. module: app_auto_backup +#: model:ir.model.fields,field_description:app_auto_backup.field_db_backup__name +msgid "Database" +msgstr "数据库" + +#. module: app_auto_backup +#: model:ir.model.fields,field_description:app_auto_backup.field_db_backup_details__db_backup_id +msgid "Database Backup" +msgstr "数据库备份" + +#. module: app_auto_backup +#: model:ir.model,name:app_auto_backup.model_db_backup_details +msgid "Database Backup Details" +msgstr "数据库备份明细" + +#. module: app_auto_backup +#: model:ir.actions.act_window,name:app_auto_backup.action_db_backup_details +#: model:ir.ui.menu,name:app_auto_backup.menu_action_db_backup_details +msgid "Database backups" +msgstr "数据库备份管理" + +#. module: app_auto_backup +#: model:ir.model.fields,help:app_auto_backup.field_db_backup__name +msgid "Database you want to schedule backups for" +msgstr "计划备份的数据库" + +#. module: app_auto_backup +#: model:ir.model.fields,field_description:app_auto_backup.field_db_backup__display_name +#: model:ir.model.fields,field_description:app_auto_backup.field_db_backup_details__display_name +msgid "Display Name" +msgstr "显示名称" + +#. module: app_auto_backup +#: model_terms:ir.ui.view,arch_db:app_auto_backup.db_backup_details_tree_view +#: model_terms:ir.ui.view,arch_db:app_auto_backup.view_backup_config_form +msgid "Download File" +msgstr "下载文件" + +#. module: app_auto_backup +#: model:ir.model.fields.selection,name:app_auto_backup.selection__db_backup__backup_type__dump +msgid "Dump" +msgstr "" + +#. module: app_auto_backup +#: model:ir.model.fields,field_description:app_auto_backup.field_db_backup__email_to_notify +msgid "E-mail to notify" +msgstr "提醒E-mail地址" + +#. module: app_auto_backup +#: model:ir.model.fields,field_description:app_auto_backup.field_db_backup_details__file_path +msgid "File Path" +msgstr "文件路径" + +#. module: app_auto_backup +#. odoo-python +#: code:addons/app_auto_backup/models/db_backup_details.py:0 +#, python-format +msgid "File Path or URL not found." +msgstr "无法找到该文件路径或URL" + +#. module: app_auto_backup +#: model:ir.model.fields,help:app_auto_backup.field_db_backup__email_to_notify +msgid "" +"Fill in the e-mail where you want to be notified that the backup failed on " +"the FTP." +msgstr "FTP备份失败时,邮件通知你详细信息" + +#. module: app_auto_backup +#: model_terms:ir.ui.view,arch_db:app_auto_backup.view_backup_config_form +msgid "For example: /odoo/backups/" +msgstr "如:/odoo/backups/" + +#. module: app_auto_backup +#: model_terms:ir.ui.view,arch_db:app_auto_backup.view_backup_config_form +msgid "Go to Settings / Technical / Automation / Scheduled Actions." +msgstr "点击 设置 / 技术 / 自动化 / 计划的动作" + +#. module: app_auto_backup +#: model_terms:ir.ui.view,arch_db:app_auto_backup.view_backup_config_form +msgid "Help" +msgstr "帮助" + +#. module: app_auto_backup +#. odoo-python +#: code:addons/app_auto_backup/models/db_backup.py:0 +#, python-format +msgid "Here is what we got instead:\n" +msgstr "这里是我们 is what we got instead:\n" + +#. module: app_auto_backup +#: model:ir.model.fields,field_description:app_auto_backup.field_db_backup__host +msgid "Host" +msgstr "服务器" + +#. module: app_auto_backup +#: model:ir.model.fields,field_description:app_auto_backup.field_db_backup__id +#: model:ir.model.fields,field_description:app_auto_backup.field_db_backup_details__id +msgid "ID" +msgstr "" + +#. module: app_auto_backup +#: model:ir.model.fields,field_description:app_auto_backup.field_db_backup__sftp_host +msgid "IP Address SFTP Server" +msgstr " SFTP 服务器 IP 地址" + +#. module: app_auto_backup +#: model:ir.model.fields,help:app_auto_backup.field_db_backup__send_mail_sftp_fail +msgid "" +"If you check this option you can choose to automaticly get e-mailed when the" +" backup to the external server failed." +msgstr "如果您选中此选项,您可以选择自动收到通过邮件发送到外部服务器备份失败的信息。" + +#. module: app_auto_backup +#: model:ir.model.fields,help:app_auto_backup.field_db_backup__autoremove +msgid "" +"If you check this option you can choose to automaticly remove the backup " +"after xx days" +msgstr "如果您选中此选项,您可以指定需要写入 sftp 的远程服务器的详细信息。" + +#. module: app_auto_backup +#: model:ir.model.fields,help:app_auto_backup.field_db_backup__sftp_write +msgid "" +"If you check this option you can specify the details needed to write to a " +"remote server with SFTP." +msgstr "如果勾选此项,您可以进行远程 SFTP 备份。" + +#. module: app_auto_backup +#: model:ir.model.fields,field_description:app_auto_backup.field_db_backup____last_update +#: model:ir.model.fields,field_description:app_auto_backup.field_db_backup_details____last_update +msgid "Last Modified on" +msgstr "最后更新时间" + +#. module: app_auto_backup +#: model:ir.model.fields,field_description:app_auto_backup.field_db_backup__write_uid +#: model:ir.model.fields,field_description:app_auto_backup.field_db_backup_details__write_uid +msgid "Last Updated by" +msgstr "最后更新者" + +#. module: app_auto_backup +#: model:ir.model.fields,field_description:app_auto_backup.field_db_backup__write_date +#: model:ir.model.fields,field_description:app_auto_backup.field_db_backup_details__write_date +msgid "Last Updated on" +msgstr "最后更新日期" + +#. module: app_auto_backup +#: model_terms:ir.ui.view,arch_db:app_auto_backup.view_backup_config_form +msgid "Local backup configuration" +msgstr "本地备份配置" + +#. module: app_auto_backup +#: model:res.groups,name:app_auto_backup.group_manager +msgid "Manager" +msgstr "备份管理员" + +#. module: app_auto_backup +#: model:ir.model.fields,field_description:app_auto_backup.field_db_backup_details__name +msgid "Name" +msgstr "文件名" + +#. module: app_auto_backup +#: model_terms:ir.ui.view,arch_db:app_auto_backup.view_backup_config_form +msgid "Need more help?" +msgstr "需要更多帮助吗?" + +#. module: app_auto_backup +#: model:ir.model.fields,field_description:app_auto_backup.field_db_backup__sftp_password +msgid "Password User SFTP Server" +msgstr " SFTP服务器密码" + +#. module: app_auto_backup +#: model:ir.model.fields,field_description:app_auto_backup.field_db_backup__sftp_path +msgid "Path external server" +msgstr "服务器目录" + +#. module: app_auto_backup +#: model:ir.model.fields,field_description:app_auto_backup.field_db_backup__port +msgid "Port" +msgstr "端口" + +#. module: app_auto_backup +#: model_terms:ir.ui.view,arch_db:app_auto_backup.db_backup_details_tree_view +#: model_terms:ir.ui.view,arch_db:app_auto_backup.view_backup_config_form +msgid "Remove File" +msgstr "删除文件" + +#. module: app_auto_backup +#: model:ir.model.fields,field_description:app_auto_backup.field_db_backup__days_to_keep_sftp +msgid "Remove SFTP after x days" +msgstr "多少天后从服务器删除" + +#. module: app_auto_backup +#: model:ir.model.fields,field_description:app_auto_backup.field_db_backup__days_to_keep +msgid "Remove after x days" +msgstr "多少天后删除" + +#. module: app_auto_backup +#: model_terms:ir.ui.view,arch_db:app_auto_backup.view_backup_config_form +msgid "Run Backup" +msgstr "执行备份" + +#. module: app_auto_backup +#: model_terms:ir.ui.view,arch_db:app_auto_backup.view_backup_config_form +msgid "SFTP" +msgstr "" + +#. module: app_auto_backup +#: model:ir.model.fields,field_description:app_auto_backup.field_db_backup__sftp_port +msgid "SFTP Port" +msgstr "SFTP 端口" + +#. module: app_auto_backup +#: model_terms:ir.ui.view,arch_db:app_auto_backup.view_backup_config_form +msgid "Search the action named 'Backup scheduler'." +msgstr "搜索计划备份调度程序“备份计划”。" + +#. module: app_auto_backup +#: model_terms:ir.ui.view,arch_db:app_auto_backup.view_backup_config_form +msgid "" +"Set the scheduler to active and fill in how often you want backups " +"generated." +msgstr "设置计划动作为有效,并填写备份间隔时间,间隔时间单位,间隔次数,执行时间等数据库具体备份方案。" + +#. module: app_auto_backup +#: model_terms:ir.ui.view,arch_db:app_auto_backup.view_backup_config_form +msgid "Test SFTP Connection" +msgstr "测试 SFTP 连接" + +#. module: app_auto_backup +#: model:ir.model.fields,help:app_auto_backup.field_db_backup__sftp_host +msgid "The IP address from your remote server. For example 192.168.0.1" +msgstr "SFTP服务器的 IP 地址。例如: 192.168.0.1" + +#. module: app_auto_backup +#: model:ir.model.fields,help:app_auto_backup.field_db_backup__sftp_path +msgid "" +"The location to the folder where the dumps should be written to. For example /odoo/backups/.\n" +"Files will then be written to /odoo/backups/ on your remote server." +msgstr "" +"转储应将写入的文件夹位置。例如 /odoo/backups/远程服务器上,然后将写入 /odoo/backups/.\n" +"Files。" + +#. module: app_auto_backup +#: model:ir.model.fields,help:app_auto_backup.field_db_backup__sftp_password +msgid "" +"The password from the user where the SFTP connection should be made with. " +"This is the password from the user on the external server." +msgstr "从 SFTP 服务器连接该用户的密码。这是SFTP服务器上的用户密码。" + +#. module: app_auto_backup +#: model:ir.model.fields,help:app_auto_backup.field_db_backup__sftp_port +msgid "The port on the FTP server that accepts SSH/SFTP calls." +msgstr "接受 SSH/SFTP 使用的FTP 服务器上的端口。" + +#. module: app_auto_backup +#: model:ir.model.fields,help:app_auto_backup.field_db_backup__sftp_user +msgid "" +"The username where the SFTP connection should be made with. This is the user" +" on the external server." +msgstr "SFTP 连接使用该用户名。这是在SFTP服务器上的用户。" + +#. module: app_auto_backup +#: model_terms:ir.ui.view,arch_db:app_auto_backup.view_backup_config_form +msgid "" +"This configures the scheduler for automatic backup of the given database running on given host\n" +" at given port on regular intervals.\n" +"
\n" +" Automatic backups of the database can be scheduled as follows:" +msgstr "配置适用指定数据库备份 在设置服务器端口定期运行" + +#. module: app_auto_backup +#: model:ir.model.fields,field_description:app_auto_backup.field_db_backup_details__url +msgid "URL" +msgstr "" + +#. module: app_auto_backup +#: model:ir.module.category,description:app_auto_backup.module_management +msgid "User access level for this module" +msgstr "本模块用户权限" + +#. module: app_auto_backup +#: model:ir.model.fields,field_description:app_auto_backup.field_db_backup__sftp_user +msgid "Username SFTP Server" +msgstr "SFTP服务器用户名" + +#. module: app_auto_backup +#: model_terms:ir.ui.view,arch_db:app_auto_backup.view_backup_config_form +msgid "View Cron" +msgstr "查看定时任务" + +#. module: app_auto_backup +#: model:ir.model.fields,field_description:app_auto_backup.field_db_backup__sftp_write +msgid "Write to external server with sftp" +msgstr "SFtp备份至远程服务器:" + +#. module: app_auto_backup +#: model:ir.model.fields.selection,name:app_auto_backup.selection__db_backup__backup_type__zip +msgid "Zip" +msgstr "Zip压缩包" diff --git a/app_auto_backup/models/__init__.py b/app_auto_backup/models/__init__.py new file mode 100644 index 00000000..4d57b241 --- /dev/null +++ b/app_auto_backup/models/__init__.py @@ -0,0 +1,4 @@ +# -*- coding: utf-8 -*- + +from . import db_backup +from . import db_backup_details diff --git a/app_auto_backup/models/db_backup.py b/app_auto_backup/models/db_backup.py new file mode 100644 index 00000000..42b89c36 --- /dev/null +++ b/app_auto_backup/models/db_backup.py @@ -0,0 +1,360 @@ +# -*- coding: utf-8 -*- + +import os +import logging +import datetime +import time +import shutil +import subprocess +import json +import tempfile + +import odoo +from odoo import models, fields, api, tools, _ +from odoo.exceptions import ValidationError, AccessDenied +from odoo.tools.misc import exec_pg_environ, find_pg_tool + +_logger = logging.getLogger(__name__) + +try: + import paramiko +except ImportError: + raise ImportError( + 'This module needs paramiko to automatically write backups to the FTP through SFTP. ' + 'Please install paramiko on your system. (sudo pip3 install paramiko)') + + +class DbBackup(models.Model): + _name = 'db.backup' + _description = 'Backup configuration record' + + def _get_db_name(self): + dbName = self._cr.dbname + return dbName + + # Columns for local server configuration + host = fields.Char('Host', required=True, default='localhost') + port = fields.Char('Port', required=True, default=8069) + name = fields.Char('Database', required=True, help='Database you want to schedule backups for', + default=_get_db_name) + folder = fields.Char('Backup Directory', help='Absolute path for storing the backups', required=True, + default='/usr/lib/python3/dist-packages/odoo/backups') + backup_type = fields.Selection([('zip', 'Zip'), ('dump', 'Dump')], 'Backup Type', required=True, default='zip') + autoremove = fields.Boolean('Auto. Remove Backups', + help='If you check this option you can choose to automaticly remove the backup ' + 'after xx days') + days_to_keep = fields.Integer('Remove after x days', + help="Choose after how many days the backup should be deleted. For example:\n" + "If you fill in 5 the backups will be removed after 5 days.", + required=True) + + # Columns for external server (SFTP) + sftp_write = fields.Boolean('Write to external server with sftp', + help="If you check this option you can specify the details needed to write to a remote " + "server with SFTP.") + sftp_path = fields.Char('Path external server', + help='The location to the folder where the dumps should be written to. For example ' + '/odoo/backups/.\nFiles will then be written to /odoo/backups/ on your remote server.') + sftp_host = fields.Char('IP Address SFTP Server', + help='The IP address from your remote server. For example 192.168.0.1') + sftp_port = fields.Integer('SFTP Port', help='The port on the FTP server that accepts SSH/SFTP calls.', default=22) + sftp_user = fields.Char('Username SFTP Server', + help='The username where the SFTP connection should be made with. This is the user on the ' + 'external server.') + sftp_password = fields.Char('Password User SFTP Server', + help='The password from the user where the SFTP connection should be made with. This ' + 'is the password from the user on the external server.') + days_to_keep_sftp = fields.Integer('Remove SFTP after x days', + help='Choose after how many days the backup should be deleted from the FTP ' + 'server. For example:\nIf you fill in 5 the backups will be removed after ' + '5 days from the FTP server.', + default=30) + send_mail_sftp_fail = fields.Boolean('Auto. E-mail on backup fail', + help='If you check this option you can choose to automaticly get e-mailed ' + 'when the backup to the external server failed.') + email_to_notify = fields.Char('E-mail to notify', + help='Fill in the e-mail where you want to be notified that the backup failed on ' + 'the FTP.') + backup_details_ids = fields.One2many('db.backup.details', 'db_backup_id', 'Backup Details') + + def test_sftp_connection(self, context=None): + self.ensure_one() + + # Check if there is a success or fail and write messages + message_title = "" + message_content = "" + error = "" + has_failed = False + + for rec in self: + path_to_write_to = rec.sftp_path + ip_host = rec.sftp_host + port_host = rec.sftp_port + username_login = rec.sftp_user + password_login = rec.sftp_password + + # Connect with external server over SFTP, so we know sure that everything works. + try: + s = paramiko.SSHClient() + s.set_missing_host_key_policy(paramiko.AutoAddPolicy()) + s.connect(ip_host, port_host, username_login, password_login, timeout=10) + sftp = s.open_sftp() + sftp.close() + message_title = _("Connection Test Succeeded!\nEverything seems properly set up for FTP back-ups!") + except Exception as e: + _logger.critical('There was a problem connecting to the remote ftp: %s', str(e)) + error += str(e) + has_failed = True + message_title = _("Connection Test Failed!") + if len(rec.sftp_host) < 8: + message_content += "\nYour IP address seems to be too short.\n" + message_content += _("Here is what we got instead:\n") + finally: + if s: + s.close() + + if has_failed: + raise ValidationError(message_title + '\n\n' + message_content + "%s" % str(error)) + else: + raise ValidationError(message_title + '\n\n' + message_content) + + @api.model + def schedule_backup(self): + conf_ids = self.search([]) + for rec in conf_ids: + + try: + if not os.path.isdir(rec.folder): + os.makedirs(rec.folder) + except: + raise + # Create name for dumpfile. + bkp_file = '%s_%s.%s' % (time.strftime('%Y_%m_%d_%H_%M_%S'), rec.name, rec.backup_type) + file_path = os.path.join(rec.folder, bkp_file) + uri = 'http://' + rec.host + ':' + rec.port + bkp = '' + fp = open(file_path, 'wb') + try: + # try to backup database and write it away + fp = open(file_path, 'wb') + self._take_dump(rec.name, fp, 'db.backup', rec.backup_type) + fp.close() + rec.backup_details_ids.create({ + 'name': bkp_file, + 'file_path': file_path, + 'url': '/dbbackup/download%s' % file_path, + 'db_backup_id': rec.id, + }) + except Exception as error: + _logger.warning( + "Couldn't backup database %s. Bad database administrator password for server running at " + "http://%s:%s" % (rec.name, rec.host, rec.port)) + _logger.warning("Exact error from the exception: %s", str(error)) + continue + + # Check if user wants to write to SFTP or not. + if rec.sftp_write is True: + try: + # Store all values in variables + dir = rec.folder + path_to_write_to = rec.sftp_path + ip_host = rec.sftp_host + port_host = rec.sftp_port + username_login = rec.sftp_user + password_login = rec.sftp_password + _logger.debug('sftp remote path: %s' % path_to_write_to) + + try: + s = paramiko.SSHClient() + s.set_missing_host_key_policy(paramiko.AutoAddPolicy()) + s.connect(ip_host, port_host, username_login, password_login, timeout=20) + sftp = s.open_sftp() + except Exception as error: + _logger.critical('Error connecting to remote server! Error: %s', str(error)) + + try: + sftp.chdir(path_to_write_to) + except IOError: + # Create directory and subdirs if they do not exist. + current_directory = '' + for dirElement in path_to_write_to.split('/'): + current_directory += dirElement + '/' + try: + sftp.chdir(current_directory) + except: + _logger.info('(Part of the) path didn\'t exist. Creating it now at %s', current_directory) + # Make directory and then navigate into it + sftp.mkdir(current_directory, 777) + sftp.chdir(current_directory) + pass + sftp.chdir(path_to_write_to) + # Loop over all files in the directory. + for f in os.listdir(dir): + if rec.name in f: + fullpath = os.path.join(dir, f) + if os.path.isfile(fullpath): + try: + sftp.stat(os.path.join(path_to_write_to, f)) + _logger.debug( + 'File %s already exists on the remote FTP Server ------ skipped' % fullpath) + # This means the file does not exist (remote) yet! + except IOError: + try: + # sftp.put(fullpath, path_to_write_to) + sftp.put(fullpath, os.path.join(path_to_write_to, f)) + _logger.info('Copying File % s------ success' % fullpath) + except Exception as err: + _logger.critical( + 'We couldn\'t write the file to the remote server. Error: ' + str(err)) + + # Navigate in to the correct folder. + sftp.chdir(path_to_write_to) + + # Loop over all files in the directory from the back-ups. + # We will check the creation date of every back-up. + for file in sftp.listdir(path_to_write_to): + if rec.name in file: + # Get the full path + fullpath = os.path.join(path_to_write_to, file) + # Get the timestamp from the file on the external server + timestamp = sftp.stat(fullpath).st_mtime + createtime = datetime.datetime.fromtimestamp(timestamp) + now = datetime.datetime.now() + delta = now - createtime + # If the file is older than the days_to_keep_sftp (the days to keep that the user filled in + # on the Odoo form it will be removed. + if delta.days >= rec.days_to_keep_sftp: + # Only delete files, no directories! + if ".dump" in file or '.zip' in file: + _logger.info("Delete too old file from SFTP servers: %s", file) + sftp.unlink(file) + # Close the SFTP session. + sftp.close() + except Exception as e: + _logger.debug('Exception! We couldn\'t back up to the FTP server..') + # At this point the SFTP backup failed. We will now check if the user wants + # an e-mail notification about this. + if rec.send_mail_sftp_fail: + try: + ir_mail_server = self.env['ir.mail_server'].search([], order='sequence asc', limit=1) + message = "Dear,\n\nThe backup for the server " + rec.host + " (IP: " + rec.sftp_host + \ + ") failed. Please check the following details:\n\nIP address SFTP server: " + \ + rec.sftp_host + "\nUsername: " + rec.sftp_user + \ + "\n\nError details: " + tools.ustr(e) + \ + "\n\nWith kind regards" + catch_all_domain = self.env["ir.config_parameter"].sudo().get_param("mail.catchall.domain") + response_mail = "auto_backup@%s" % catch_all_domain if catch_all_domain else self.env.user.partner_id.email + msg = ir_mail_server.build_email(response_mail, [rec.email_to_notify], + "Backup from " + rec.host + "(" + rec.sftp_host + + ") failed", + message) + ir_mail_server.send_email(msg) + except Exception: + pass + + """ + Remove all old files (on local server) in case this is configured.. + """ + if rec.autoremove: + directory = rec.folder + # Loop over all files in the directory. + for f in os.listdir(directory): + fullpath = os.path.join(directory, f) + # Only delete the ones wich are from the current database + # (Makes it possible to save different databases in the same folder) + if rec.name in fullpath: + timestamp = os.stat(fullpath).st_ctime + createtime = datetime.datetime.fromtimestamp(timestamp) + now = datetime.datetime.now() + delta = now - createtime + if delta.days >= rec.days_to_keep: + # Only delete files (which are .dump and .zip), no directories. + if os.path.isfile(fullpath) and (".dump" in f or '.zip' in f): + _logger.info("Delete local out-of-date file: %s", fullpath) + backup_details_id = self.env['db.backup.details'].search([('file_path', '=', fullpath)]) + if backup_details_id: + backup_details_id.unlink() + else: + os.remove(fullpath) + + # This is more or less the same as the default Odoo function at + # https://github.com/odoo/odoo/blob/e649200ab44718b8faefc11c2f8a9d11f2db7753/odoo/service/db.py#L209 + # The main difference is that we do not do have a wrapper for the function check_db_management_enabled here and + # that we authenticate based on the cron its user id and by checking if we have 'db.backup' defined in the function + # call. Since this function is called from the cron and since we have these security checks on model and on user_id + # its pretty impossible to hack any way to take a backup. This allows us to disable the Odoo database manager + # which is a MUCH safer way + def _take_dump(self, db_name, stream, model, backup_format='zip'): + """Dump database `db` into file-like object `stream` if stream is None + return a file object with the dump """ + + cron_user_id = self.env.ref('app_auto_backup.backup_scheduler').user_id.id + if self._name != 'db.backup' or cron_user_id != self.env.user.id: + _logger.error('Unauthorized database operation. Backups should only be available from the cron job.') + raise AccessDenied() + + _logger.info('DUMP DB: %s format %s', db_name, backup_format) + + cmd = [find_pg_tool('pg_dump'), '--no-owner', db_name] + env = exec_pg_environ() + if backup_format == 'zip': + with tempfile.TemporaryDirectory() as dump_dir: + filestore = odoo.tools.config.filestore(db_name) + if os.path.exists(filestore): + shutil.copytree(filestore, os.path.join(dump_dir, 'filestore')) + with open(os.path.join(dump_dir, 'manifest.json'), 'w') as fh: + db = odoo.sql_db.db_connect(db_name) + with db.cursor() as cr: + json.dump(self._dump_db_manifest(cr), fh, indent=4) + cmd.insert(-1, '--file=' + os.path.join(dump_dir, 'dump.sql')) + subprocess.run(cmd, env=env, stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT, check=True) + if stream: + odoo.tools.osutil.zip_dir(dump_dir, stream, include_dir=False, fnct_sort=lambda file_name: file_name != 'dump.sql') + else: + t=tempfile.TemporaryFile() + odoo.tools.osutil.zip_dir(dump_dir, t, include_dir=False, fnct_sort=lambda file_name: file_name != 'dump.sql') + t.seek(0) + return t + else: + cmd.insert(-1, '--format=c') + process = subprocess.Popen(cmd, env=env, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + stdout, _ = process.communicate() + if stream: + # shutil.copyfileobj(stdout, stream) + stream.write(stdout) + else: + return stdout + + def _dump_db_manifest(self, cr): + pg_version = "%d.%d" % divmod(cr._obj.connection.server_version / 100, 100) + cr.execute("SELECT name, latest_version FROM ir_module_module WHERE state = 'installed'") + modules = dict(cr.fetchall()) + manifest = { + 'odoo_dump': '1', + 'db_name': cr.dbname, + 'version': odoo.release.version, + 'version_info': odoo.release.version_info, + 'major_version': odoo.release.major_version, + 'pg_version': pg_version, + 'modules': modules, + } + return manifest + + def action_view_cron(self): + self.ensure_one() + + action = self.env.ref('base.ir_cron_act', False).sudo().read()[0] + cron = self.env.ref('app_auto_backup.backup_scheduler', False) + if action and cron: + action['views'] = [(self.env.ref('base.ir_cron_view_form').id, 'form')] + action['res_id'] = cron.id + return action + else: + return False + + def action_run_cron(self): + self.ensure_one() + cron = self.env.ref('app_auto_backup.backup_scheduler', False) + if cron: + cron.method_direct_trigger() + return True diff --git a/app_auto_backup/models/db_backup_details.py b/app_auto_backup/models/db_backup_details.py new file mode 100644 index 00000000..70d4b26e --- /dev/null +++ b/app_auto_backup/models/db_backup_details.py @@ -0,0 +1,41 @@ +# -*- coding: utf-8 -*- + +import os + +from odoo import api, fields, models, _ +from odoo.exceptions import AccessError, UserError + + +class DbBackupDetails(models.Model): + _name = 'db.backup.details' + _description = 'Database Backup Details' + + name = fields.Char(string='Name') + file_path = fields.Char(string="File Path") + url = fields.Char(string='URL') + db_backup_id = fields.Many2one('db.backup', 'Database Backup') + + def action_download_file(self): + self.ensure_one() + if not self.file_path or not self.url: + raise UserError(_("File Path or URL not found.")) + else: + return { + 'type': 'ir.actions.act_url', + 'url': self.url, + 'target': 'new', + } + + def unlink(self): + for rec in self: + try: + if rec.file_path: + if os.path.exists(rec.file_path): + os.remove(rec.file_path) + except Exception as e: + pass + return super(DbBackupDetails, self).unlink() + + def action_remove_file(self): + self.ensure_one() + self.unlink() diff --git a/app_auto_backup/security/ir.model.access.csv b/app_auto_backup/security/ir.model.access.csv new file mode 100644 index 00000000..6199afa0 --- /dev/null +++ b/app_auto_backup/security/ir.model.access.csv @@ -0,0 +1,4 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +admin_access, db_backup admin access,model_db_backup,base.group_no_one,1,1,1,1 +admin_security_rule, Model db_backup admin access,model_db_backup,app_auto_backup.group_manager,1,1,1,1 +admin_db_backup_details, Model db_backup_details admin access,model_db_backup_details,app_auto_backup.group_manager,1,1,1,1 diff --git a/app_auto_backup/security/user_groups.xml b/app_auto_backup/security/user_groups.xml new file mode 100644 index 00000000..be904895 --- /dev/null +++ b/app_auto_backup/security/user_groups.xml @@ -0,0 +1,19 @@ + + + + + Auto backup access + User access level for this module + 3 + + + + Manager + + + + + + + + diff --git a/app_auto_backup/static/description/banner.png b/app_auto_backup/static/description/banner.png new file mode 100644 index 00000000..9693ea85 Binary files /dev/null and b/app_auto_backup/static/description/banner.png differ diff --git a/app_auto_backup/static/description/banner1.png b/app_auto_backup/static/description/banner1.png new file mode 100644 index 00000000..2bf46765 Binary files /dev/null and b/app_auto_backup/static/description/banner1.png differ diff --git a/app_auto_backup/static/description/demo1.jpg b/app_auto_backup/static/description/demo1.jpg new file mode 100644 index 00000000..cac2d3fb Binary files /dev/null and b/app_auto_backup/static/description/demo1.jpg differ diff --git a/app_auto_backup/static/description/demo2.jpg b/app_auto_backup/static/description/demo2.jpg new file mode 100644 index 00000000..513a46fd Binary files /dev/null and b/app_auto_backup/static/description/demo2.jpg differ diff --git a/app_auto_backup/static/description/demo3.jpg b/app_auto_backup/static/description/demo3.jpg new file mode 100644 index 00000000..f0e66f09 Binary files /dev/null and b/app_auto_backup/static/description/demo3.jpg differ diff --git a/app_auto_backup/static/description/demo4.jpg b/app_auto_backup/static/description/demo4.jpg new file mode 100644 index 00000000..abd60347 Binary files /dev/null and b/app_auto_backup/static/description/demo4.jpg differ diff --git a/app_auto_backup/static/description/emailnotification.png b/app_auto_backup/static/description/emailnotification.png new file mode 100644 index 00000000..14fce118 Binary files /dev/null and b/app_auto_backup/static/description/emailnotification.png differ diff --git a/app_auto_backup/static/description/icon.png b/app_auto_backup/static/description/icon.png new file mode 100644 index 00000000..669657d8 Binary files /dev/null and b/app_auto_backup/static/description/icon.png differ diff --git a/app_auto_backup/static/description/index.html b/app_auto_backup/static/description/index.html new file mode 100644 index 00000000..0a73217e --- /dev/null +++ b/app_auto_backup/static/description/index.html @@ -0,0 +1,267 @@ + + + + +
+

Database auto backup. Easy schedule backup and Download

+

数据库自动备份及快速下载

+
+ + +
+
+
+
+

Latest update: v18.24.11.06

+
+ +
+
+
+ Key features: +
    +
  • + + 1. Easy schedule database backup +
  • +
  • + + 2. Set remote backup and cron schedule +
  • +
  • + + 3. Manual backup database in one click +
  • +
  • + + 4. Easy download backup file or remove file for System user +
  • +
  • + + 11. Multi-language Support. Multi-Company Support. +
  • +
  • + + 12. Support Odoo 18,17,16,15,14,13,12, Enterprise and Community and odoo.sh Edition. +
  • +
  • + + 13. Full Open Source. +
  • +
+
+
+
+
+
+
+ + +
+
+

1. Easy schedule database backup

+

快速按日程自动备份,方便的直接下载

+
+ +
+
+
+ +
+
+

2. Set remote backup and cron schedule

+

支持远程备份

+
+ +
+
+
+ +
+
+

3. Manual backup database in one click

+

一键手动备份

+
+ +
+
+
+ +
+
+

4. Easy download backup file or remove file for System user

+

系统用户可以直接下载所有数据库备份文件,进行备份管理

+
+ +
+
+
+ + +
+
+
+

Database Automated backups

+

optimized from auto_backup of Yenthe Van Ginneken

+

A tool for all your back-ups, internal and external!

+
+
+
+ +
+
+
+

+ Keep your Odoo data safe with this module. Take automated back-ups, remove them automatically + and even write them to an external server through an encrypted tunnel. + You can even specify how long local backups and external backups should be kept, automatically! +

+ +
+
+
+ +
+
+

Connect with an FTP Server

+

Keep your data safe, through an SSH tunnel!

+
+

+ Want to go even further and write your backups to an external server? + You can with this module! Specify the credentials to the server, specify a path and everything will be backed up automatically. This is done through an SSH (encrypted) tunnel, thanks to pysftp, so your data is safe! + +

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

Test connection

+

Checks your credentials in one click

+
+
+
+ + +
+
+
+

+ Want to make sure if the connection details are correct and if Odoo can automatically write them to the remote server? Simply click on the 'Test SFTP Connection' button and you will get message telling you if everything is OK, or what is wrong! +

+
+
+
+ +
+
+

E-mail on backup failure

+

Stay informed of problems, automatically!

+
+

+ Do you want to know if the database backup failed? Check the checkbox 'Auto. E-mail on backup fail' and fill in your e-mail. + Every time a backup fails you will get an e-mail in your mailbox with technical details. +

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

Contact / Support

+

Need help or want extra features?

+
+
+

+ Need help with the configuration or want this module to have more functionalities? + Please create a bug report on the Github issue tracker +

+
+
+
+ + + + + +
+

- How to setup and use -

+

This app need no extra module. The price already included

+
+
+
+

+ 1. Buy and Install +

+

+ 2. Read the app description for user guide +

+

+ 3. Enjoy and easy use +

+
+
+ +
+
+

4. More information in our FAQ

+
+ https://www.odooai.cn/faq +
+
+
+ + + + + + +
+
+
+

Technical Help & Support

+
+
+
+

+ For any type of technical help & support requests, Feel free to contact us

+ + odoo@china.com +

+ Via QQ: 300883 (App user would not get QQ or any other IM support. Only for odoo project customize.)

+ + 300883@qq.com +
+
+

Visit our website for more support.

+

https://www.odooai.cn

+
+
+
+
+ + diff --git a/app_auto_backup/static/description/overview.png b/app_auto_backup/static/description/overview.png new file mode 100644 index 00000000..f531afd4 Binary files /dev/null and b/app_auto_backup/static/description/overview.png differ diff --git a/app_auto_backup/static/description/terminalssh.png b/app_auto_backup/static/description/terminalssh.png new file mode 100644 index 00000000..15165f1e Binary files /dev/null and b/app_auto_backup/static/description/terminalssh.png differ diff --git a/app_auto_backup/static/description/testconnection.png b/app_auto_backup/static/description/testconnection.png new file mode 100644 index 00000000..e6d5eea8 Binary files /dev/null and b/app_auto_backup/static/description/testconnection.png differ diff --git a/app_auto_backup/static/description/testconnectionfailed.png b/app_auto_backup/static/description/testconnectionfailed.png new file mode 100644 index 00000000..bdbeb81d Binary files /dev/null and b/app_auto_backup/static/description/testconnectionfailed.png differ diff --git a/app_auto_backup/views/backup_view.xml b/app_auto_backup/views/backup_view.xml new file mode 100644 index 00000000..fa73d954 --- /dev/null +++ b/app_auto_backup/views/backup_view.xml @@ -0,0 +1,112 @@ + + + + db.backup.form + db.backup + form + +
+ +
+
+ + + + + + + + + + + + + + + +
+ Warning: + Use SFTP with caution! This writes files to external servers under the path you specify. +
+ + + + + + + + + + + + +
+ +
+
+ +
+ + + + + \ No newline at end of file diff --git a/app_chatgpt/static/src/models/message_model.js b/app_chatgpt/static/src/models/message_model.js new file mode 100644 index 00000000..f829cd7a --- /dev/null +++ b/app_chatgpt/static/src/models/message_model.js @@ -0,0 +1,39 @@ +/** @odoo-module **/ +import { Message } from "@mail/core/common/message_model"; +import { assignDefined } from "@mail/utils/common/misc"; +import { patch } from "@web/core/utils/patch"; + +// 参考模块 whatsapp + +patch(Message.prototype, { + update(data) { + assignDefined(this, data, ["human_prompt_tokens", "ai_completion_tokens", "is_ai"]); + super.update(data); + }, +}); + +// +// registerPatch({ +// name: 'Message', +// modelMethods: { +// convertData(data) { +// const data2 = this._super(data); +// if ('human_prompt_tokens' in data) { +// data2.human_prompt_tokens = data.human_prompt_tokens; +// } +// if ('ai_completion_tokens' in data) { +// data2.ai_completion_tokens = data.ai_completion_tokens; +// } +// if ('is_ai' in data) { +// data2.is_ai = data.is_ai; +// } +// return data2; +// }, +// }, +// fields: { +// human_prompt_tokens: attr(), +// ai_completion_tokens: attr(), +// is_ai: attr(), +// +// } +// }) \ No newline at end of file diff --git a/app_chatgpt/views/ai_robot_views.xml b/app_chatgpt/views/ai_robot_views.xml new file mode 100644 index 00000000..3f38b16d --- /dev/null +++ b/app_chatgpt/views/ai_robot_views.xml @@ -0,0 +1,170 @@ + + + + ai.robot.tree + ai.robot + + + + + + + + + + + + + + + + ai.robot.kanban + ai.robot + + + + + + + + + + + + +
+
+ Robot Provider +
+
+
+
+ + + +
+
+
+ Model: + +
+
+ + + + Bind Partner +
+
+
+
+
+
+
+
+ + + ai.robot.form + ai.robot + +
+
+
+ +
+ +
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+ + + ai.robot.search + ai.robot + + + + + + + + + + + + + + Ai Robot + ai.robot + kanban,list,form + +

+ Let's create a Ai Robot. +

+
+
+ + + Disconnect + + + kanban,list,form + code + action = records.action_disconnect() + + + + +
diff --git a/app_chatgpt/views/discuss_channel_views.xml b/app_chatgpt/views/discuss_channel_views.xml new file mode 100644 index 00000000..c2e7d1f4 --- /dev/null +++ b/app_chatgpt/views/discuss_channel_views.xml @@ -0,0 +1,122 @@ + + + + + + discuss.channel + ai.discuss.channel.tree + + + + + + + + + + + + ai.discuss.channel.form + discuss.channel + extension + + + + + + + + + + + +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ + + +
+
+
+
+ + + +

0-3,设定后,会将最近n次对话发给Ai,有助于他更好的回答

+ +

最大响应Token,控制返回内容长度

+ +

0-1,值越大越富有想像力,越小则越保守

+ +

0-1,值越大越少使用常用词

+ +

0-1,值越大越少重复词

+
+
+
+
+ + + + discuss.channel + + extension + + + + + + 角色: + + + + + + + + + + + + + + discuss.channel + + + + + + + + + + +
+
diff --git a/app_chatgpt/views/mail_message_views.xml b/app_chatgpt/views/mail_message_views.xml new file mode 100644 index 00000000..d08cbac5 --- /dev/null +++ b/app_chatgpt/views/mail_message_views.xml @@ -0,0 +1,29 @@ + + + + + + mail.message + ai.mail.message.tree + + + + + + + + + + + ai.mail.message.form + mail.message + + + + + + + + + + diff --git a/app_chatgpt/views/res_partner_ai_use_views.xml b/app_chatgpt/views/res_partner_ai_use_views.xml new file mode 100644 index 00000000..2ee80295 --- /dev/null +++ b/app_chatgpt/views/res_partner_ai_use_views.xml @@ -0,0 +1,91 @@ + + + + res.partner.ai.use.tree + res.partner.ai.use + + + + + + + + + + + + + + + + + + + + + res.partner.ai.use.form + res.partner.ai.use + +
+ + +
+
+
+ + + res.partner.ai.use.search + res.partner.ai.use + + + + + + + + + + Partner Ai Use + res.partner.ai.use + list,form + {'create': 0, 'delete': 0} + + + + + Partner Ai Use + res.partner.ai.use + list,form + [('ai_user_id', 'in', active_ids)] + {'default_ai_user_id':active_id,} + + + +
diff --git a/app_chatgpt/views/res_users_views.xml b/app_chatgpt/views/res_users_views.xml new file mode 100644 index 00000000..1537bb66 --- /dev/null +++ b/app_chatgpt/views/res_users_views.xml @@ -0,0 +1,53 @@ + + + + app.chat.res.users.tree + res.users + + + + + + + + + app.chatgpt.res.users.form + res.users + + + + + + + + + + + + + + + + + + + + + + + app.res.users.search + res.users + + + + + + + + + + + + + diff --git a/app_odoo_customize/__manifest__.py b/app_odoo_customize/__manifest__.py index 64bd54d2..b6110bef 100644 --- a/app_odoo_customize/__manifest__.py +++ b/app_odoo_customize/__manifest__.py @@ -23,7 +23,7 @@ { 'name': 'odoo18 Tweak,Ai Employee,Boost,Customize All in One. Customize,UI,Boost,Security,Data,Development Enhance', - 'version': '24.11.04', + 'version': '24.11.06', 'author': 'odooai.cn', 'category': 'Extra Tools', 'website': 'https://www.odooai.cn', @@ -73,6 +73,7 @@ 'app_odoo_customize/static/src/js/user_menu.js', 'app_odoo_customize/static/src/js/ribbon.js', 'app_odoo_customize/static/src/js/dialog.js', + 'app_odoo_customize/static/src/js/base_import_list_renderer.js', 'app_odoo_customize/static/src/webclient/*.js', 'app_odoo_customize/static/src/webclient/user_menu.xml', 'app_odoo_customize/static/src/xml/res_config_edition.xml', diff --git a/app_odoo_customize/models/ir_module_module.py b/app_odoo_customize/models/ir_module_module.py index 5029d324..a41ba3e0 100644 --- a/app_odoo_customize/models/ir_module_module.py +++ b/app_odoo_customize/models/ir_module_module.py @@ -20,6 +20,7 @@ class IrModuleModule(models.Model): addons_path_id = fields.Many2one('ir.module.addons.path', string='Addons Path ID', readonly=True) addons_path = fields.Char(string='Addons Path', related='addons_path_id.path', readonly=True) license = fields.Char(readonly=True) + module_type = fields.Selection(selection_add=[('odooapp.cn', 'Odoo中文应用')]) def module_multi_uninstall(self): """ Perform the various steps required to uninstall a module completely @@ -87,5 +88,18 @@ class IrModuleModule(models.Model): local_updatable = False if mod.local_updatable != local_updatable: mod.write({'local_updatable': local_updatable}) - return res + + def _update_from_terp(self, terp): + res = super()._update_from_terp(terp) + author = terp.get('author') + if author in ['odooai.cn', 'sunpop.cn', 'odooapp.cn']: + self.module_type = 'odooapp.cn' + return res + + def web_read(self, specification): + fields = list(specification.keys()) + module_type = self.env.context.get('module_type', 'official') + if module_type == 'odooapp.cn': + self.env.context = {**self.env.context, "module_type": 'official'} + return super().web_read(specification) diff --git a/app_odoo_customize/static/src/js/base_import_list_renderer.js b/app_odoo_customize/static/src/js/base_import_list_renderer.js new file mode 100644 index 00000000..741999bd --- /dev/null +++ b/app_odoo_customize/static/src/js/base_import_list_renderer.js @@ -0,0 +1,26 @@ +/** @odoo-module */ + +import { patch } from "@web/core/utils/patch"; +import { ImportModuleListRenderer } from "@base_import_module/base_import_list_renderer"; + +patch(ImportModuleListRenderer.prototype, { + async onCellClicked(record, column, ev) { + if (record._values.module_type && record._values.module_type === 'odooapp.cn') { + const re_action = { + name: "more_info", + res_model: "ir.module.module", + res_id: record.resId, + type: "ir.actions.act_window", + views: [[false, "form"]], + context: { + 'module_name': record._values.name, + 'module_type': record._values.module_type, + } + } + this.env.services.action.doAction(re_action); + } + else{ + super.onCellClicked(record, column, ev); + } + } +}) diff --git a/app_odoo_customize/views/ir_module_addons_path_views.xml b/app_odoo_customize/views/ir_module_addons_path_views.xml index be5de703..d873efba 100644 --- a/app_odoo_customize/views/ir_module_addons_path_views.xml +++ b/app_odoo_customize/views/ir_module_addons_path_views.xml @@ -72,8 +72,8 @@ Addons Paths ir.module.addons.path - kanban,tree,form + kanban,list,form {} [] -
\ No newline at end of file + diff --git a/app_odoo_customize/views/ir_module_module_views.xml b/app_odoo_customize/views/ir_module_module_views.xml index 63e35a56..265acc85 100644 --- a/app_odoo_customize/views/ir_module_module_views.xml +++ b/app_odoo_customize/views/ir_module_module_views.xml @@ -36,6 +36,9 @@ + + + @@ -51,6 +54,18 @@ + + to_buy or state != 'uninstalled' or (module_type and module_type not in ('official', 'odooapp.cn')) + + + state == 'uninstalled' or (module_type and module_type not in ('official', 'odooapp.cn')) + + + state != 'uninstalled' or module_type in ('official', 'odooapp.cn') + + + state == 'uninstalled' or module_type in ('official', 'odooapp.cn') + @@ -67,6 +82,15 @@ + + state != 'uninstalled' or (module_type and module_type not in ('official', 'odooapp.cn')) + + + state != 'uninstalled' or module_type in ('official', 'odooapp.cn') + + + state == 'uninstalled' or module_type in ('official', 'odooapp.cn') +