diff --git a/soong_workflow/__init__.py b/soong_workflow/__init__.py index cde864bae..3616b3002 100644 --- a/soong_workflow/__init__.py +++ b/soong_workflow/__init__.py @@ -1,3 +1,4 @@ # -*- coding: utf-8 -*- from . import models +from . import wizard \ No newline at end of file diff --git a/soong_workflow/__manifest__.py b/soong_workflow/__manifest__.py index 4ed811fde..0f2db46f0 100644 --- a/soong_workflow/__manifest__.py +++ b/soong_workflow/__manifest__.py @@ -17,7 +17,8 @@ "views/soong_workflow_node_views.xml", "views/soong_workflow_link_views.xml", "views/soong_workflow_views.xml", - 'views/soong_workflow_record.xml' + 'views/soong_workflow_record.xml', + 'wizard/approval_notice_wizard_views.xml' ], "installable": True, "auto_install": False, diff --git a/soong_workflow/models/soong_approval_users.py b/soong_workflow/models/soong_approval_users.py index 95ee95b51..d91d0b388 100644 --- a/soong_workflow/models/soong_approval_users.py +++ b/soong_workflow/models/soong_approval_users.py @@ -26,7 +26,7 @@ class SoongApprovalUsers(models.Model): approval_user = fields.Many2one('soong.user.role', string='审批角色') approval_user_datetime = fields.Datetime(string='审批时间') approval_user_notice = fields.Char(string='审批意见') - approval_user_sign = fields.Char(string='审批签署') + approval_user_sign = fields.Binary(string='审批签署') approval_user_state = fields.Selection([('will', '待审批')], string='状态') @api.model_create_multi diff --git a/soong_workflow/models/soong_workflow_mixin.py b/soong_workflow/models/soong_workflow_mixin.py index 368efbc68..3265b298a 100644 --- a/soong_workflow/models/soong_workflow_mixin.py +++ b/soong_workflow/models/soong_workflow_mixin.py @@ -148,18 +148,18 @@ class SoongWorkflowMixin(models.AbstractModel): @api.model def get_view(self, view_id=None, view_type="form", **options): res = super().get_view(view_id, view_type, **options) - - if view_type in ["tree", "form"]: - if "field" in res["arch"]: - # res = self._remove_default_header(view_type, res) - res = self._remove_default_modifiers(view_type, res) - res = self._load_buttons_view(view_type, res) - res = self._load_button_box_view(view_type, res) - res = self._load_state_view(view_type, res) - res = self._load_one2many_views(view_type, res) - res = self._update_fields_view(view_type, res) - # 列表视图增加状态字段 - # res = self._add_state_tree_view(view_type, res) + if 'create="false"' not in res['arch']: # 针对一个模型多个form临时做的区分 + if view_type in ["tree", "form"]: + if "field" in res["arch"]: + # res = self._remove_default_header(view_type, res) + res = self._remove_default_modifiers(view_type, res) + res = self._load_buttons_view(view_type, res) + res = self._load_button_box_view(view_type, res) + res = self._load_state_view(view_type, res) + res = self._load_one2many_views(view_type, res) + res = self._update_fields_view(view_type, res) + # 列表视图增加状态字段 + # res = self._add_state_tree_view(view_type, res) return res def _remove_default_header(self, view_type, res): diff --git a/soong_workflow/security/ir.model.access.csv b/soong_workflow/security/ir.model.access.csv index e7766b223..22fa7a293 100644 --- a/soong_workflow/security/ir.model.access.csv +++ b/soong_workflow/security/ir.model.access.csv @@ -17,4 +17,6 @@ access_soong_workflow_link_manager,soong_workflow_link_manager,model_soong_workf access_soong_workflow_record_user,soong_workflow_record_user,model_soong_workflow_record,soong_workflow.group_soong_workflow_user,1,1,1,1 access_soong_user_role,soong_user_role,model_soong_user_role,soong_workflow.group_soong_workflow_user,1,1,1,1 access_soong_approval_users,soong_approval_users,model_soong_approval_users,soong_workflow.group_soong_workflow_user,1,1,1,1 -access_soong_note,soong_note,model_soong_note,soong_workflow.group_soong_workflow_user,1,1,1,1 \ No newline at end of file +access_soong_note,soong_note,model_soong_note,soong_workflow.group_soong_workflow_user,1,1,1,1 + +access_soong_approval_notice_wizard,soong_approval_notice_wizard,model_soong_approval_notice_wizard,base.group_user,1,1,1,1 diff --git a/soong_workflow/views/soong_approval_users_views.xml b/soong_workflow/views/soong_approval_users_views.xml index fe5ef2886..fb76a2936 100644 --- a/soong_workflow/views/soong_approval_users_views.xml +++ b/soong_workflow/views/soong_approval_users_views.xml @@ -13,7 +13,7 @@ - + diff --git a/soong_workflow/wizard/__init__.py b/soong_workflow/wizard/__init__.py new file mode 100644 index 000000000..f048f7d3d --- /dev/null +++ b/soong_workflow/wizard/__init__.py @@ -0,0 +1,5 @@ +# + +from . import approval_notice_wizard + + diff --git a/soong_workflow/wizard/approval_notice_wizard.py b/soong_workflow/wizard/approval_notice_wizard.py new file mode 100644 index 000000000..c439f2e7f --- /dev/null +++ b/soong_workflow/wizard/approval_notice_wizard.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- +from odoo import api, fields, models, tools, _ + + +class SoongApprovalNoticeWizard(models.TransientModel): + _name = 'soong.approval.notice.wizard' + _description = """ 审批意见弹窗 """ + + # approval_res_id = fields.Char(string="审批明细行") + # approval_res_model = fields.Char(string="审批明细行") + + approval_user_notice = fields.Char(string='审批意见') + approval_user_sign = fields.Binary(string="审批签署", copy=False) + + def confirm(self): + self.ensure_one() + active_model = self.env.context.get("active_model") + active_id = self.env.context.get("active_id") + self.env["soong.approval.users"].create({ + 'res_model_id': self.env['ir.model']._get(active_model).id, + 'res_id': active_id, + 'approval_user_notice': self.approval_user_notice, + 'approval_user_id': self.env.user.id, + 'approval_user_datetime': fields.datetime.now(), + 'approval_user_sign': self.approval_user_sign, + }) diff --git a/soong_workflow/wizard/approval_notice_wizard_views.xml b/soong_workflow/wizard/approval_notice_wizard_views.xml new file mode 100644 index 000000000..f2da5adb7 --- /dev/null +++ b/soong_workflow/wizard/approval_notice_wizard_views.xml @@ -0,0 +1,34 @@ + + + 审批意见弹窗 + soong.approval.notice.wizard + +
+ + + + + + + + +
+
+
+
+
+ + 审批意见弹窗 + ir.actions.act_window + soong.approval.notice.wizard + + + + + + form + new + +
\ No newline at end of file diff --git a/web_editor_tinymce/__manifest__.py b/web_editor_tinymce/__manifest__.py new file mode 100644 index 000000000..947706809 --- /dev/null +++ b/web_editor_tinymce/__manifest__.py @@ -0,0 +1,19 @@ +{ + 'name': 'TinyMCE Editor', + 'summary': 'TinyMCE Editor for Html Field', + 'author': 'Hoang Minh Hieu', + 'version': '17.0.1.0.0', + 'license': 'OPL-1', + 'support ': 'hieuhoangminh1996@gmail.com', + 'depends': ['web'], + 'assets': { + 'web.assets_backend': [ + 'web_editor_tinymce/static/src/field_tinymce/field_tinymce.scss', + 'web_editor_tinymce/static/src/field_tinymce/field_tinymce.xml', + 'web_editor_tinymce/static/src/field_tinymce/field_tinymce.js', + ], + }, + 'images': ['static/description/main_screenshot.png'], + 'price': 35, + 'currency': 'USD', +} diff --git a/web_editor_tinymce/static/description/icon.png b/web_editor_tinymce/static/description/icon.png new file mode 100644 index 000000000..15e5ed47f Binary files /dev/null and b/web_editor_tinymce/static/description/icon.png differ diff --git a/web_editor_tinymce/static/description/index.html b/web_editor_tinymce/static/description/index.html new file mode 100644 index 000000000..32e00ac75 --- /dev/null +++ b/web_editor_tinymce/static/description/index.html @@ -0,0 +1,36 @@ + + + + + This module replaces Odoo html editor with TinyMCE editor. + + + +
+

