diff --git a/.badges/code_style-black-000000.svg b/.badges/code_style-black-000000.svg new file mode 100644 index 0000000000000000000000000000000000000000..1d0c80b8baa395de2b880e3cccc3b03cfb3468b0_LmJhZGdlcy9jb2RlX3N0eWxlLWJsYWNrLTAwMDAwMC5zdmc= --- /dev/null +++ b/.badges/code_style-black-000000.svg @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<svg xmlns="http://www.w3.org/2000/svg" width="114" height="20"> + <linearGradient id="b" x2="0" y2="100%"> + <stop offset="0" stop-color="#bbb" stop-opacity=".1" /> + <stop offset="1" stop-opacity=".1" /> + </linearGradient> + <mask id="anybadge_1"> + <rect width="114" height="20" rx="3" fill="#fff" /> + </mask> + <g mask="url(#anybadge_1)"> + <path fill="#555" d="M0 0h72v20H0z" /> + <path fill="#000000" d="M72 0h42v20H72z" /> + <path fill="url(#b)" d="M0 0h114v20H0z" /> + </g> + <g + fill="#fff" + text-anchor="middle" + font-family="DejaVu Sans,Verdana,Geneva,sans-serif" + font-size="11" + > + <text x="37.0" y="15" fill="#010101" fill-opacity=".3">code style</text> + <text x="36.0" y="14">code style</text> + </g> + <g + fill="#fff" + text-anchor="middle" + font-family="DejaVu Sans,Verdana,Geneva,sans-serif" + font-size="11" + > + <text x="94.0" y="15" fill="#010101" fill-opacity=".3">black</text> + <text x="93.0" y="14">black</text> + </g> +</svg> diff --git a/.badges/code_style-prettier-ff69b4.svg b/.badges/code_style-prettier-ff69b4.svg new file mode 100644 index 0000000000000000000000000000000000000000..1d0c80b8baa395de2b880e3cccc3b03cfb3468b0_LmJhZGdlcy9jb2RlX3N0eWxlLXByZXR0aWVyLWZmNjliNC5zdmc= --- /dev/null +++ b/.badges/code_style-prettier-ff69b4.svg @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<svg xmlns="http://www.w3.org/2000/svg" width="129" height="20"> + <linearGradient id="b" x2="0" y2="100%"> + <stop offset="0" stop-color="#bbb" stop-opacity=".1" /> + <stop offset="1" stop-opacity=".1" /> + </linearGradient> + <mask id="anybadge_1"> + <rect width="129" height="20" rx="3" fill="#fff" /> + </mask> + <g mask="url(#anybadge_1)"> + <path fill="#555" d="M0 0h72v20H0z" /> + <path fill="#ff69b4" d="M72 0h57v20H72z" /> + <path fill="url(#b)" d="M0 0h129v20H0z" /> + </g> + <g + fill="#fff" + text-anchor="middle" + font-family="DejaVu Sans,Verdana,Geneva,sans-serif" + font-size="11" + > + <text x="37.0" y="15" fill="#010101" fill-opacity=".3">code style</text> + <text x="36.0" y="14">code style</text> + </g> + <g + fill="#fff" + text-anchor="middle" + font-family="DejaVu Sans,Verdana,Geneva,sans-serif" + font-size="11" + > + <text x="101.5" y="15" fill="#010101" fill-opacity=".3">prettier</text> + <text x="100.5" y="14">prettier</text> + </g> +</svg> diff --git a/.badges/licence-AGPL--3-blue.svg b/.badges/licence-AGPL--3-blue.svg new file mode 100644 index 0000000000000000000000000000000000000000..1d0c80b8baa395de2b880e3cccc3b03cfb3468b0_LmJhZGdlcy9saWNlbmNlLUFHUEwtLTMtYmx1ZS5zdmc= --- /dev/null +++ b/.badges/licence-AGPL--3-blue.svg @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<svg xmlns="http://www.w3.org/2000/svg" width="107" height="20"> + <linearGradient id="b" x2="0" y2="100%"> + <stop offset="0" stop-color="#bbb" stop-opacity=".1" /> + <stop offset="1" stop-opacity=".1" /> + </linearGradient> + <mask id="anybadge_1"> + <rect width="107" height="20" rx="3" fill="#fff" /> + </mask> + <g mask="url(#anybadge_1)"> + <path fill="#555" d="M0 0h53v20H0z" /> + <path fill="#0000FF" d="M53 0h54v20H53z" /> + <path fill="url(#b)" d="M0 0h107v20H0z" /> + </g> + <g + fill="#fff" + text-anchor="middle" + font-family="DejaVu Sans,Verdana,Geneva,sans-serif" + font-size="11" + > + <text x="27.5" y="15" fill="#010101" fill-opacity=".3">licence</text> + <text x="26.5" y="14">licence</text> + </g> + <g + fill="#fff" + text-anchor="middle" + font-family="DejaVu Sans,Verdana,Geneva,sans-serif" + font-size="11" + > + <text x="81.0" y="15" fill="#010101" fill-opacity=".3">AGPL-3</text> + <text x="80.0" y="14">AGPL-3</text> + </g> +</svg> diff --git a/.badges/maturity.svg b/.badges/maturity.svg new file mode 100644 index 0000000000000000000000000000000000000000..1d0c80b8baa395de2b880e3cccc3b03cfb3468b0_LmJhZGdlcy9tYXR1cml0eS5zdmc= --- /dev/null +++ b/.badges/maturity.svg @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<svg xmlns="http://www.w3.org/2000/svg" width="103" height="20"> + <linearGradient id="b" x2="0" y2="100%"> + <stop offset="0" stop-color="#bbb" stop-opacity=".1" /> + <stop offset="1" stop-opacity=".1" /> + </linearGradient> + <mask id="anybadge_1"> + <rect width="103" height="20" rx="3" fill="#fff" /> + </mask> + <g mask="url(#anybadge_1)"> + <path fill="#555" d="M0 0h61v20H0z" /> + <path fill="#e05d44" d="M61 0h42v20H61z" /> + <path fill="url(#b)" d="M0 0h103v20H0z" /> + </g> + <g + fill="#fff" + text-anchor="middle" + font-family="DejaVu Sans,Verdana,Geneva,sans-serif" + font-size="11" + > + <text x="31.5" y="15" fill="#010101" fill-opacity=".3">maturity</text> + <text x="30.5" y="14">maturity</text> + </g> + <g + fill="#fff" + text-anchor="middle" + font-family="DejaVu Sans,Verdana,Geneva,sans-serif" + font-size="11" + > + <text x="83.0" y="15" fill="#010101" fill-opacity=".3">Alpha</text> + <text x="82.0" y="14">Alpha</text> + </g> +</svg> diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000000000000000000000000000000000000..1d0c80b8baa395de2b880e3cccc3b03cfb3468b0_LmVkaXRvcmNvbmZpZw== --- /dev/null +++ b/.editorconfig @@ -0,0 +1,20 @@ +# Configuration for known file extensions +[*.{css,htm,html,js,json,jsx,less,markdown,md,py,rst,sass,scss,toml,xml,yaml,yml}] +charset = utf-8 +end_of_line = lf +indent_size = 4 +indent_style = space +insert_final_newline = true +trim_trailing_whitespace = true + +[*.{json,yml,yaml,rst,markdown,md,toml}] +indent_size = 2 + +# Do not configure editor for libs +[*/static/{lib,src/lib}/**] +charset = unset +end_of_line = unset +indent_size = unset +indent_style = unset +insert_final_newline = false +trim_trailing_whitespace = false diff --git a/.eslintrc.yml b/.eslintrc.yml new file mode 100644 index 0000000000000000000000000000000000000000..1d0c80b8baa395de2b880e3cccc3b03cfb3468b0_LmVzbGludHJjLnltbA== --- /dev/null +++ b/.eslintrc.yml @@ -0,0 +1,187 @@ +env: + browser: true + es6: true + +# See https://github.com/OCA/odoo-community.org/issues/37#issuecomment-470686449 +parserOptions: + ecmaVersion: 2019 + +overrides: + - files: + - "**/*.esm.js" + parserOptions: + sourceType: module + +# Globals available in Odoo that shouldn't produce errorings +globals: + _: readonly + $: readonly + fuzzy: readonly + jQuery: readonly + moment: readonly + odoo: readonly + openerp: readonly + owl: readonly + +# Styling is handled by Prettier, so we only need to enable AST rules; +# see https://github.com/OCA/maintainer-quality-tools/pull/618#issuecomment-558576890 +rules: + accessor-pairs: warn + array-callback-return: warn + callback-return: warn + capitalized-comments: + - warn + - always + - ignoreConsecutiveComments: true + ignoreInlineComments: true + complexity: + - warn + - 15 + constructor-super: warn + dot-notation: warn + eqeqeq: warn + global-require: warn + handle-callback-err: warn + id-blacklist: warn + id-match: warn + init-declarations: error + max-depth: warn + max-nested-callbacks: warn + max-statements-per-line: warn + no-alert: warn + no-array-constructor: warn + no-caller: warn + no-case-declarations: warn + no-class-assign: warn + no-cond-assign: error + no-const-assign: error + no-constant-condition: warn + no-control-regex: warn + no-debugger: error + no-delete-var: warn + no-div-regex: warn + no-dupe-args: error + no-dupe-class-members: error + no-dupe-keys: error + no-duplicate-case: error + no-duplicate-imports: error + no-else-return: warn + no-empty-character-class: warn + no-empty-function: error + no-empty-pattern: error + no-empty: warn + no-eq-null: error + no-eval: error + no-ex-assign: error + no-extend-native: warn + no-extra-bind: warn + no-extra-boolean-cast: warn + no-extra-label: warn + no-fallthrough: warn + no-func-assign: error + no-global-assign: error + no-implicit-coercion: + - warn + - allow: ["~"] + no-implicit-globals: warn + no-implied-eval: warn + no-inline-comments: warn + no-inner-declarations: warn + no-invalid-regexp: warn + no-irregular-whitespace: warn + no-iterator: warn + no-label-var: warn + no-labels: warn + no-lone-blocks: warn + no-lonely-if: error + no-mixed-requires: error + no-multi-str: warn + no-native-reassign: error + no-negated-condition: warn + no-negated-in-lhs: error + no-new-func: warn + no-new-object: warn + no-new-require: warn + no-new-symbol: warn + no-new-wrappers: warn + no-new: warn + no-obj-calls: warn + no-octal-escape: warn + no-octal: warn + no-param-reassign: warn + no-path-concat: warn + no-process-env: warn + no-process-exit: warn + no-proto: warn + no-prototype-builtins: warn + no-redeclare: warn + no-regex-spaces: warn + no-restricted-globals: warn + no-restricted-imports: warn + no-restricted-modules: warn + no-restricted-syntax: warn + no-return-assign: error + no-script-url: warn + no-self-assign: warn + no-self-compare: warn + no-sequences: warn + no-shadow-restricted-names: warn + no-shadow: warn + no-sparse-arrays: warn + no-sync: warn + no-this-before-super: warn + no-throw-literal: warn + no-undef-init: warn + no-undef: error + no-unmodified-loop-condition: warn + no-unneeded-ternary: error + no-unreachable: error + no-unsafe-finally: error + no-unused-expressions: error + no-unused-labels: error + no-unused-vars: error + no-use-before-define: error + no-useless-call: warn + no-useless-computed-key: warn + no-useless-concat: warn + no-useless-constructor: warn + no-useless-escape: warn + no-useless-rename: warn + no-void: warn + no-with: warn + operator-assignment: [error, always] + prefer-const: warn + radix: warn + require-yield: warn + sort-imports: warn + spaced-comment: [error, always] + strict: [error, function] + use-isnan: error + valid-jsdoc: + - warn + - prefer: + arg: param + argument: param + augments: extends + constructor: class + exception: throws + func: function + method: function + prop: property + return: returns + virtual: abstract + yield: yields + preferType: + array: Array + bool: Boolean + boolean: Boolean + number: Number + object: Object + str: String + string: String + requireParamDescription: false + requireReturn: false + requireReturnDescription: false + requireReturnType: false + valid-typeof: warn + yoda: warn diff --git a/.flake8 b/.flake8 new file mode 100644 index 0000000000000000000000000000000000000000..1d0c80b8baa395de2b880e3cccc3b03cfb3468b0_LmZsYWtlOA== --- /dev/null +++ b/.flake8 @@ -0,0 +1,5 @@ +[flake8] +max-line-length = 88 +per-file-ignores= + __init__.py:F401 + __manifest__.py:B018 diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000000000000000000000000000000000000..1d0c80b8baa395de2b880e3cccc3b03cfb3468b0_LmdpdGxhYi1jaS55bWw= --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,3 @@ +include: + - project: xcg/ci-templates + file: /odoo/16.0/gitlab-ci.yaml diff --git a/.hgignore b/.hgignore index e2623afff2ec70097927cb98e2dc22e2e44f48a4_LmhnaWdub3Jl..1d0c80b8baa395de2b880e3cccc3b03cfb3468b0_LmhnaWdub3Jl 100644 --- a/.hgignore +++ b/.hgignore @@ -1,24 +1,3 @@ syntax: glob -**/*.pyc -*.pyc -*.pyo -*.swp -.tmp* -*~ -*.egg-info -dist/* -build/* -lib/* -output/* -*.orig -*.log -.settings/* -storage/* -.project -.idea -.pydevproject -*.db -.ropeproject/* -.mob ./doc/_build ./doc/autotodo @@ -23,5 +2,2 @@ ./doc/_build ./doc/autotodo -./doc/manifest -pyproject.toml -.isort.cfg diff --git a/.prettierrc.yml b/.prettierrc.yml new file mode 100644 index 0000000000000000000000000000000000000000..1d0c80b8baa395de2b880e3cccc3b03cfb3468b0_LnByZXR0aWVycmMueW1s --- /dev/null +++ b/.prettierrc.yml @@ -0,0 +1,8 @@ +# Defaults for all prettier-supported languages. +# Prettier will complete this with settings from .editorconfig file. +bracketSpacing: false +printWidth: 88 +proseWrap: always +semi: true +trailingComma: "es5" +xmlWhitespaceSensitivity: "ignore" diff --git a/.yamllint.yaml b/.yamllint.yaml new file mode 100644 index 0000000000000000000000000000000000000000..1d0c80b8baa395de2b880e3cccc3b03cfb3468b0_LnlhbWxsaW50LnlhbWw= --- /dev/null +++ b/.yamllint.yaml @@ -0,0 +1,4 @@ +rules: + document-start: disable + indentation: + indent-sequences: true diff --git a/NEWS.rst b/NEWS.rst index e2623afff2ec70097927cb98e2dc22e2e44f48a4_TkVXUy5yc3Q=..1d0c80b8baa395de2b880e3cccc3b03cfb3468b0_TkVXUy5yc3Q= 100644 --- a/NEWS.rst +++ b/NEWS.rst @@ -1,6 +1,10 @@ -======= -History -======= +Changelog +========= + +16.0.1.0.0 +---------- + +* Migrate to Odoo 16.0 13.0.1.0.0 ---------- diff --git a/README.rst b/README.rst index e2623afff2ec70097927cb98e2dc22e2e44f48a4_UkVBRE1FLnJzdA==..1d0c80b8baa395de2b880e3cccc3b03cfb3468b0_UkVBRE1FLnJzdA== 100644 --- a/README.rst +++ b/README.rst @@ -1,3 +1,4 @@ +============ Board Alerts ============ @@ -1,6 +2,21 @@ Board Alerts ============ +.. |maturity| image:: .badges/maturity.svg + :target: https://odoo-community.org/page/development-status + :alt: Alpha +.. |license| image:: .badges/licence-AGPL--3-blue.svg + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 +.. |black| image:: .badges/code_style-black-000000.svg + :target: https://github.com/psf/black + :alt: Black +.. |prettier| image:: .badges/code_style-prettier-ff69b4.svg + :target: https://github.com/prettier/prettier + :alt: Prettier + +|maturity| |license| |black| |prettier| + Send emails at regular intervals to summarize the contents of a dashboard. @@ -13,6 +29,9 @@ * The administrator can adjust the email frequency. +Usage +===== + Example use ----------- @@ -34,3 +53,14 @@ - The administrator can adjust the email frequency: .. image:: doc_images/board_alerts_example_4.png + + +Credits +======= + +Authors +------- + +- XCG Consulting, part of `Orbeet <https://orbeet.io>`_ + + - `Arthur Mayer <arthur.mayer@xcg-consulting.fr>`_ \ No newline at end of file diff --git a/__init__.py b/__init__.py index e2623afff2ec70097927cb98e2dc22e2e44f48a4_X19pbml0X18ucHk=..1d0c80b8baa395de2b880e3cccc3b03cfb3468b0_X19pbml0X18ucHk= 100644 --- a/__init__.py +++ b/__init__.py @@ -1,3 +1,1 @@ -# flake8: noqa - from . import controllers, models, wizard diff --git a/__manifest__.py b/__manifest__.py index e2623afff2ec70097927cb98e2dc22e2e44f48a4_X19tYW5pZmVzdF9fLnB5..1d0c80b8baa395de2b880e3cccc3b03cfb3468b0_X19tYW5pZmVzdF9fLnB5 100644 --- a/__manifest__.py +++ b/__manifest__.py @@ -1,3 +1,2 @@ -# -*- coding: utf-8 -*- ############################################################################## # @@ -2,7 +1,7 @@ ############################################################################## # -# Board Alerts, for Odoo -# Copyright (C) 2013 XCG Consulting (http://odoo.consulting) +# Board alerts, for Odoo +# Copyright © 2021, 2022, 2023 XCG Consulting <https://xcg-consulting.fr> # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as @@ -20,21 +19,11 @@ ############################################################################## { "name": "Board alerts", - "description": """ -Board alerts -============ - -Send emails at regular intervals to summarize the contents of a dashboard. - - -Create your own Odoo notifications ----------------------------------- - -* Create your own notifications based on the contents of your activity. -* See your alerts in your dasboard. -* Receive them automatically by email at regular intervals. -* The administrator can adjust the email frequency. -""", - "version": "13.0.1.0", + "summary": """ + Send emails at regular intervals" + to summarize the contents of a dashboard. + """, + "license": "AGPL-3", + "version": "16.0.1.0.0", "category": "Tools", "author": "XCG Consulting", @@ -39,5 +28,5 @@ "category": "Tools", "author": "XCG Consulting", - "website": "http://odoo.consulting/", - "depends": ["board", "mail", "web"], + "website": "https://orbeet.io/", + "depends": ["web", "board", "mail", "spreadsheet_dashboard"], "data": [ @@ -43,4 +32,5 @@ "data": [ + "security/ir.model.access.csv", "data/alert_board.xml", "data/board_alerts_email_template.xml", "data/board_alerts_cron_task.xml", @@ -44,6 +34,5 @@ "data/alert_board.xml", "data/board_alerts_email_template.xml", "data/board_alerts_cron_task.xml", - "views/board_alerts_assets.xml", "wizard/board_alerts_dlg.xml", ], @@ -48,5 +37,11 @@ "wizard/board_alerts_dlg.xml", ], - "qweb": ["static/src/xml/alert_board.xml"], + "assets": { + "web.assets_backend": [ + "/board_alerts/static/src/xml/alert_board.xml", + # "/board_alerts/static/src/scss/alert_board.scss", + "/board_alerts/static/src/js/alert_board.js", + ], + }, "installable": True, } diff --git a/controllers/add_to_alert_dashboard.py b/controllers/add_to_alert_dashboard.py index e2623afff2ec70097927cb98e2dc22e2e44f48a4_Y29udHJvbGxlcnMvYWRkX3RvX2FsZXJ0X2Rhc2hib2FyZC5weQ==..1d0c80b8baa395de2b880e3cccc3b03cfb3468b0_Y29udHJvbGxlcnMvYWRkX3RvX2FsZXJ0X2Rhc2hib2FyZC5weQ== 100644 --- a/controllers/add_to_alert_dashboard.py +++ b/controllers/add_to_alert_dashboard.py @@ -13,11 +13,8 @@ """ @route(route="/board/add_to_alert_dashboard", type="json", auth="user") - def add_to_dashboard( - self, action_id, context_to_save, domain, view_mode, name="" - ): - """Called when adding to the alert dashboard from the Odoo web client. - """ + def add_to_dashboard(self, action_id, context_to_save, domain, view_mode, name=""): + """Called when adding to the alert dashboard from the Odoo web client.""" action = request.env.ref("board_alerts.action_alert_board") if ( @@ -28,7 +25,7 @@ ): # Maybe should check the content instead of model board.board ? view_id = action["views"][0][0] - board = request.env["board.board"].fields_view_get(view_id, "form") + board = request.env["board.board"].get_view(view_id, "form") if board and "arch" in board: xml = ElementTree.fromstring(board["arch"]) column = xml.find("./board/column") diff --git a/data/alert_board.xml b/data/alert_board.xml index e2623afff2ec70097927cb98e2dc22e2e44f48a4_ZGF0YS9hbGVydF9ib2FyZC54bWw=..1d0c80b8baa395de2b880e3cccc3b03cfb3468b0_ZGF0YS9hbGVydF9ib2FyZC54bWw= 100644 --- a/data/alert_board.xml +++ b/data/alert_board.xml @@ -1,3 +1,3 @@ -<?xml version="1.0" encoding="utf-8"?> +<?xml version="1.0" encoding="utf-8" ?> <odoo> <data noupdate="1"> @@ -2,6 +2,5 @@ <odoo> <data noupdate="1"> - <!-- Define the board the user can drag views into that will be used to send emails. Imitated from the definition of the "My Dashboard" board in odoo/addons/board/views/board_views.xml. --> @@ -25,8 +24,11 @@ <field name="view_id" ref="alert_board" /> </record> - <menuitem id="menu_alert_board" parent="base.menu_board_root" - action="action_alert_board" sequence="6" /> - + <menuitem + id="menu_alert_board" + parent="spreadsheet_dashboard.spreadsheet_dashboard_menu_root" + action="action_alert_board" + sequence="126" + /> </data> </odoo> diff --git a/data/board_alerts_cron_task.xml b/data/board_alerts_cron_task.xml index e2623afff2ec70097927cb98e2dc22e2e44f48a4_ZGF0YS9ib2FyZF9hbGVydHNfY3Jvbl90YXNrLnhtbA==..1d0c80b8baa395de2b880e3cccc3b03cfb3468b0_ZGF0YS9ib2FyZF9hbGVydHNfY3Jvbl90YXNrLnhtbA== 100644 --- a/data/board_alerts_cron_task.xml +++ b/data/board_alerts_cron_task.xml @@ -1,3 +1,3 @@ -<?xml version="1.0" encoding="utf-8"?> +<?xml version="1.0" encoding="utf-8" ?> <odoo> <data noupdate="1"> @@ -2,6 +2,5 @@ <odoo> <data noupdate="1"> - <!-- Cron task to send board alert emails every morning at 07H00. --> <record id="board_alerts_cron_task" model="ir.cron"> @@ -10,5 +9,5 @@ <field name="code">model.send_board_alerts()</field> <field name="interval_number">1</field> <field name="interval_type">days</field> - <field name="model_id" ref="model_res_users"/> + <field name="model_id" ref="model_res_users" /> <field name="name">Board alerts - Daily emails</field> @@ -14,5 +13,4 @@ <field name="name">Board alerts - Daily emails</field> - <field name="nextcall" - eval="time.strftime('%Y-%m-%d 07:00:00', time.localtime(time.time() + 24 * 3600))" /> + <field name="nextcall" eval="time.strftime('%Y-%m-%d 07:00:00')" /> <field name="numbercall">-1</field> </record> @@ -17,5 +15,4 @@ <field name="numbercall">-1</field> </record> - </data> </odoo> diff --git a/data/board_alerts_email_template.xml b/data/board_alerts_email_template.xml index e2623afff2ec70097927cb98e2dc22e2e44f48a4_ZGF0YS9ib2FyZF9hbGVydHNfZW1haWxfdGVtcGxhdGUueG1s..1d0c80b8baa395de2b880e3cccc3b03cfb3468b0_ZGF0YS9ib2FyZF9hbGVydHNfZW1haWxfdGVtcGxhdGUueG1s 100644 --- a/data/board_alerts_email_template.xml +++ b/data/board_alerts_email_template.xml @@ -1,3 +1,3 @@ -<?xml version="1.0" encoding="utf-8"?> +<?xml version="1.0" encoding="utf-8" ?> <odoo> <data noupdate="1"> @@ -2,6 +2,5 @@ <odoo> <data noupdate="1"> - <!-- Default email template for board alert emails. --> <record id="board_alerts_mail_template" model="mail.template"> @@ -5,8 +4,10 @@ <!-- Default email template for board alert emails. --> <record id="board_alerts_mail_template" model="mail.template"> - <field name="body_html"><![CDATA[ + <field + name="body_html" + ><![CDATA[ <p>Hello ${ object.name },</p> <p>Here follow your alerts for today.</p> <div>${ object.get_board_alert_contents() | safe }</div> ]]></field> @@ -9,8 +10,10 @@ <p>Hello ${ object.name },</p> <p>Here follow your alerts for today.</p> <div>${ object.get_board_alert_contents() | safe }</div> ]]></field> - <field name="email_from">${ object.company_id.name or '' } <${ object.company_id.email or '' }></field> + <field + name="email_from" + >${ object.company_id.name or '' } <${ object.company_id.email or '' }></field> <field name="partner_to">${ object.partner_id.id }</field> <field name="email_to">${ object.email }</field> <field name="lang">${object.partner_id.lang}</field> @@ -18,6 +21,5 @@ <field name="name">Board alert email</field> <field name="subject">Today's alerts</field> </record> - </data> </odoo> diff --git a/doc/.badges b/doc/.badges new file mode 120000 index 0000000000000000000000000000000000000000..1d0c80b8baa395de2b880e3cccc3b03cfb3468b0_ZG9jLy5iYWRnZXM= --- /dev/null +++ b/doc/.badges @@ -0,0 +1,1 @@ +../.badges \ No newline at end of file diff --git a/doc/Makefile b/doc/Makefile index e2623afff2ec70097927cb98e2dc22e2e44f48a4_ZG9jL01ha2VmaWxl..1d0c80b8baa395de2b880e3cccc3b03cfb3468b0_ZG9jL01ha2VmaWxl 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -1,3 +1,3 @@ -# Makefile for Sphinx documentation +# Minimal makefile for Sphinx documentation # @@ -2,10 +2,13 @@ # -# You can set these variables from the command line. -SPHINXOPTS = -SPHINXBUILD = sphinx-build -PAPER = -BUILDDIR = _build +# You can set these variables from the command line, and also +# from the environment for the first two. +SPHINXOPTS ?= +SPHINXBUILD ?= sphinx-build +SOURCEDIR = . +BUILDDIR = _build +LANGUAGE ?= en +BUILDDIRSUFFIX = # User-friendly check for sphinx-build ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) @@ -9,6 +12,6 @@ # User-friendly check for sphinx-build ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) - $(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) +$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) endif @@ -13,5 +16,4 @@ endif -project:=$(shell basename $(shell readlink -f ..)) -branch:=$(shell hg identify --branch) +.PHONY: help Makefile gettext clean @@ -17,10 +19,3 @@ -# Internal variables. -PAPEROPT_a4 = -D latex_paper_size=a4 -PAPEROPT_letter = -D latex_paper_size=letter -ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . -# the i18n builder cannot share the environment and doctrees with the others -I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . - -.PHONY: help +# Put it first so that "make" without argument is like "make help". help: @@ -26,27 +21,3 @@ help: - @echo "Please use \`make <target>' where <target> is one of" - @echo " html to make standalone HTML files" - @echo " dirhtml to make HTML files named index.html in directories" - @echo " singlehtml to make a single large HTML file" - @echo " pickle to make pickle files" - @echo " json to make JSON files" - @echo " htmlhelp to make HTML files and a HTML help project" - @echo " qthelp to make HTML files and a qthelp project" - @echo " applehelp to make an Apple Help Book" - @echo " devhelp to make HTML files and a Devhelp project" - @echo " epub to make an epub" - @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" - @echo " latexpdf to make LaTeX files and run them through pdflatex" - @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" - @echo " text to make text files" - @echo " man to make manual pages" - @echo " texinfo to make Texinfo files" - @echo " info to make Texinfo files and run them through makeinfo" - @echo " gettext to make PO message catalogs" - @echo " changes to make an overview of all changed/added/deprecated items" - @echo " xml to make Docutils-native XML files" - @echo " pseudoxml to make pseudoxml-XML files for display purposes" - @echo " linkcheck to check all external links for integrity" - @echo " doctest to run all doctests embedded in the documentation (if enabled)" - @echo " coverage to run coverage check of the documentation (if enabled)" + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)/$(LANGUAGE)$(BUILDDIRSUFFIX)" $(SPHINXOPTS) $(O) @@ -52,4 +23,3 @@ -.PHONY: clean clean: rm -rf $(BUILDDIR)/* @@ -54,69 +24,4 @@ clean: rm -rf $(BUILDDIR)/* - rm -f autotodo manifest - -.PHONY: html -html: autotodo manifest - $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html - @echo - @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." - -.PHONY: dirhtml -dirhtml: autotodo manifest - $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml - @echo - @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." - -.PHONY: singlehtml -singlehtml: autotodo manifest - $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml - @echo - @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." - -.PHONY: pickle -pickle: - $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle - @echo - @echo "Build finished; now you can process the pickle files." - -.PHONY: json -json: - $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json - @echo - @echo "Build finished; now you can process the JSON files." - -.PHONY: htmlhelp -htmlhelp: autotodo manifest - $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp - @echo - @echo "Build finished; now you can run HTML Help Workshop with the" \ - ".hhp project file in $(BUILDDIR)/htmlhelp." - -.PHONY: epub -epub: - $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub - @echo - @echo "Build finished. The epub file is in $(BUILDDIR)/epub." - -.PHONY: latex -latex: - $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex - @echo - @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." - @echo "Run \`make' in that directory to run these through (pdf)latex" \ - "(use \`make latexpdf' here to do that automatically)." - -.PHONY: latexpdf -latexpdf: - $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex - @echo "Running LaTeX files through pdflatex..." - $(MAKE) -C $(BUILDDIR)/latex all-pdf - @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." - -.PHONY: latexpdfja -latexpdfja: - $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex - @echo "Running LaTeX files through platex and dvipdfmx..." - $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja - @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + rm -f autotodo @@ -122,74 +27,5 @@ -.PHONY: text -text: - $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text - @echo - @echo "Build finished. The text files are in $(BUILDDIR)/text." - -.PHONY: man -man: - $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man - @echo - @echo "Build finished. The manual pages are in $(BUILDDIR)/man." - -.PHONY: texinfo -texinfo: - $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo - @echo - @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." - @echo "Run \`make' in that directory to run these through makeinfo" \ - "(use \`make info' here to do that automatically)." - -.PHONY: info -info: - $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo - @echo "Running Texinfo files through makeinfo..." - make -C $(BUILDDIR)/texinfo info - @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." - -.PHONY: gettext -gettext: - $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale - @echo - @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." - -.PHONY: changes -changes: - $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes - @echo - @echo "The overview file is in $(BUILDDIR)/changes." - -.PHONY: linkcheck -linkcheck: - $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck - @echo - @echo "Link check complete; look for any errors in the above output " \ - "or in $(BUILDDIR)/linkcheck/output.txt." - -.PHONY: doctest -doctest: - $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest - @echo "Testing of doctests in the sources finished, look at the " \ - "results in $(BUILDDIR)/doctest/output.txt." - -.PHONY: coverage -coverage: - $(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage - @echo "Testing of coverage in the sources finished, look at the " \ - "results in $(BUILDDIR)/coverage/python.txt." - -.PHONY: xml -xml: - $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml - @echo - @echo "Build finished. The XML files are in $(BUILDDIR)/xml." - -.PHONY: pseudoxml -pseudoxml: - $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml - @echo - @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." - -autotodo: autotodo.py +# depends on autotodo.py but that does not work well with the catch-all below +autotodo: @./autotodo.py ../ .py TODO,FIXME,XXX @@ -194,4 +30,13 @@ @./autotodo.py ../ .py TODO,FIXME,XXX -manifest: ../__manifest__.py manifest.py - @./manifest.py +gettext: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)$(BUILDDIRSUFFIX)" $(SPHINXOPTS) $(O) + +# Use to update *.po files +update_locale: Makefile gettext + @sphinx-intl update -p "$(BUILDDIR)$(BUILDDIRSUFFIX)/gettext" -l $(LANGUAGE) + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile autotodo + @$(SPHINXBUILD) -b $@ "$(SOURCEDIR)" "$(BUILDDIR)/$@/$(LANGUAGE)$(BUILDDIRSUFFIX)" -d "$(BUILDDIR)$(BUILDDIRSUFFIX)/doctrees/$(LANGUAGE)" $(SPHINXOPTS) -D language=$(LANGUAGE) $(O) diff --git a/doc/autotodo.py b/doc/autotodo.py index e2623afff2ec70097927cb98e2dc22e2e44f48a4_ZG9jL2F1dG90b2RvLnB5..1d0c80b8baa395de2b880e3cccc3b03cfb3468b0_ZG9jL2F1dG90b2RvLnB5 100755 --- a/doc/autotodo.py +++ b/doc/autotodo.py @@ -2,7 +2,7 @@ ############################################################################## # # OpenERP, Open Source Management Solution -# Copyright (C) 2014, 2018 XCG Consulting +# Copyright © 2014, 2018, 2022, 2023 XCG Consulting # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as @@ -22,6 +22,7 @@ import os import os.path import sys +from collections.abc import Mapping def main(): @@ -33,4 +34,5 @@ exts = sys.argv[2].split(",") tags = sys.argv[3].split(",") todolist = {tag: [] for tag in tags} + path_file_length: Mapping[str, int] = {} @@ -36,6 +38,6 @@ - for root, dirs, files in os.walk(folder): - scan_folder((exts, tags, todolist), root, files) - create_autotodo(folder, todolist) + for root, _dirs, files in os.walk(folder): + scan_folder((exts, tags, todolist, path_file_length), root, files) + create_autotodo(folder, todolist, path_file_length) @@ -40,6 +42,6 @@ -def write_info(f, infos, folder): +def write_info(f, infos, folder, path_file_length: Mapping[str, int]): # Check sphinx version for lineno-start support import sphinx @@ -52,10 +54,8 @@ for i in infos: path = i[0] line = i[1] - lines = (line - 3, line + 4) - class_name = ":class:`%s`" % os.path.basename( - os.path.splitext(path)[0] - ) + lines = (line - 3, min(line + 4, path_file_length[path])) + class_name = ":class:`%s`" % os.path.basename(os.path.splitext(path)[0]) f.write( "%s\n" "%s\n\n" @@ -71,7 +71,7 @@ path, lines[0], lines[1], - line, + 4, ) ) if lineno_start: @@ -79,5 +79,5 @@ f.write("\n") -def create_autotodo(folder, todolist): +def create_autotodo(folder, todolist, path_file_length: Mapping[str, int]): with open("autotodo", "w+") as f: @@ -83,3 +83,3 @@ with open("autotodo", "w+") as f: - for tag, info in todolist.items(): + for tag, info in list(todolist.items()): f.write("%s\n%s\n\n" % (tag, "=" * len(tag))) @@ -85,5 +85,5 @@ f.write("%s\n%s\n\n" % (tag, "=" * len(tag))) - write_info(f, info, folder) + write_info(f, info, folder, path_file_length) def scan_folder(data_tuple, dirname, names): @@ -87,8 +87,7 @@ def scan_folder(data_tuple, dirname, names): - (exts, tags, res) = data_tuple - file_info = {} + (exts, tags, res, path_file_length) = data_tuple for name in names: (root, ext) = os.path.splitext(name) if ext in exts: @@ -92,9 +91,11 @@ for name in names: (root, ext) = os.path.splitext(name) if ext in exts: - file_info = scan_file(os.path.join(dirname, name), tags) - for tag, info in file_info.items(): + path = os.path.join(dirname, name) + file_info, length = scan_file(path, tags) + path_file_length[path] = length + for tag, info in list(file_info.items()): if info: res[tag].extend(info) @@ -97,11 +98,12 @@ if info: res[tag].extend(info) -def scan_file(filename, tags): - res = {tag: [] for tag in tags} +def scan_file(filename, tags) -> tuple[dict[str, list[tuple[str, int, str]]], int]: + res: dict[str, list[tuple[str, int, str]]] = {tag: [] for tag in tags} + line_num: int = 0 with open(filename, "r") as f: for line_num, line in enumerate(f): for tag in tags: if tag in line: res[tag].append((filename, line_num, line[:-1].strip())) @@ -103,9 +105,9 @@ with open(filename, "r") as f: for line_num, line in enumerate(f): for tag in tags: if tag in line: res[tag].append((filename, line_num, line[:-1].strip())) - return res + return res, line_num if __name__ == "__main__": diff --git a/doc/board_alerts.models.rst b/doc/board_alerts.models.rst deleted file mode 100644 index e2623afff2ec70097927cb98e2dc22e2e44f48a4_ZG9jL2JvYXJkX2FsZXJ0cy5tb2RlbHMucnN0..0000000000000000000000000000000000000000 --- a/doc/board_alerts.models.rst +++ /dev/null @@ -1,22 +0,0 @@ -odoo.addons.board_alerts.models package -======================================= - -Submodules ----------- - -odoo.addons.board_alerts.models.board_alerts module ---------------------------------------------------- - -.. automodule:: odoo.addons.board_alerts.models.board_alerts - :members: - :undoc-members: - :show-inheritance: - - -Module contents ---------------- - -.. automodule:: odoo.addons.board_alerts.models - :members: - :undoc-members: - :show-inheritance: diff --git a/doc/board_alerts.rst b/doc/board_alerts.rst deleted file mode 100644 index e2623afff2ec70097927cb98e2dc22e2e44f48a4_ZG9jL2JvYXJkX2FsZXJ0cy5yc3Q=..0000000000000000000000000000000000000000 --- a/doc/board_alerts.rst +++ /dev/null @@ -1,18 +0,0 @@ -board_alerts package -==================== - -Subpackages ------------ - -.. toctree:: - - board_alerts.models - board_alerts.wizard - -Module contents ---------------- - -.. automodule:: odoo.addons.board_alerts - :members: - :undoc-members: - :show-inheritance: diff --git a/doc/board_alerts.wizard.rst b/doc/board_alerts.wizard.rst deleted file mode 100644 index e2623afff2ec70097927cb98e2dc22e2e44f48a4_ZG9jL2JvYXJkX2FsZXJ0cy53aXphcmQucnN0..0000000000000000000000000000000000000000 --- a/doc/board_alerts.wizard.rst +++ /dev/null @@ -1,22 +0,0 @@ -odoo.addons.board_alerts.wizard package -======================================= - -Submodules ----------- - -odoo.addons.board_alerts.wizard.board_alerts_dlg module -------------------------------------------------------- - -.. automodule:: odoo.addons.board_alerts.wizard.board_alerts_dlg - :members: - :undoc-members: - :show-inheritance: - - -Module contents ---------------- - -.. automodule:: odoo.addons.board_alerts.wizard - :members: - :undoc-members: - :show-inheritance: diff --git a/doc/conf.py b/doc/conf.py index e2623afff2ec70097927cb98e2dc22e2e44f48a4_ZG9jL2NvbmYucHk=..1d0c80b8baa395de2b880e3cccc3b03cfb3468b0_ZG9jL2NvbmYucHk= 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -11,6 +11,10 @@ import configparser import os import sys +from importlib.metadata import PackageNotFoundError +from importlib.metadata import version as import_version + +from odoo_scripts.config import Configuration import odoo @@ -30,7 +34,9 @@ "sphinx.ext.intersphinx", "sphinx.ext.todo", "sphinx.ext.coverage", - "sphinxodoo.ext.autodoc", + "sphinx.ext.graphviz", + "sphinx.ext.viewcode", + "sphinxodooautodoc.ext.autodoc", ] # Add any paths that contain templates here, relative to this directory. @@ -46,7 +52,16 @@ # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. -# + +# The full version, including alpha/beta/rc tags. +try: + release = import_version("odoo-addon-board_alerts") + # The short X.Y version. + version = ".".join(release.split(".")[:2]) +except PackageNotFoundError: + # No version number if not installed + pass + with open(os.path.join("..", "__manifest__.py"), "r") as f: read_data = f.read() d = ast.literal_eval(read_data) @@ -50,10 +65,6 @@ with open(os.path.join("..", "__manifest__.py"), "r") as f: read_data = f.read() d = ast.literal_eval(read_data) -# The full version, including alpha/beta/rc tags. -release = d["version"] -# The short X.Y version. -version = ".".join(release.split(".")[:4]) # General information about the project. project = d["name"] @@ -57,7 +68,7 @@ # General information about the project. project = d["name"] -copyright = "2016, XCG Consulting" +copyright = "2021, 2022, 2023 XCG Consulting" author = d["author"] module_nospace = project.replace(" ", "") module_description = d.get("summary", "") @@ -70,6 +81,8 @@ # Usually you set "language" from the command line for these cases. language = None +locale_dirs = ["locale"] + # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = ["_build"] @@ -90,7 +103,7 @@ # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ["doc_images"] +html_static_path = ["_static"] # Output file base name for HTML help builder. htmlhelp_basename = "%sdoc" % module_nospace @@ -116,9 +129,7 @@ # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). -man_pages = [ - (master_doc, module_lowercase, "%s Documentation" % project, [author], 1) -] +man_pages = [(master_doc, module_lowercase, "%s Documentation" % project, [author], 1)] # -- Options for Texinfo output ------------------------------------------- @@ -141,7 +152,5 @@ intersphinx_mapping = {"https://docs.python.org/3/": None} -# -# odoo-sphinx-autodoc -# +# -- Options for sphinx-odoo-autodoc extension ---------------------------- @@ -147,6 +156,6 @@ -# sphinxodoo_addons: List of addons name to load (if empty, no addon will be +# sphinxodooautodoc_addons : List of addons name to load (if empty, no addon will be # loaded) this_module = os.path.basename( os.path.dirname(os.path.dirname(os.path.abspath(__file__))) ) @@ -149,9 +158,9 @@ # loaded) this_module = os.path.basename( os.path.dirname(os.path.dirname(os.path.abspath(__file__))) ) -sphinxodoo_addons = [this_module] -# sphinxodoo_root_path : Path of the Odoo root directory -sphinxodoo_root_path = os.path.dirname( +sphinxodooautodoc_addons = [this_module] +# sphinxodooautodoc_root_path : Path of the Odoo root directory +sphinxodooautodoc_root_path = os.path.dirname( os.path.dirname(os.path.abspath(odoo.__file__)) ) @@ -156,6 +165,6 @@ os.path.dirname(os.path.abspath(odoo.__file__)) ) -# sphinxodoo_addons_path : List of paths were Odoo addons to load are located +# sphinxodooautodoc_addons_path : List of paths were Odoo addons to load are located c = None # find setup file of superproject, if any directory = os.path.dirname(os.getenv("PWD")) @@ -164,7 +173,10 @@ if os.path.isfile(setup_path): c = configparser.ConfigParser() c.read(setup_path) - if not c.has_section("odoo_scripts"): + if c.has_section("odoo_scripts"): + # reload with odoo_scripts + c = Configuration(directory) + else: c = None if not c: if os.path.dirname(directory) != directory: @@ -172,6 +184,6 @@ else: directory = None -sphinxodoo_addons_path = list() +sphinxodooautodoc_addons_path = [] if c: @@ -176,8 +188,5 @@ if c: - addon_dirs = set( - os.path.dirname(path) - for path in c.get("odoo_scripts", "modules").split() - ) + addon_dirs = set(os.path.dirname(path) for path in c.modules) for line in addon_dirs: @@ -182,3 +191,12 @@ for line in addon_dirs: - sphinxodoo_addons_path.append(os.path.join(directory, line)) + sphinxodooautodoc_addons_path.append(os.path.join(directory, line)) +else: + # add this directory top dir + sphinxodooautodoc_addons_path.append( + os.path.dirname(os.path.dirname(os.getenv("PWD"))) + ) + other_addons = os.getenv("ODOO_ADDONS_PATH", default=None) + if other_addons: + for addon_path in other_addons.split(","): + sphinxodooautodoc_addons_path.append(addon_path) diff --git a/doc/index.rst b/doc/index.rst index e2623afff2ec70097927cb98e2dc22e2e44f48a4_ZG9jL2luZGV4LnJzdA==..1d0c80b8baa395de2b880e3cccc3b03cfb3468b0_ZG9jL2luZGV4LnJzdA== 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -1,5 +1,15 @@ -.. include:: manifest +.. TEMPLATE +.. |coverage| image:: .badges/coverage.svg + :target: https://orus.io/xcg/template/odoo_modules/-/pipelines?ref=branch/16.0 + :alt: Coverage report +.. |pylint| image:: .badges/pylint.svg + :target: https://orus.io/xcg/template/odoo_modules/-/pipelines?ref=branch/16.0 + :alt: pylint score + +|coverage| |pylint| + +.. include:: README.rst Contents: .. toctree:: @@ -2,6 +12,6 @@ Contents: .. toctree:: - :maxdepth: 2 + :maxdepth: 4 @@ -7,5 +17,4 @@ - README modules NEWS TODO @@ -16,4 +25,3 @@ * :ref:`genindex` * :ref:`modindex` * :ref:`search` - diff --git a/doc/manifest.py b/doc/manifest.py deleted file mode 100755 index e2623afff2ec70097927cb98e2dc22e2e44f48a4_ZG9jL21hbmlmZXN0LnB5..0000000000000000000000000000000000000000 --- a/doc/manifest.py +++ /dev/null @@ -1,43 +0,0 @@ -#!/usr/bin/env python3 -############################################################################### -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2016, 2018 XCG Consulting (http://www.xcg-consulting.fr/) -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see <http://www.gnu.org/licenses/>. -# -############################################################################### -import os - -from odoo.modules import load_information_from_description_file - - -def main(): - module = os.path.basename( - os.path.dirname(os.path.dirname(os.path.realpath(__file__))) - ) - d = load_information_from_description_file(module) - with open("manifest", "w") as out: - manifest_content = ( - d["description"] - if "description" in d - else d["summary"] - if "summary" in d - else "" - ) - out.write(manifest_content) - - -if __name__ == "__main__": - main() diff --git a/doc/modules.rst b/doc/modules.rst index e2623afff2ec70097927cb98e2dc22e2e44f48a4_ZG9jL21vZHVsZXMucnN0..1d0c80b8baa395de2b880e3cccc3b03cfb3468b0_ZG9jL21vZHVsZXMucnN0 100644 --- a/doc/modules.rst +++ b/doc/modules.rst @@ -1,6 +1,15 @@ -board_alerts -============ +.. TEMPLATE This file is generated by running `sphinx-apidoc -o doc . -f` + Then add the full path to the module package and functions by using something like (need zsh to work due to the substitution) + module_name=$(basename $PWD) + find doc \( -name "${module_name}*.rst" -or -name modules.rst \) -exec sed -i -e "s,${module_name:gs/_/\\\\\\?_/},odoo.addons.\0,g" {} + + find doc -name "${module_name}*.rst" -printf "%f\n" | xargs -I{} mv doc/{} doc/odoo.addons.{} + Titles need to be manually updated after the previous command. + Then add the new files to mercurial with something like: + hg add doc/modules.rst doc/odoo.addons.${module_name}*.rst + +odoo_module +=========== .. toctree:: :maxdepth: 4 @@ -3,5 +12,5 @@ .. toctree:: :maxdepth: 4 - board_alerts + odoo_module diff --git a/i18n/fr.po b/i18n/fr.po index e2623afff2ec70097927cb98e2dc22e2e44f48a4_aTE4bi9mci5wbw==..1d0c80b8baa395de2b880e3cccc3b03cfb3468b0_aTE4bi9mci5wbw== 100644 --- a/i18n/fr.po +++ b/i18n/fr.po @@ -6,8 +6,8 @@ msgstr "" "Project-Id-Version: Odoo Server 13.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-06-12 07:40+0000\n" -"PO-Revision-Date: 2020-06-12 09:42+0200\n" +"POT-Creation-Date: 2024-01-04 09:09+0000\n" +"PO-Revision-Date: 2024-01-04 09:16+0000\n" "Last-Translator: Houzéfa Abbasbhay <houzefa.abba@xcg-consulting.fr>\n" "Language-Team: XCG Consulting\n" "Language: fr\n" @@ -15,7 +15,7 @@ "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" -"X-Generator: Poedit 2.0.6\n" +"X-Generator: Poedit 3.4.2\n" #. module: board_alerts #: model:mail.template,body_html:board_alerts.board_alerts_mail_template @@ -34,5 +34,5 @@ " " #. module: board_alerts -#. openerp-web +#. odoo-javascript #: code:addons/board_alerts/static/src/js/alert_board.js:0 @@ -38,6 +38,7 @@ #: code:addons/board_alerts/static/src/js/alert_board.js:0 -#, python-format -msgid "'%s' added to alert dashboard" +#, fuzzy, python-format +#| msgid "'%s' added to alert dashboard" +msgid "\"%s\" added to alert dashboard" msgstr "'%s' ajouté au tableau de bord des alertes" #. module: board_alerts @@ -41,10 +42,10 @@ msgstr "'%s' ajouté au tableau de bord des alertes" #. module: board_alerts -#. openerp-web +#. odoo-javascript #: code:addons/board_alerts/static/src/xml/alert_board.xml:0 #, python-format msgid "Add" msgstr "Ajouter" #. module: board_alerts @@ -45,13 +46,13 @@ #: code:addons/board_alerts/static/src/xml/alert_board.xml:0 #, python-format msgid "Add" msgstr "Ajouter" #. module: board_alerts -#. openerp-web +#. odoo-javascript #: code:addons/board_alerts/static/src/xml/alert_board.xml:0 #, python-format msgid "Add to my alert dashboard" msgstr "Ajouter à mon tableau d'alertes" #. module: board_alerts @@ -52,9 +53,14 @@ #: code:addons/board_alerts/static/src/xml/alert_board.xml:0 #, python-format msgid "Add to my alert dashboard" msgstr "Ajouter à mon tableau d'alertes" #. module: board_alerts +#: model:mail.template,name:board_alerts.board_alerts_mail_template +msgid "Board alert email" +msgstr "E-mail d'alerte du tableau de bord" + +#. module: board_alerts #: model:ir.model,name:board_alerts.model_board_alerts_dlg msgid "Board alert sender dialog box" msgstr "Boîte de dialogue d'envoi d'alertes de tableau de bord" @@ -62,7 +68,6 @@ #. module: board_alerts #: model:ir.actions.server,name:board_alerts.board_alerts_cron_task_ir_actions_server #: model:ir.cron,cron_name:board_alerts.board_alerts_cron_task -#: model:ir.cron,name:board_alerts.board_alerts_cron_task msgid "Board alerts - Daily emails" msgstr "Tableau de bord d'alertes - Emails quotidiens" @@ -72,7 +77,7 @@ msgstr "Annuler" #. module: board_alerts -#. openerp-web +#. odoo-javascript #: code:addons/board_alerts/static/src/js/alert_board.js:0 #, python-format msgid "Could not add filter to alert dashboard" @@ -99,6 +104,7 @@ msgstr "ID" #. module: board_alerts +#. odoo-python #: code:addons/board_alerts/models/board_alerts.py:0 #, python-format msgid "Lang %s not found" @@ -120,6 +126,7 @@ msgstr "Modifié le" #. module: board_alerts +#. odoo-python #: code:addons/board_alerts/models/board_alerts.py:0 #: model:ir.actions.act_window,name:board_alerts.action_alert_board #: model:ir.ui.menu,name:board_alerts.menu_alert_board @@ -129,9 +136,10 @@ msgstr "Mes alertes" #. module: board_alerts +#. odoo-python #: code:addons/board_alerts/models/board_alerts.py:0 #, python-format msgid "No" msgstr "Non" #. module: board_alerts @@ -132,10 +140,10 @@ #: code:addons/board_alerts/models/board_alerts.py:0 #, python-format msgid "No" msgstr "Non" #. module: board_alerts -#. openerp-web +#. odoo-javascript #: code:addons/board_alerts/static/src/js/alert_board.js:0 #, python-format msgid "Please refresh your browser for the changes to take effect." @@ -155,8 +163,8 @@ #. module: board_alerts #: model:ir.model,name:board_alerts.model_res_users -msgid "Users" -msgstr "Utilisateurs" +msgid "User" +msgstr "Utilisateur" #. module: board_alerts #: model_terms:ir.ui.view,arch_db:board_alerts.board_alerts_dlg @@ -168,6 +176,7 @@ "les alertes des tableaux\"." #. module: board_alerts +#. odoo-python #: code:addons/board_alerts/models/board_alerts.py:0 #, python-format msgid "Yes" diff --git a/models/__init__.py b/models/__init__.py index e2623afff2ec70097927cb98e2dc22e2e44f48a4_bW9kZWxzL19faW5pdF9fLnB5..1d0c80b8baa395de2b880e3cccc3b03cfb3468b0_bW9kZWxzL19faW5pdF9fLnB5 100644 --- a/models/__init__.py +++ b/models/__init__.py @@ -1,3 +1,1 @@ -# flake8: noqa - from . import board_alerts diff --git a/models/board_alerts.py b/models/board_alerts.py index e2623afff2ec70097927cb98e2dc22e2e44f48a4_bW9kZWxzL2JvYXJkX2FsZXJ0cy5weQ==..1d0c80b8baa395de2b880e3cccc3b03cfb3468b0_bW9kZWxzL2JvYXJkX2FsZXJ0cy5weQ== 100644 --- a/models/board_alerts.py +++ b/models/board_alerts.py @@ -32,7 +32,6 @@ @api.model def send_board_alerts(self): - """Find users and send them their board alerts. - """ + """Find users and send them their board alerts.""" # Get our email template, referenced by its XML ID. @@ -37,8 +36,6 @@ # Get our email template, referenced by its XML ID. - email_template = self.sudo().env.ref( - "board_alerts.board_alerts_mail_template" - ) + email_template = self.sudo().env.ref("board_alerts.board_alerts_mail_template") # Loop through all users; send them an email. for user in self.sudo().search([]): @@ -50,9 +47,9 @@ # Fill the context to avoid computing contents twice. ( - email_template.with_context( - board_alert_contents=contents - ).send_mail(user.id) + email_template.with_context(board_alert_contents=contents).send_mail( + user.id + ) ) def get_board_alert_contents(self): @@ -83,9 +80,7 @@ board_view = self.env.ref("board_alerts.alert_board") # Set up the link that will be inserted in emails. - board_link = ( - self.env["ir.config_parameter"].sudo().get_param("web.base.url") - ) + board_link = self.env["ir.config_parameter"].sudo().get_param("web.base.url") if board_link: board_link += "/?db=%s#action=%s" % ( self.env.cr.dbname, @@ -93,7 +88,7 @@ ) # Get the "custom view" representing the board. - board = self.env["board.board"].fields_view_get(view_id=board_view.id) + board = self.env["board.board"].get_view(view_id=board_view.id) to_send = [] @@ -132,7 +127,7 @@ ) .id ) - act_view = act_model.with_context(act_context).fields_view_get( + act_view = act_model.with_context(act_context).get_view( view_id=act_view_id, view_type=view_type ) @@ -194,12 +189,10 @@ for data_title, data in data_list: frame = etree.SubElement(root, "div") frame.attrib["style"] = ( - "border: 1px solid LightGray;" - "margin-top: 8px;" - "padding: 8px;" + "border: 1px solid LightGray;" "margin-top: 8px;" "padding: 8px;" ) title = etree.SubElement(frame, "h3") title.text = data_title or "" table = etree.SubElement(frame, "table") @@ -200,12 +193,10 @@ ) title = etree.SubElement(frame, "h3") title.text = data_title or "" table = etree.SubElement(frame, "table") - table.attrib["style"] = ( - "border-collapse: collapse;" "border-spacing: 2px;" - ) + table.attrib["style"] = "border-collapse: collapse;" "border-spacing: 2px;" first_record = True @@ -236,11 +227,7 @@ # The user object only has a "lang" selection key; find the actual # language object. - lang = ( - self.sudo() - .env["res.lang"] - .search([("code", "=", self.lang)], limit=1) - ) + lang = self.sudo().env["res.lang"].search([("code", "=", self.lang)], limit=1) if not lang: raise exceptions.UserError(_("Lang %s not found") % self.lang) @@ -251,8 +238,7 @@ "allowed_company_ids": self.company_ids.ids, # --- "date_format": lang.date_format, - "datetime_format": "%s %s" - % (lang.date_format, lang.time_format), + "datetime_format": "%s %s" % (lang.date_format, lang.time_format), "lang": self.lang, "tz": self.tz, "uid": self.id, @@ -337,8 +323,7 @@ return content or "" def _get_object_name(self, content): - """Call the "name_get" function of the specified Odoo record set. - """ + """Call the "name_get" function of the specified Odoo record set.""" # 0: first element of the returned list. # 1: second element of the (ID, name) tuple. diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000000000000000000000000000000000000..1d0c80b8baa395de2b880e3cccc3b03cfb3468b0_cHlwcm9qZWN0LnRvbWw= --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,78 @@ +[project] +name = "odoo-addon-board_alerts" +dynamic = ["version"] +readme = "README.rst" +requires-python = "~=3.10" +license = { file = "LICENSE", name = "GNU Affero General Public License v3" } +keywords = ["odoo"] +authors = [{ name = "XCG Consulting" }] +classifiers = [ + "Programming Language :: Python", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.10", + "Framework :: Odoo", + "Framework :: Odoo :: 16.0", + "License :: OSI Approved :: GNU Affero General Public License v3" +] +dependencies = ["odoo==16.0.*"] + +[project.optional-dependencies] +doc = ["sphinx", "sphinx-odoo-autodoc", "odoo-scripts"] +test = [] + +[project.urls] +repository = "https://orus.io/xcg/odoo-modules/board_alerts" +changelog = "https://orus.io/xcg/odoo-module/board_alerts/-/blob/branch/16.0/NEWS.rst" + +[build-system] +requires = ["hatchling >=1.19", "hatch-vcs"] +build-backend = "hatchling.build" + +[tool.hatch.build] +exclude = [ + "/doc/", + "/.editorconfig", + "/.eslintrc.yml", + "/.flake8", + "/.gitlab-ci.yml", + "/.hgignore", + "/.hgtags", + "/.prettierrc.yml", + "/.yamllint.yaml" +] + +[tool.hatch.build.targets.wheel] +include = [ + "*.csv", + "/i18n/", + "/static/", + "README.rst", + "*.xml", + "*.py", + "*.svg", + "*.png" +] + +[tool.hatch.build.targets.wheel.sources] +"" = "odoo/addons/board_alerts" + +[tool.hatch.version] +source = "vcs" + +[tool.black] +target = 3.10 + +[tool.isort] +py_version = 310 +profile = "black" +known_odoo = ['odoo'] +known_odoo_addons = ['odoo.addons'] +sections = [ + 'FUTURE', + 'STDLIB', + 'THIRDPARTY', + 'ODOO', + 'ODOO_ADDONS', + 'FIRSTPARTY', + 'LOCALFOLDER' +] diff --git a/security/ir.model.access.csv b/security/ir.model.access.csv new file mode 100644 index 0000000000000000000000000000000000000000..1d0c80b8baa395de2b880e3cccc3b03cfb3468b0_c2VjdXJpdHkvaXIubW9kZWwuYWNjZXNzLmNzdg== --- /dev/null +++ b/security/ir.model.access.csv @@ -0,0 +1,2 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_board_alerts_dlg,board_alerts_dlg,model_board_alerts_dlg,base.group_user,1,1,1,1 diff --git a/static/src/js/alert_board.js b/static/src/js/alert_board.js index e2623afff2ec70097927cb98e2dc22e2e44f48a4_c3RhdGljL3NyYy9qcy9hbGVydF9ib2FyZC5qcw==..1d0c80b8baa395de2b880e3cccc3b03cfb3468b0_c3RhdGljL3NyYy9qcy9hbGVydF9ib2FyZC5qcw== 100644 --- a/static/src/js/alert_board.js +++ b/static/src/js/alert_board.js @@ -1,3 +1,13 @@ +/** @odoo-module **/ + +import {Dropdown} from "@web/core/dropdown/dropdown"; +import {registry} from "@web/core/registry"; +import {useAutofocus, useService} from "@web/core/utils/hooks"; +import {sprintf} from "@web/core/utils/strings"; + +const {Component, useState} = owl; +const favoriteMenuRegistry = registry.category("favoriteMenu"); + /* Alert board. */ /* Taken from the default "My board" dashboard. @@ -6,17 +16,9 @@ * Adapted to add an "Add to my alert board" menu command. */ -odoo.define('board_alerts.AddToAlertBoardMenu', function(require) { - "use strict"; - - var ActionManager = require('web.ActionManager'); - var Context = require('web.Context'); - var core = require('web.core'); - var Domain = require('web.Domain'); - var favorites_submenus_registry = require('web.favorites_submenus_registry'); - var pyUtils = require('web.py_utils'); - var Widget = require('web.Widget'); - - var _t = core._t; - var QWeb = core.qweb; +export class AddToAlertBoard extends Component { + setup() { + this.notification = useService("notification"); + this.rpc = useService("rpc"); + this.state = useState({name: this.env.config.getDisplayName()}); @@ -22,28 +24,4 @@ - var AddToAlertBoardMenu = Widget.extend({ - events: _.extend({}, Widget.prototype.events, { - 'click .o_add_to_alert_board.o_menu_header': '_onAlertMenuHeaderClick', - 'click .o_add_to_alert_board_confirm_button': '_onAddToAlertBoardConfirmButtonClick', - 'click .o_add_to_alert_board_input': '_onAddToAlertBoardInputClick', - 'keyup .o_add_to_alert_board_input': '_onAlertKeyUp', - }), - /** - * @override - * @param {Object} params - * @param {Object} params.action an ir.actions description - */ - init: function(parent, params) { - this._super(parent); - this.action = params.action; - this.isOpen = false; - }, - /** - * @override - */ - start: function() { - if (this.action.id && this.action.type === 'ir.actions.act_window') { - this._render(); - } - return this._super.apply(this, arguments); - }, + useAutofocus(); + } @@ -49,5 +27,5 @@ - //-------------------------------------------------------------------------- - // Public - //-------------------------------------------------------------------------- + //--------------------------------------------------------------------- + // Protected + //--------------------------------------------------------------------- @@ -53,14 +31,14 @@ - /** - * Closes the menu and render it. - * - */ - closeMenu: function() { - this.isOpen = false; - this._render(); - }, - - //-------------------------------------------------------------------------- - // Private - //-------------------------------------------------------------------------- + async addToAlertBoard() { + const {domain, globalContext} = this.env.searchModel; + const {comparison, context, groupBys, orderBy} = + this.env.searchModel.getPreFavoriteValues(); + const contextToSave = { + ...globalContext, + ...context, + comparison, + orderedBy: orderBy, + group_by: groupBys, + dashboard_merge_domains_contexts: false, + }; @@ -66,42 +44,9 @@ - /** - * This is the main function for actually saving the dashboard. This method is supposed to call - * the route /board/add_to_alert_dashboard with proper information. - * - * @private - * @returns {Promise} - */ - _addToAlertBoard: function() { - var self = this; - var searchQuery; - // TO DO: for now the domains in query are evaluated. - // This should be changed I think. - this.trigger_up('get_search_query', { - callback: function(query) { - searchQuery = query; - } - }); - // TO DO: replace direct reference to action manager, controller, and currentAction in code below - - // AAB: trigger_up an event that will be intercepted by the controller, - // as soon as the controller is the parent of the control panel - var actionManager = this.findAncestor(function(ancestor) { - return ancestor instanceof ActionManager; - }); - var controller = actionManager.getCurrentController(); - - var context = new Context(this.action.context); - context.add(searchQuery.context); - context.add({ - group_by: searchQuery.groupBy, - orderedBy: searchQuery.orderedBy, - }); - - this.trigger_up('get_controller_query_params', { - callback: function(controllerQueryParams) { - var queryContext = controllerQueryParams.context; - var allContext = _.extend(_.omit(controllerQueryParams, ['context']), queryContext); - context.add(allContext); - } + const result = await this.rpc("/board/add_to_alert_dashboard", { + action_id: this.env.config.actionId || false, + context_to_save: contextToSave, + domain, + name: this.state.name, + view_mode: this.env.config.viewType, }); @@ -106,11 +51,24 @@ }); - var domain = new Domain(this.action.domain || []); - domain = Domain.prototype.normalizeArray(domain.toArray().concat(searchQuery.domain)); - - var evalutatedContext = pyUtils.eval('context', context); - for ( var key in evalutatedContext) { - if (evalutatedContext.hasOwnProperty(key) && /^search_default_/.test(key)) { - delete evalutatedContext[key]; - } + if (result) { + this.notification.add( + this.env._t( + "Please refresh your browser for the changes to take effect." + ), + { + title: sprintf( + this.env._t(`"%s" added to alert dashboard`), + this.state.name + ), + type: "warning", + } + ); + this.state.name = this.env.config.getDisplayName(); + } else { + this.notification.add( + this.env._t("Could not add filter to alert dashboard"), + { + type: "danger", + } + ); } @@ -116,7 +74,3 @@ } - evalutatedContext.dashboard_merge_domains_contexts = false; - - var name = this.$input.val(); - - this.closeMenu(); + } @@ -122,46 +76,3 @@ - return self._rpc({ - route: '/board/add_to_alert_dashboard', - params: { - action_id: self.action.id || false, - context_to_save: evalutatedContext, - domain: domain, - view_mode: controller.viewType, - name: name, - }, - }).then(function(r) { - if (r) { - self.do_notify(_.str.sprintf(_t("'%s' added to alert dashboard"), name), _t('Please refresh your browser for the changes to take effect.')); - } else { - self.do_warn(_t("Could not add filter to alert dashboard")); - } - }); - }, - /** - * Renders and focuses the unique input if it is visible. - * - * @private - */ - _render: function() { - var $el = QWeb.render('AddToAlertBoardMenu', { - widget: this - }); - this._replaceElement($el); - if (this.isOpen) { - this.$input = this.$('.o_add_to_alert_board_input'); - this.$input.val(this.action.name); - this.$input.focus(); - } - }, - /** - * Hides and displays the submenu which allows adding custom filters. - * - * @private - */ - _toggleMenu: function() { - this.isOpen = !this.isOpen; - this._render(); - }, - - //-------------------------------------------------------------------------- + //--------------------------------------------------------------------- // Handlers @@ -167,4 +78,4 @@ // Handlers - //-------------------------------------------------------------------------- + //--------------------------------------------------------------------- /** @@ -169,23 +80,4 @@ /** - * @private - * @param {jQueryEvent} event - */ - _onAddToAlertBoardInputClick: function(event) { - event.preventDefault(); - event.stopPropagation(); - this.$input.focus(); - }, - /** - * @private - * @param {jQueryEvent} event - */ - _onAddToAlertBoardConfirmButtonClick: function(event) { - event.preventDefault(); - event.stopPropagation(); - this._addToAlertBoard(); - }, - /** - * @private - * @param {jQueryEvent} event + * @param {KeyboardEvent} ev */ @@ -191,5 +83,6 @@ */ - _onAlertKeyUp: function(event) { - if (event.which === $.ui.keyCode.ENTER) { - this._addToAlertBoard(); + onInputKeydown(ev) { + if (ev.key === "Enter") { + ev.preventDefault(); + this.addToAlertBoard(); } @@ -195,12 +88,7 @@ } - }, - /** - * @private - * @param {jQueryEvent} event - */ - _onAlertMenuHeaderClick: function(event) { - event.preventDefault(); - event.stopPropagation(); - this._toggleMenu(); - }, + } +} + +AddToAlertBoard.template = "board_alerts.AddToAlertBoard"; +AddToAlertBoard.components = {Dropdown}; @@ -206,3 +94,8 @@ - }); +export const addToAlertBoardItem = { + Component: AddToAlertBoard, + groupNumber: 4, + isDisplayed: ({config}) => + config.actionType === "ir.actions.act_window" && config.actionId, +}; @@ -208,6 +101,2 @@ - // Use prio=11 here so this comes right after the base "add_to_board_menu" which has prio=10. - favorites_submenus_registry.add('add_to_alert_board_menu', AddToAlertBoardMenu, 11); - - return AddToAlertBoardMenu; -}); +favoriteMenuRegistry.add("add-to-board-alerts", addToAlertBoardItem, {sequence: 11}); diff --git a/static/src/xml/alert_board.xml b/static/src/xml/alert_board.xml index e2623afff2ec70097927cb98e2dc22e2e44f48a4_c3RhdGljL3NyYy94bWwvYWxlcnRfYm9hcmQueG1s..1d0c80b8baa395de2b880e3cccc3b03cfb3468b0_c3RhdGljL3NyYy94bWwvYWxlcnRfYm9hcmQueG1s 100644 --- a/static/src/xml/alert_board.xml +++ b/static/src/xml/alert_board.xml @@ -1,1 +1,2 @@ +<?xml version="1.0" encoding="UTF-8" ?> <!-- QWeb template for the alert board. --> @@ -1,19 +2,26 @@ <!-- QWeb template for the alert board. --> - -<template> - - <!-- Include a link in the "Favorites" menu to add to the alert dashboard. Same - as the default "Add to my dashboard" link defined in - odoo/addons/board/static/src/xml/board.xml. --> - <div t-name="AddToAlertBoardMenu"> - <button type="button" class="dropdown-item o_add_to_alert_board o_menu_header">Add to my alert dashboard</button> - <div t-if="widget.isOpen" class="dropdown-item-text o_add_to_alert_board"> - <input class="o_input o_add_to_alert_board_input" type="text" /> - </div> - <div t-if="widget.isOpen" class="dropdown-item-text o_add_to_alert_board"> - <button type="button" - class="btn btn-primary o_add_to_alert_board_confirm_button">Add</button> - </div> - </div> - -</template> +<templates xml:space="preserve"> + <t t-name="board_alerts.AddToAlertBoard" owl="1"> + <Dropdown class="'o_add_to_board'"> + <t t-set-slot="toggler">Add to my alert dashboard</t> + <div class="px-3 py-2"> + <input + type="text" + class="o_input" + t-ref="autofocus" + t-model.trim="state.name" + t-on-keydown="onInputKeydown" + /> + </div> + <div class="px-3 py-2"> + <button + type="button" + class="btn btn-primary" + t-on-click="addToAlertBoard" + > + Add + </button> + </div> + </Dropdown> + </t> +</templates> diff --git a/views/board_alerts_assets.xml b/views/board_alerts_assets.xml deleted file mode 100644 index e2623afff2ec70097927cb98e2dc22e2e44f48a4_dmlld3MvYm9hcmRfYWxlcnRzX2Fzc2V0cy54bWw=..0000000000000000000000000000000000000000 --- a/views/board_alerts_assets.xml +++ /dev/null @@ -1,15 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<odoo> - - <!-- Include assets used by alert boards. --> - - <template id="assets_backend" name="board_alerts assets" - inherit_id="web.assets_backend"> - <xpath expr="." position="inside"> - <link rel="stylesheet" type="text/scss" - href="/board_alerts/static/src/scss/alert_board.scss" /> - <script type="text/javascript" src="/board_alerts/static/src/js/alert_board.js"></script> - </xpath> - </template> - -</odoo> diff --git a/wizard/__init__.py b/wizard/__init__.py index e2623afff2ec70097927cb98e2dc22e2e44f48a4_d2l6YXJkL19faW5pdF9fLnB5..1d0c80b8baa395de2b880e3cccc3b03cfb3468b0_d2l6YXJkL19faW5pdF9fLnB5 100644 --- a/wizard/__init__.py +++ b/wizard/__init__.py @@ -1,3 +1,1 @@ -# flake8: noqa - from . import board_alerts_dlg diff --git a/wizard/board_alerts_dlg.py b/wizard/board_alerts_dlg.py index e2623afff2ec70097927cb98e2dc22e2e44f48a4_d2l6YXJkL2JvYXJkX2FsZXJ0c19kbGcucHk=..1d0c80b8baa395de2b880e3cccc3b03cfb3468b0_d2l6YXJkL2JvYXJkX2FsZXJ0c19kbGcucHk= 100644 --- a/wizard/board_alerts_dlg.py +++ b/wizard/board_alerts_dlg.py @@ -10,8 +10,7 @@ _description = "Board alert sender dialog box" def send_board_alerts(self): - """Send board alerts then show emails. - """ + """Send board alerts then show emails.""" self.env["res.users"].send_board_alerts() diff --git a/wizard/board_alerts_dlg.xml b/wizard/board_alerts_dlg.xml index e2623afff2ec70097927cb98e2dc22e2e44f48a4_d2l6YXJkL2JvYXJkX2FsZXJ0c19kbGcueG1s..1d0c80b8baa395de2b880e3cccc3b03cfb3468b0_d2l6YXJkL2JvYXJkX2FsZXJ0c19kbGcueG1s 100644 --- a/wizard/board_alerts_dlg.xml +++ b/wizard/board_alerts_dlg.xml @@ -1,2 +1,2 @@ -<?xml version="1.0" encoding="utf-8"?> +<?xml version="1.0" encoding="utf-8" ?> <odoo> @@ -2,5 +2,4 @@ <odoo> - <!-- Dialog shown before manually sending board alert emails, to have some kind of confirmation. --> @@ -9,6 +8,5 @@ <field name="model">board_alerts_dlg</field> <field name="arch" type="xml"> <form> - <footer> <separator @@ -13,4 +11,5 @@ <footer> <separator - string="Warning! Emails will be sent when clicking on the "Send board alerts" button." /> + string="Warning! Emails will be sent when clicking on the "Send board alerts" button." + /> @@ -16,5 +15,9 @@ - <button string="Send board alerts" name="send_board_alerts" - type="object" class="oe_highlight" /> + <button + string="Send board alerts" + name="send_board_alerts" + type="object" + class="oe_highlight" + /> <button string="Cancel" class="oe_link" special="cancel" /> </footer> @@ -19,6 +22,5 @@ <button string="Cancel" class="oe_link" special="cancel" /> </footer> - </form> </field> </record> @@ -32,7 +34,11 @@ <field name="target">new</field> </record> - <menuitem id="menu_board_alerts_dlg" action="action_board_alerts_dlg" - parent="base.menu_reporting_config" sequence="10" groups="base.group_no_one" /> - + <menuitem + id="menu_board_alerts_dlg" + action="action_board_alerts_dlg" + parent="spreadsheet_dashboard.spreadsheet_dashboard_menu_configuration" + sequence="20" + groups="base.group_no_one" + /> </odoo>