+ This module replaces Odoo html editor with TinyMCE editor. +

+
+
+

Screenshot

+ + +

Usage

+ +
+
+

+ Contact me +

+

+ Skype: live:hieuhoangminh1996 +
+ Email: hieuhoangminh1996@gmail.com +
+
+ Email subject should begin with + [Odoo Module] +

+
+ + diff --git a/web_editor_tinymce/static/description/main_screenshot.png b/web_editor_tinymce/static/description/main_screenshot.png new file mode 100644 index 000000000..2e999d5ca Binary files /dev/null and b/web_editor_tinymce/static/description/main_screenshot.png differ diff --git a/web_editor_tinymce/static/description/usage.png b/web_editor_tinymce/static/description/usage.png new file mode 100644 index 000000000..25524eedc Binary files /dev/null and b/web_editor_tinymce/static/description/usage.png differ diff --git a/web_editor_tinymce/static/src/field_tinymce/field_tinymce.js b/web_editor_tinymce/static/src/field_tinymce/field_tinymce.js new file mode 100644 index 000000000..946769557 --- /dev/null +++ b/web_editor_tinymce/static/src/field_tinymce/field_tinymce.js @@ -0,0 +1,130 @@ +/** @odoo-module **/ + +import {_t} from '@web/core/l10n/translation'; +import {Component, onWillStart, onMounted, onPatched, onWillUnmount, useEffect} from '@odoo/owl'; +import {standardFieldProps} from '@web/views/fields/standard_field_props'; +import {registry} from '@web/core/registry'; +import {TranslationButton} from '@web/views/fields/translation_button'; +import {loadJS} from '@web/core/assets'; +import {cookie} from '@web/core/browser/cookie'; + +export class FieldTinyMCE extends Component { + static template = 'web_editor_tinymce.TinyMCE'; + static components = { + TranslationButton, + }; + static props = { + ...standardFieldProps, + placeholder: {type: String, optional: true}, + dynamicPlaceholder: {type: Boolean, optional: true}, + dynamicPlaceholderModelReferenceField: {type: String, optional: true}, + }; + static defaultProps = { + dynamicPlaceholder: false, + }; + + setup() { + super.setup(); + this.editorId = `tinymce_${this.props.name}` + const useDarkMode = this.theme === 'dark'; + this.defaultTinymceConfig = { + selector: `#${this.editorId}`, + plugins: 'autoresize preview importcss searchreplace autolink directionality code visualblocks visualchars fullscreen image link media codesample table charmap pagebreak nonbreaking anchor insertdatetime advlist lists wordcount help charmap quickbars emoticons accordion', + editimage_cors_hosts: ['picsum.photos'], + image_advtab: true, + importcss_append: true, + height: 'auto', + image_caption: true, + quickbars_selection_toolbar: 'bold italic | quicklink h2 h3 blockquote quickimage quicktable', + noneditable_class: 'mceNonEditable', + toolbar_mode: 'sliding', + contextmenu: 'link image table', + skin: useDarkMode ? 'oxide-dark' : 'oxide', + content_css: useDarkMode ? 'dark' : 'default', + content_style: 'body { font-family:Helvetica,Arial,sans-serif; font-size:16px }', + placeholder: this.props.placeholder, + } + onWillStart(async () => { + await loadJS('web_editor_tinymce/static/lib/tinymce/tinymce.min.js'); + }); + onMounted(async () => { + await this.initEditor(); + }) + useEffect(() => { + this.updateEditor(); + }) + onWillUnmount(async () => { + this.removeEditor() + }) + } + + get value() { + return this.props.record.data[this.props.name] || ''; + } + + get theme() { + return cookie.get('color_scheme'); + } + + get isTranslatable() { + return this.props.record.fields[this.props.name].translate; + } + + get tinymceConfig() { + return { + ...this.defaultTinymceConfig, + menubar: !this.props.readonly && 'file edit view insert format tools table help', + toolbar: !this.props.readonly && 'undo redo | accordion accordionremove | blocks fontfamily fontsize | bold italic underline strikethrough | align numlist bullist | link image | table media | lineheight outdent indent| forecolor backcolor removeformat | charmap emoticons | code fullscreen preview | save print | pagebreak anchor codesample | ltr rtl', + readonly: this.props.readonly, + setup: this.setupEditor.bind(this), + init_instance_callback: this.initCallback.bind(this), + } + } + + setupEditor(editor) { + editor.on('change', e => { + const value = tinymce.activeEditor.getContent(); + if (value !== this.value) { + this.props.record.update({[this.props.name]: value}) + } + }); + } + + removeEditor() { + if (this.editor) { + this.editor.remove() + } + } + + async initEditor() { + this.removeEditor() + const config = this.tinymceConfig; + this.editor = (await tinymce.init(config))[0]; + } + + initCallback(editor) { + editor.setContent(this.value); + } + + updateEditor() { + if (!this.editor || this.props.record.dirty) { + return + } + this.editor.setContent(this.value); + } + +} + +export const fieldTinyMCE = { + component: FieldTinyMCE, + displayName: _t('TinyMCE'), + supportedTypes: ['html', 'text'], + extractProps: ({attrs, options}) => ({ + placeholder: attrs.placeholder, + dynamicPlaceholder: options?.dynamic_placeholder || false, + dynamicPlaceholderModelReferenceField: + options?.dynamic_placeholder_model_reference_field || '', + }), +} + +registry.category('fields').add('tinymce', fieldTinyMCE); diff --git a/web_editor_tinymce/static/src/field_tinymce/field_tinymce.scss b/web_editor_tinymce/static/src/field_tinymce/field_tinymce.scss new file mode 100644 index 000000000..ed817fb39 --- /dev/null +++ b/web_editor_tinymce/static/src/field_tinymce/field_tinymce.scss @@ -0,0 +1,3 @@ +.o_field_tinymce { + width: 100%; +} diff --git a/web_editor_tinymce/static/src/field_tinymce/field_tinymce.xml b/web_editor_tinymce/static/src/field_tinymce/field_tinymce.xml new file mode 100644 index 000000000..301ffd7e9 --- /dev/null +++ b/web_editor_tinymce/static/src/field_tinymce/field_tinymce.xml @@ -0,0 +1,21 @@ + +