Compare commits
No commits in common. '14.0' and '9.0' have entirely different histories.
-
1.DINAR/build-date.txt
-
15.DINAR/config.yaml
-
4.DINAR/image/README.md
-
2.DINAR/image/dependencies/pip.txt
-
1.DINAR/image/src/addons.yaml
-
12.DINAR/image/src/repos.yaml
-
20.editorconfig
-
184.eslintrc.yml
-
11.flake8
-
2.github/FUNDING.yml
-
145.github/workflows/DINAR-PORT.yml
-
264.github/workflows/DINAR-pr.yml
-
2.github/workflows/DINAR-readme.yml
-
116.github/workflows/DINAR.yml
-
19.github/workflows/main.yml
-
58.gitignore
-
13.isort.cfg
-
111.pre-commit-config.yaml
-
8.prettierrc.yml
-
86.pylintrc
-
65.pylintrc-mandatory
-
45.travis.yml
-
166LICENSE
-
22README.md
-
17mail_all/README.rst
-
3mail_all/__init__.py
-
26mail_all/__openerp__.py
-
7mail_all/doc/changelog.rst
-
9mail_all/doc/index.rst
-
BINmail_all/images/1.jpg
-
1mail_all/models/__init__.py
-
BINmail_all/static/description/1.png
-
BINmail_all/static/description/2.png
-
BINmail_all/static/description/icon.png
-
45mail_all/static/description/index.html
-
65mail_all/static/src/js/mail_all.js
-
19mail_all/static/src/xml/menu.xml
-
3mail_all/tests/__init__.py
-
18mail_all/tests/test_js.py
-
12mail_all/views/templates.xml
-
19mail_archives/README.rst
-
1mail_archives/__init__.py
-
24mail_archives/__openerp__.py
-
BINmail_archives/images/1.jpg
-
BINmail_archives/static/description/1.png
-
BINmail_archives/static/description/2.png
-
BINmail_archives/static/description/icon.png
-
82mail_archives/static/description/index.html
-
85mail_archives/static/src/js/archives.js
-
19mail_archives/static/src/xml/menu.xml
-
3mail_archives/tests/__init__.py
-
18mail_archives/tests/test_js.py
-
12mail_archives/views/templates.xml
-
26mail_attachment_popup/README.rst
-
0mail_attachment_popup/__init__.py
-
26mail_attachment_popup/__openerp__.py
-
4mail_attachment_popup/doc/changelog.rst
-
16mail_attachment_popup/doc/index.rst
-
BINmail_attachment_popup/images/popup_image.png
-
BINmail_attachment_popup/static/description/attach_image.png
-
BINmail_attachment_popup/static/description/download.png
-
BINmail_attachment_popup/static/description/icon.png
-
84mail_attachment_popup/static/description/index.html
-
BINmail_attachment_popup/static/description/popup.png
-
429mail_attachment_popup/static/lib/js/jquery.arcticmodal.js
-
8mail_attachment_popup/static/src/css/jquery.arcticmodal.css
-
11mail_attachment_popup/static/src/css/simple.css
-
16mail_attachment_popup/static/src/css/styles.css
-
BINmail_attachment_popup/static/src/img/loading.gif
-
29mail_attachment_popup/static/src/xml/mail_attachment_popup.xml
-
13mail_attachment_popup/views/mail_attachment_popup_template.xml
-
26mail_base/README.rst
-
4mail_base/__init__.py
-
22mail_base/__openerp__.py
-
1mail_base/controllers/__init__.py
-
15mail_base/controllers/main.py
-
9mail_base/doc/changelog.rst
-
4mail_base/doc/index.rst
-
31mail_base/models.py
-
BINmail_base/static/description/icon.png
-
1197mail_base/static/lib/base.js
-
12mail_base/views/templates.xml
-
12mail_move_message/README.rst
-
3mail_move_message/__init__.py
-
19mail_move_message/__openerp__.py
-
2mail_move_message/controllers/__init__.py
-
69mail_move_message/controllers/main.py
-
9mail_move_message/data/mail_move_message_data.xml
-
21mail_move_message/doc/changelog.rst
-
173mail_move_message/i18n/mail_move_message.pot
-
160mail_move_message/i18n/sl.po
-
BINmail_move_message/images/inbox.png
-
413mail_move_message/mail_move_message_models.py
-
122mail_move_message/mail_move_message_views.xml
-
BINmail_move_message/static/description/delete-message.png
-
BINmail_move_message/static/description/html-message-viewer.png
-
BINmail_move_message/static/description/html-message-viewer1.png
-
BINmail_move_message/static/description/html-message.png
-
BINmail_move_message/static/description/icon.png
-
BINmail_move_message/static/description/inbox-move.png
@ -1 +0,0 @@ |
|||||
new repo readme files |
|
@ -1,15 +0,0 @@ |
|||||
addons: |
|
||||
# modules to install before running tests |
|
||||
include: |
|
||||
- contacts |
|
||||
# "contacts" already has mail in dependencies, |
|
||||
# but without the following line DINAR may run odoo odoo with --init=mail,.... |
|
||||
# which leads to error on loading mail's demo data |
|
||||
- mail |
|
||||
|
|
||||
# modules to exclude from installation/testing |
|
||||
exclude: |
|
||||
- hw_proxy |
|
||||
|
|
||||
server_wide_modules: |
|
||||
- web |
|
@ -1,4 +0,0 @@ |
|||||
This folder is attached on image building as `custom/` folder in |
|
||||
[doobba](https://github.com/Tecnativa/doodba#image-usage). Few additional |
|
||||
[files](https://github.com/itpp-labs/DINAR/tree/master/embedded-files/.DINAR/image) are |
|
||||
attached temporary on image building. |
|
@ -1,2 +0,0 @@ |
|||||
# Python dependencies |
|
||||
pycryptodome |
|
@ -1 +0,0 @@ |
|||||
# see https://github.com/Tecnativa/doodba#optodoocustomsrcaddonsyaml |
|
@ -1,12 +0,0 @@ |
|||||
# Documentation: https://github.com/Tecnativa/doodba#optodoocustomsrcreposyaml |
|
||||
|
|
||||
# Odoo source. |
|
||||
# Update this section to switch to OCA/OCB or apply custom patches |
|
||||
odoo: |
|
||||
defaults: |
|
||||
depth: 1 |
|
||||
remotes: |
|
||||
origin: https://github.com/odoo/odoo.git |
|
||||
target: origin $ODOO_VERSION |
|
||||
merges: |
|
||||
- origin $ODOO_VERSION |
|
@ -1,20 +0,0 @@ |
|||||
# Configuration for known file extensions |
|
||||
[*.{css,js,json,less,md,py,rst,sass,scss,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,md}] |
|
||||
indent_size = 2 |
|
||||
|
|
||||
# Do not configure editor for libs and autogenerated content |
|
||||
[{*/static/{lib,src/lib}/**,*/static/description/index.html,*/readme/../README.rst}] |
|
||||
charset = unset |
|
||||
end_of_line = unset |
|
||||
indent_size = unset |
|
||||
indent_style = unset |
|
||||
insert_final_newline = false |
|
||||
trim_trailing_whitespace = false |
|
@ -1,184 +0,0 @@ |
|||||
env: |
|
||||
browser: true |
|
||||
|
|
||||
# See https://github.com/OCA/odoo-community.org/issues/37#issuecomment-470686449 |
|
||||
parserOptions: |
|
||||
ecmaVersion: 2017 |
|
||||
|
|
||||
# Globals available in Odoo that shouldn't produce errorings |
|
||||
globals: |
|
||||
_: readonly |
|
||||
$: readonly |
|
||||
fuzzy: readonly |
|
||||
jQuery: readonly |
|
||||
moment: readonly |
|
||||
odoo: readonly |
|
||||
openerp: readonly |
|
||||
Promise: 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: off |
|
||||
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 |
|
||||
- args: none |
|
||||
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] |
|
||||
space-before-function-paren: ["warn", {"anonymous": "always", "named": "never"}] |
|
||||
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 |
|
@ -1,11 +0,0 @@ |
|||||
[flake8] |
|
||||
max-line-length = 80 |
|
||||
max-complexity = 16 |
|
||||
# B = bugbear |
|
||||
# B9 = bugbear opinionated (incl line length) |
|
||||
select = C,E,F,W,B,B9 |
|
||||
# E203: whitespace before ':' (black behaviour) |
|
||||
# E501: flake8 line length (covered by bugbear B950) |
|
||||
# W503: line break before binary operator (black behaviour) |
|
||||
# C901: "method is too complex" -- it's too complex to fix existing modules. Disable for now |
|
||||
ignore = E203,E501,W503,B950,C901 |
|
@ -1,2 +0,0 @@ |
|||||
ko_fi: itprojectsllc # This is supposed to bring some coffee for us |
|
||||
patreon: itpp # become our patron |
|
@ -1,145 +0,0 @@ |
|||||
# Copyright 2020 IT Projects Labs |
|
||||
# |
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); |
|
||||
# you may not use this file except in compliance with the License. |
|
||||
# You may obtain a copy of the License at |
|
||||
# |
|
||||
# http://www.apache.org/licenses/LICENSE-2.0 |
|
||||
# |
|
||||
# Unless required by applicable law or agreed to in writing, software |
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, |
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
||||
# See the License for the specific language governing permissions and |
|
||||
# limitations under the License. |
|
||||
name: "Port module" |
|
||||
|
|
||||
on: |
|
||||
issues: |
|
||||
types: |
|
||||
- opened |
|
||||
- reopened |
|
||||
|
|
||||
jobs: |
|
||||
port: |
|
||||
runs-on: ubuntu-latest |
|
||||
if: "startsWith(github.event.issue.title, 'DINAR-PORT ')" |
|
||||
steps: |
|
||||
- name: Post link |
|
||||
uses: KeisukeYamashita/create-comment@v1 |
|
||||
with: |
|
||||
comment: |
|
||||
"Porting is started. Check logs: https://github.com/${{ github.repository |
|
||||
}}/actions/runs/${{ github.run_id }}" |
|
||||
- name: Checkout DINAR |
|
||||
uses: actions/checkout@v2 |
|
||||
with: |
|
||||
path: DINAR |
|
||||
repository: itpp-labs/DINAR-fork |
|
||||
ref: master |
|
||||
- uses: actions/setup-python@v2 |
|
||||
with: |
|
||||
python-version: "3.7.x" |
|
||||
- name: Install python tools |
|
||||
run: | |
|
||||
pip install plumbum pre-commit git-aggregator |
|
||||
- name: Check Python Version |
|
||||
run: |
|
||||
echo "PY=$(python --version --version | sha256sum | cut -d' ' -f1)" >> |
|
||||
$GITHUB_ENV |
|
||||
- name: Analyze request |
|
||||
env: |
|
||||
TITLE: ${{ github.event.issue.title }} |
|
||||
run: | |
|
||||
# sets environment variables that available in next steps via $ {{ env.PORT_... }} notation |
|
||||
python DINAR/workflow-files/analyze_port_trigger.py "$TITLE" |
|
||||
- name: Checkout Repo |
|
||||
uses: actions/checkout@v2 |
|
||||
with: |
|
||||
path: REPO |
|
||||
fetch-depth: 0 |
|
||||
ref: ${{ env.PORT_TO_BRANCH }} |
|
||||
- uses: actions/cache@v1 |
|
||||
with: |
|
||||
path: ~/.cache/pre-commit |
|
||||
key: pre-commit|${{ env.PY }}|${{ hashFiles('REPO/.pre-commit-config.yaml') }} |
|
||||
- name: Copy module to new branch |
|
||||
run: | |
|
||||
git config --global user.email "itpp-bot@users.noreply.github.com" |
|
||||
git config --global user.name "Mitchell Admin" |
|
||||
cd REPO |
|
||||
if [ ! -d ${{ env.PORT_MODULE }} ] |
|
||||
then |
|
||||
# apply original commit history |
|
||||
if ! git format-patch --keep-subject --stdout origin/${{ env.PORT_TO_BRANCH }}..origin/${{ env.PORT_FROM_BRANCH }} -- ${{ env.PORT_MODULE }} | git am -3 --keep |
|
||||
then |
|
||||
# git am failed |
|
||||
git am --abort |
|
||||
|
|
||||
# just copy source |
|
||||
git checkout origin/${{ env.PORT_FROM_BRANCH }} -- ${{ env.PORT_MODULE }} |
|
||||
git commit -m ":tada:${{ env.PORT_FROM_BRANCH_TAGS }} ${{ env.PORT_MODULE }} |
|
||||
previous commits history: https://github.com/${{ github.repository }}/commits/${{ env.PORT_FROM_BRANCH }}/${{ env.PORT_MODULE }} |
|
||||
|
|
||||
> Made via .github/workflows/DINAR-PORT.yml" |
|
||||
fi |
|
||||
fi |
|
||||
- name: make OCA/odoo-module-migrator |
|
||||
run: | |
|
||||
gitaggregate -c DINAR/workflow-files/odoo-module-migrator-mix.yml |
|
||||
pip install -e ./odoo-module-migrator |
|
||||
- name: apply OCA/odoo-module-migrator |
|
||||
run: | |
|
||||
LOG_FILE=../odoo-module-migrator.logs |
|
||||
cd REPO |
|
||||
odoo-module-migrate \ |
|
||||
--modules ${{ env.PORT_MODULE }} \ |
|
||||
--init-version-name ${{ env.PORT_FROM_BRANCH }} \ |
|
||||
--target-version-name ${{ env.PORT_TO_BRANCH }} \ |
|
||||
--no-commit \ |
|
||||
--no-pre-commit \ |
|
||||
2> $LOG_FILE || true |
|
||||
cat $LOG_FILE |
|
||||
|
|
||||
# remove colors |
|
||||
sed -r -i "s/\x1B\[([0-9]{1,3}(;[0-9]{1,2})?)?[mGK]//g" $LOG_FILE |
|
||||
# escape character |
|
||||
# TODO: update KeisukeYamashita/create-comment to support reading comment's body from file |
|
||||
echo 'MIGRATOR_LOGS<<EOF' >> $GITHUB_ENV |
|
||||
cat $LOG_FILE >> $GITHUB_ENV |
|
||||
echo 'EOF' >> $GITHUB_ENV |
|
||||
|
|
||||
git add -A |
|
||||
git commit -m ":arrow_up:${{ env.PORT_TO_BRANCH_TAGS }} OCA/odoo-module-migrator |
|
||||
|
|
||||
close #${{ github.event.issue.number }} |
|
||||
|
|
||||
> Made via .github/workflows/DINAR-PORT.yml" |
|
||||
- name: pre-commit |
|
||||
run: | |
|
||||
cd REPO |
|
||||
pre-commit run --files $(find ${{ env.PORT_MODULE }} -type f) || true |
|
||||
git add -A |
|
||||
git commit -m ":rainbow: pre-commit |
|
||||
> Made via .github/workflows/DINAR-PORT.yml" || echo "pre-commit: no changes" |
|
||||
- name: PR |
|
||||
uses: peter-evans/create-pull-request@v3 |
|
||||
id: cpr |
|
||||
with: |
|
||||
path: REPO |
|
||||
# GITHUB_TOKEN would not trigger PR checks |
|
||||
token: ${{ secrets.DINAR_TOKEN }} |
|
||||
branch: ${{ env.PORT_TO_BRANCH }}-${{ env.PORT_MODULE }} |
|
||||
title: "[${{ env.PORT_TO_BRANCH }}] ${{ env.PORT_MODULE }}" |
|
||||
body: | |
|
||||
Made by [DINAR](https://github.com/itpp-labs/DINAR#readme) by request in #${{ github.event.issue.number }} |
|
||||
- name: Post logs |
|
||||
uses: KeisukeYamashita/create-comment@v1 |
|
||||
with: |
|
||||
number: ${{ steps.cpr.outputs.pull-request-number }} |
|
||||
comment: | |
|
||||
[Migrator](https://github.com/OCA/odoo-module-migrator/)'s [logs](https://github.com/${{ github.repository |
|
||||
}}/actions/runs/${{ github.run_id }}): |
|
||||
|
|
||||
``` |
|
||||
${{ env.MIGRATOR_LOGS }} |
|
||||
``` |
|
@ -1,264 +0,0 @@ |
|||||
# Copyright 2020 IT Projects Labs |
|
||||
# |
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); |
|
||||
# you may not use this file except in compliance with the License. |
|
||||
# You may obtain a copy of the License at |
|
||||
# |
|
||||
# http://www.apache.org/licenses/LICENSE-2.0 |
|
||||
# |
|
||||
# Unless required by applicable law or agreed to in writing, software |
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, |
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
||||
# See the License for the specific language governing permissions and |
|
||||
# limitations under the License. |
|
||||
name: "DINAR" |
|
||||
|
|
||||
on: |
|
||||
pull_request: |
|
||||
|
|
||||
jobs: |
|
||||
pre-commit: |
|
||||
name: "pre-commit" |
|
||||
# Let Quick Review/Tests run first |
|
||||
needs: |
|
||||
- review |
|
||||
- tests |
|
||||
runs-on: ubuntu-latest |
|
||||
steps: |
|
||||
- name: Checkout Repo |
|
||||
uses: actions/checkout@v2 |
|
||||
- uses: actions/setup-python@v1 |
|
||||
with: |
|
||||
python-version: "3.7.x" |
|
||||
- name: Check Python Version |
|
||||
run: |
|
||||
echo "PY=$(python --version --version | sha256sum | cut -d' ' -f1)" >> |
|
||||
$GITHUB_ENV |
|
||||
- uses: actions/cache@v1 |
|
||||
with: |
|
||||
path: ~/.cache/pre-commit |
|
||||
key: pre-commit|${{ env.PY }}|${{ hashFiles('.pre-commit-config.yaml') }} |
|
||||
- uses: pre-commit/action@v1.0.1 |
|
||||
|
|
||||
review: |
|
||||
name: "Quick Review" |
|
||||
runs-on: ubuntu-latest |
|
||||
steps: |
|
||||
- name: Checkout Repo |
|
||||
uses: actions/checkout@v2 |
|
||||
with: |
|
||||
path: REPO |
|
||||
- name: Checkout DINAR |
|
||||
uses: actions/checkout@v2 |
|
||||
with: |
|
||||
path: DINAR |
|
||||
repository: itpp-labs/DINAR-fork |
|
||||
ref: master |
|
||||
- uses: actions/setup-python@v1 |
|
||||
with: |
|
||||
python-version: "3.7.x" |
|
||||
- name: Install python tools |
|
||||
run: | |
|
||||
pip install plumbum PyGithub pyyaml |
|
||||
- name: Analyze PR |
|
||||
run: | |
|
||||
# sets environment variables that available in next steps via $ {{ env.PR_... }} notation |
|
||||
cd REPO |
|
||||
python ../DINAR/workflow-files/analyze-modules.py updated ${{ secrets.GITHUB_TOKEN }} ${{ github.repository }} ${{ github.event.number }} |
|
||||
- name: Configure docker |
|
||||
run: | |
|
||||
bash DINAR/workflow-files/configure-docker.sh ${{ secrets.DINAR_TOKEN || secrets.GITHUB_TOKEN }} |
|
||||
echo "PR_FILES=../../REPO" >> $GITHUB_ENV |
|
||||
- name: HOW TO RUN ODOO LOCALLY |
|
||||
if: always() |
|
||||
run: | |
|
||||
export MODULES=${{ env.PR_MODULES }} |
|
||||
export LOAD_MODULES=${{ env.PR_MODULES_LOAD }} |
|
||||
export PR_NUM=${{ github.event.number }} |
|
||||
export VERSION=${{ github.event.pull_request.base.ref }} |
|
||||
export REVISION_PR=${{ github.event.pull_request.head.sha}} |
|
||||
export DINAR_REPO="itpp-labs/DINAR-fork" |
|
||||
bash DINAR/workflow-files/how-to-run-locally.sh |
|
||||
- name: Check Python Version |
|
||||
run: |
|
||||
echo "PY=$(python --version --version | sha256sum | cut -d' ' -f1)" >> |
|
||||
$GITHUB_ENV |
|
||||
- uses: actions/cache@v1 |
|
||||
with: |
|
||||
path: ~/.cache/pre-commit |
|
||||
key: pre-commit|${{ env.PY }}|${{ hashFiles('REPO/.pre-commit-config.yaml') }} |
|
||||
- name: Install pre-commit |
|
||||
run: | |
|
||||
pip install pre-commit |
|
||||
- name: PRE-COMMIT against updated files only |
|
||||
run: | |
|
||||
cd REPO |
|
||||
git fetch origin ${{ github.event.pull_request.base.ref }} |
|
||||
echo "CHANGED FILES:" |
|
||||
git diff --name-only --no-ext-diff FETCH_HEAD..HEAD -- . |
|
||||
echo "RUN PRE-COMMIT:" |
|
||||
pre-commit run --show-diff-on-failure --color=always --show-diff-on-failure --files $(git diff --name-only --no-ext-diff FETCH_HEAD..HEAD -- .) |
|
||||
|
|
||||
tests: |
|
||||
name: "Quick Tests" |
|
||||
runs-on: ubuntu-latest |
|
||||
steps: |
|
||||
- name: Checkout Repo |
|
||||
uses: actions/checkout@v2 |
|
||||
with: |
|
||||
path: REPO |
|
||||
- name: Checkout DINAR |
|
||||
uses: actions/checkout@v2 |
|
||||
with: |
|
||||
path: DINAR |
|
||||
repository: itpp-labs/DINAR-fork |
|
||||
ref: master |
|
||||
- name: Configure docker |
|
||||
run: | |
|
||||
bash DINAR/workflow-files/configure-docker.sh ${{ secrets.DINAR_TOKEN || secrets.GITHUB_TOKEN }} |
|
||||
echo "PR_FILES=../../REPO" >> $GITHUB_ENV |
|
||||
- name: Install python tools |
|
||||
run: | |
|
||||
pip install plumbum PyGithub pyyaml |
|
||||
- name: Download Docker images with preinstalled modules |
|
||||
run: | |
|
||||
docker-compose -p DINAR -f DINAR/workflow-files/docker-compose-DINAR-pr.yml config |
|
||||
docker-compose -p DINAR -f DINAR/workflow-files/docker-compose-DINAR-pr.yml pull |
|
||||
docker-compose -p DINAR -f DINAR/workflow-files/docker-compose-DINAR-pr.yml up --no-start |
|
||||
- name: Analyze PR |
|
||||
run: | |
|
||||
# Get list of installed modules |
|
||||
ODOO_BASE_MODULES=$(docker inspect \ |
|
||||
--format '{{ index .Config.Labels "dinar.odoo.modules"}}' \ |
|
||||
dinar_odoo_1) |
|
||||
echo "ODOO_BASE_MODULES=$ODOO_BASE_MODULES" |
|
||||
|
|
||||
# sets environment variables that available in next steps via $ {{ env.PR_... }} notation |
|
||||
cd REPO |
|
||||
python ../DINAR/workflow-files/analyze-modules.py updated ${{ secrets.GITHUB_TOKEN }} ${{ github.repository }} ${{ github.event.number }} $ODOO_BASE_MODULES |
|
||||
- name: Install json parser |
|
||||
run: | |
|
||||
sudo apt-get install jq |
|
||||
- name: Install Additional Dependencies (Modules) |
|
||||
if: env.PR_MODULES_DEPS != '' |
|
||||
run: | |
|
||||
# Install new dependencies without tests |
|
||||
export MODULES="${{ env.PR_MODULES_DEPS }}" |
|
||||
export LOAD_MODULES="${{ env.PR_MODULES_LOAD }}" |
|
||||
docker-compose -p DINAR -f DINAR/workflow-files/docker-compose-DINAR-pr.yml config |
|
||||
docker-compose -p DINAR -f DINAR/workflow-files/docker-compose-DINAR-pr.yml up --abort-on-container-exit |
|
||||
- name: Install Additional Dependencies (Packages) |
|
||||
if: env.PR_DEPS != '' |
|
||||
run: | |
|
||||
# TODO https://github.com/itpp-labs/DINAR/issues/42 |
|
||||
exit 1 |
|
||||
- name: Prepare Artifact Folder |
|
||||
run: | |
|
||||
mkdir new-deps/ |
|
||||
# create dummy file to be sure that Artifact will be uploaded |
|
||||
echo ok > new-deps/.empty |
|
||||
echo "ARTIFACT=empty" >> $GITHUB_ENV |
|
||||
- name: Prepare DINAR with additional dependencies |
|
||||
if: env.PR_MODULES_DEPS != '' || env.PR_DEPS != '' |
|
||||
run: | |
|
||||
# Save artifacts for local run and for integrations Tests |
|
||||
bash DINAR/workflow-files/save-docker-layers.sh new-deps/ |
|
||||
echo "${{ env.PR_MODULES_DEPS }}" > new-deps/modules.txt |
|
||||
echo "ARTIFACT=yes" >> $GITHUB_ENV |
|
||||
- name: Save DINAR with dependencies |
|
||||
uses: actions/upload-artifact@v1 |
|
||||
with: |
|
||||
name: new-deps |
|
||||
path: new-deps/ |
|
||||
- name: HOW TO RUN QUICK TESTS LOCALLY |
|
||||
if: always() |
|
||||
run: | |
|
||||
export MODULES=${{ env.PR_MODULES }} |
|
||||
export LOAD_MODULES=${{ env.PR_MODULES_LOAD }} |
|
||||
export PR_NUM=${{ github.event.number }} |
|
||||
export VERSION=${{ github.event.pull_request.base.ref }} |
|
||||
export REVISION_PR=${{ github.event.pull_request.head.sha}} |
|
||||
export DINAR_REPO="itpp-labs/DINAR-fork" |
|
||||
export ODOO_EXTRA_ARG=--test-enable |
|
||||
bash DINAR/workflow-files/how-to-run-locally.sh ${{ secrets.GITHUB_TOKEN }} |
|
||||
- name: Test updated modules |
|
||||
if: env.PR_MODULES != '' |
|
||||
run: | |
|
||||
export MODULES="${{ env.PR_MODULES }}" |
|
||||
export LOAD_MODULES="${{ env.PR_MODULES_LOAD }}" |
|
||||
export ODOO_EXTRA_ARG=--test-enable |
|
||||
docker-compose -p DINAR -f DINAR/workflow-files/docker-compose-DINAR-pr.yml config |
|
||||
docker-compose -p DINAR -f DINAR/workflow-files/docker-compose-DINAR-pr.yml up --abort-on-container-exit |
|
||||
|
|
||||
tests-all: |
|
||||
name: Integration Tests |
|
||||
# Let Quick Review/Tests run first |
|
||||
# This job uses artifacts from "Quick Tests" |
|
||||
needs: |
|
||||
- tests |
|
||||
- review |
|
||||
runs-on: ubuntu-latest |
|
||||
steps: |
|
||||
- name: Checkout Repo |
|
||||
uses: actions/checkout@v2 |
|
||||
with: |
|
||||
path: REPO |
|
||||
- name: Checkout DINAR |
|
||||
uses: actions/checkout@v2 |
|
||||
with: |
|
||||
path: DINAR |
|
||||
repository: itpp-labs/DINAR-fork |
|
||||
ref: master |
|
||||
- name: Install python tools |
|
||||
run: | |
|
||||
pip install plumbum pyyaml |
|
||||
- name: Download Additional Dependencies artifact |
|
||||
uses: actions/download-artifact@v1 |
|
||||
with: |
|
||||
name: new-deps |
|
||||
path: new-deps/ |
|
||||
- name: Check artifact |
|
||||
run: | |
|
||||
if [ ! -f new-deps/modules.txt ]; then |
|
||||
echo "ARTIFACT=empty" >> $GITHUB_ENV |
|
||||
fi |
|
||||
- name: Configure Docker |
|
||||
run: | |
|
||||
bash DINAR/workflow-files/configure-docker.sh ${{ secrets.DINAR_TOKEN || secrets.GITHUB_TOKEN }} |
|
||||
echo "PR_FILES=../../REPO" >> $GITHUB_ENV |
|
||||
- name: Analyze PR |
|
||||
run: | |
|
||||
# sets environment variables that available in next steps via $ {{ env.PR_... }} notation |
|
||||
DEPS_MODULES=$(cat new-deps/modules.txt || true) |
|
||||
cd REPO |
|
||||
python ../DINAR/workflow-files/analyze-modules.py all "$DEPS_MODULES" |
|
||||
- name: Install json parser |
|
||||
run: | |
|
||||
sudo apt-get install jq |
|
||||
- name: HOW TO RUN TESTS LOCALLY |
|
||||
if: always() |
|
||||
run: | |
|
||||
export MODULES=${{ env.ALL_MODULES }} |
|
||||
export LOAD_MODULES=${{ env.ALL_MODULES_LOAD }} |
|
||||
export PR_NUM=${{ github.event.number }} |
|
||||
export VERSION=${{ github.event.pull_request.base.ref }} |
|
||||
export REVISION_PR=${{ github.event.pull_request.head.sha}} |
|
||||
export DINAR_REPO="itpp-labs/DINAR-fork" |
|
||||
export ODOO_EXTRA_ARG=--test-enable |
|
||||
bash DINAR/workflow-files/how-to-run-locally.sh ${{ secrets.GITHUB_TOKEN }} |
|
||||
- name: Download base images |
|
||||
run: | |
|
||||
docker-compose -p DINAR -f DINAR/workflow-files/docker-compose-DINAR-pr.yml config |
|
||||
docker-compose -p DINAR -f DINAR/workflow-files/docker-compose-DINAR-pr.yml pull |
|
||||
docker-compose -p DINAR -f DINAR/workflow-files/docker-compose-DINAR-pr.yml up --no-start |
|
||||
- name: Apply new-deps artifact |
|
||||
if: env.ARTIFACT != 'empty' |
|
||||
run: | |
|
||||
bash DINAR/workflow-files/load-docker-layers.sh new-deps/ |
|
||||
- name: Test all modules |
|
||||
run: | |
|
||||
export MODULES="${{ env.ALL_MODULES }}" |
|
||||
export LOAD_MODULES="${{ env.ALL_MODULES_LOAD }}" |
|
||||
export ODOO_EXTRA_ARG=--test-enable |
|
||||
docker-compose -p DINAR -f DINAR/workflow-files/docker-compose-DINAR-pr.yml config |
|
||||
docker-compose -p DINAR -f DINAR/workflow-files/docker-compose-DINAR-pr.yml up --abort-on-container-exit |
|
@ -1,116 +0,0 @@ |
|||||
# Copyright 2020 IT Projects Labs |
|
||||
# |
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); |
|
||||
# you may not use this file except in compliance with the License. |
|
||||
# You may obtain a copy of the License at |
|
||||
# |
|
||||
# http://www.apache.org/licenses/LICENSE-2.0 |
|
||||
# |
|
||||
# Unless required by applicable law or agreed to in writing, software |
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, |
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
||||
# See the License for the specific language governing permissions and |
|
||||
# limitations under the License. |
|
||||
name: "DINAR: Docker Image Building" |
|
||||
|
|
||||
on: |
|
||||
push: |
|
||||
paths: |
|
||||
- ".DINAR/**" |
|
||||
- ".github/workflows/DINAR.yml" |
|
||||
|
|
||||
# Cron works only for defaul branch. See https://github.com/itpp-labs/DINAR/issues/48 |
|
||||
schedule: |
|
||||
- cron: "5 5 * * 0" |
|
||||
|
|
||||
jobs: |
|
||||
check-secret: |
|
||||
runs-on: ubuntu-latest |
|
||||
steps: |
|
||||
- name: Check that DINAR_TOKEN is set |
|
||||
run: | |
|
||||
if [ -z "${{ secrets.DINAR_TOKEN }}" ] |
|
||||
then |
|
||||
echo "DINAR_TOKEN is not set" |
|
||||
exit 1 |
|
||||
fi |
|
||||
|
|
||||
check-branch: |
|
||||
runs-on: ubuntu-latest |
|
||||
steps: |
|
||||
- name: Check that this branch needs docker images |
|
||||
run: | |
|
||||
REF=${GITHUB_BASE_REF:-${GITHUB_REF}} |
|
||||
BRANCH=${REF##*/} |
|
||||
CHECK=$( echo "$BRANCH" | grep -E "^(master|[0-9]+\.[0-9]+)(-dev-.+)?$" || true) |
|
||||
if [ -z "$CHECK" ] |
|
||||
then |
|
||||
echo "This branch is not supposed to be a target of pull requests, so docker image is not needed." |
|
||||
echo "For information check https://github.com/itpp-labs/DINAR/issues/60" |
|
||||
exit 1 |
|
||||
fi |
|
||||
|
|
||||
rebuild-images: |
|
||||
runs-on: ubuntu-latest |
|
||||
needs: |
|
||||
- check-secret |
|
||||
- check-branch |
|
||||
steps: |
|
||||
- name: Checkout Repo |
|
||||
uses: actions/checkout@v2 |
|
||||
with: |
|
||||
path: REPO |
|
||||
- name: Checkout DINAR |
|
||||
uses: actions/checkout@v2 |
|
||||
with: |
|
||||
path: DINAR |
|
||||
repository: itpp-labs/DINAR-fork |
|
||||
ref: master |
|
||||
- uses: actions/setup-python@v1 |
|
||||
with: |
|
||||
python-version: "3.7.x" |
|
||||
- name: Prepare build folder |
|
||||
run: | |
|
||||
cp -rnT DINAR/embedded-files/ REPO/ |
|
||||
|
|
||||
- name: Configure Docker |
|
||||
run: | |
|
||||
bash DINAR/workflow-files/configure-docker.sh ${{ secrets.DINAR_TOKEN }} |
|
||||
|
|
||||
cat <<- EOF > REPO/.DINAR/image/.netrc |
|
||||
machine github.com |
|
||||
login $GITHUB_ACTOR |
|
||||
password ${{ secrets.DINAR_TOKEN }} |
|
||||
EOF |
|
||||
- name: Build ${{ env.IMAGE_ODOO_BASE }} |
|
||||
uses: elgohr/Publish-Docker-Github-Action@master |
|
||||
env: |
|
||||
LOCAL_CUSTOM_DIR: ./image |
|
||||
AGGREGATE: true |
|
||||
PIP_INSTALL_ODOO: false |
|
||||
CLEAN: false |
|
||||
COMPILE: false |
|
||||
with: |
|
||||
name: ${{ env.IMAGE_ODOO_BASE }} |
|
||||
registry: ${{ env.REGISTRY }} |
|
||||
username: ${{ env.REGISTRY_USERNAME }} |
|
||||
password: ${{ env.REGISTRY_PASSWORD }} |
|
||||
buildargs: ODOO_VERSION,AGGREGATE,PIP_INSTALL_ODOO,CLEAN,COMPILE,LOCAL_CUSTOM_DIR |
|
||||
workdir: REPO/.DINAR/ |
|
||||
- name: Install python tools |
|
||||
run: | |
|
||||
pip install plumbum pyyaml |
|
||||
- name: Compute Modules Dependencies |
|
||||
run: | |
|
||||
# sets environment variables that available in next steps via $ {{ env.VAR_NAME }} notation |
|
||||
cd REPO |
|
||||
python ../DINAR/workflow-files/analyze-modules.py all |
|
||||
- name: Install Base Addons |
|
||||
run: | |
|
||||
export MODULES=$ALL_MODULES_DEPENDENCIES |
|
||||
|
|
||||
export DOODBA_WITHOUT_DEMO=all |
|
||||
bash DINAR/workflow-files/images-with-preinstalled-modules.sh $IMAGE_DB-nodemo $IMAGE_ODOO-nodemo |
|
||||
|
|
||||
export DOODBA_WITHOUT_DEMO=false |
|
||||
bash DINAR/workflow-files/images-with-preinstalled-modules.sh $IMAGE_DB $IMAGE_ODOO |
|
@ -1,19 +0,0 @@ |
|||||
name: Telegram Notifications |
|
||||
|
|
||||
on: |
|
||||
issues: |
|
||||
types: [opened, reopened, deleted, closed] |
|
||||
|
|
||||
jobs: |
|
||||
notify: |
|
||||
runs-on: ubuntu-latest |
|
||||
|
|
||||
steps: |
|
||||
- name: Send notifications to Telegram |
|
||||
run: |
|
||||
curl -s -X POST https://api.telegram.org/bot${{ secrets.TELEGRAM_TOKEN |
|
||||
}}/sendMessage -d chat_id=${{ secrets.TELEGRAM_CHAT_ID }} -d text="${MESSAGE}" |
|
||||
>> /dev/null |
|
||||
env: |
|
||||
MESSAGE: |
|
||||
"Issue ${{ github.event.action }}: \n${{ github.event.issue.html_url }}" |
|
@ -0,0 +1,58 @@ |
|||||
|
# Byte-compiled / optimized / DLL files |
||||
|
__pycache__/ |
||||
|
*.py[cod] |
||||
|
|
||||
|
# C extensions |
||||
|
*.so |
||||
|
|
||||
|
# Distribution / packaging |
||||
|
.Python |
||||
|
env/ |
||||
|
bin/ |
||||
|
build/ |
||||
|
develop-eggs/ |
||||
|
dist/ |
||||
|
eggs/ |
||||
|
lib64/ |
||||
|
parts/ |
||||
|
sdist/ |
||||
|
var/ |
||||
|
*.egg-info/ |
||||
|
.installed.cfg |
||||
|
*.egg |
||||
|
|
||||
|
# Installer logs |
||||
|
pip-log.txt |
||||
|
pip-delete-this-directory.txt |
||||
|
|
||||
|
# Unit test / coverage reports |
||||
|
htmlcov/ |
||||
|
.tox/ |
||||
|
.coverage |
||||
|
.cache |
||||
|
nosetests.xml |
||||
|
coverage.xml |
||||
|
|
||||
|
# Translations |
||||
|
*.mo |
||||
|
|
||||
|
# Pycharm |
||||
|
.idea |
||||
|
|
||||
|
# Mr Developer |
||||
|
.mr.developer.cfg |
||||
|
.project |
||||
|
.pydevproject |
||||
|
|
||||
|
# Rope |
||||
|
.ropeproject |
||||
|
|
||||
|
# Sphinx documentation |
||||
|
docs/_build/ |
||||
|
|
||||
|
# Backup files |
||||
|
*~ |
||||
|
*.swp |
||||
|
|
||||
|
# odoo |
||||
|
!static/lib/ |
@ -1,13 +0,0 @@ |
|||||
[settings] |
|
||||
; see https://github.com/psf/black |
|
||||
multi_line_output=3 |
|
||||
include_trailing_comma=True |
|
||||
force_grid_wrap=0 |
|
||||
combine_as_imports=True |
|
||||
use_parentheses=True |
|
||||
line_length=88 |
|
||||
known_odoo=odoo |
|
||||
known_odoo_addons=odoo.addons |
|
||||
sections=FUTURE,STDLIB,THIRDPARTY,ODOO,ODOO_ADDONS,FIRSTPARTY,LOCALFOLDER |
|
||||
default_section=THIRDPARTY |
|
||||
known_third_party= |
|
@ -1,111 +0,0 @@ |
|||||
exclude: | |
|
||||
(?x) |
|
||||
# Files and folders generated by bots, to avoid loops |
|
||||
^setup/|/static/description/index\.html$|/i18n/.*\.pot?$| |
|
||||
# Maybe reactivate this when all README files include prettier ignore tags? |
|
||||
^README\.md$| |
|
||||
# Library files can have extraneous formatting (even minimized) |
|
||||
/static/(src/)?lib/| |
|
||||
# Repos using Sphinx to generate docs don't need prettying |
|
||||
^docs/_templates/.*\.html$| |
|
||||
# You don't usually want a bot to modify your legal texts |
|
||||
(LICENSE.*|COPYING.*) |
|
||||
default_language_version: |
|
||||
python: python3 |
|
||||
node: "14.13.0" |
|
||||
repos: |
|
||||
- repo: https://github.com/myint/autoflake |
|
||||
rev: v1.4 |
|
||||
hooks: |
|
||||
- id: autoflake |
|
||||
args: ["-i", "--ignore-init-module-imports"] |
|
||||
- repo: https://github.com/psf/black |
|
||||
rev: 20.8b1 |
|
||||
hooks: |
|
||||
- id: black |
|
||||
- repo: https://github.com/pre-commit/mirrors-prettier |
|
||||
rev: v2.1.2 |
|
||||
hooks: |
|
||||
- id: prettier |
|
||||
name: prettier + plugin-xml |
|
||||
additional_dependencies: |
|
||||
- "prettier@2.1.2" |
|
||||
- "@prettier/plugin-xml@0.12.0" |
|
||||
args: |
|
||||
- --plugin=@prettier/plugin-xml |
|
||||
- repo: https://github.com/pre-commit/mirrors-eslint |
|
||||
rev: v7.8.1 |
|
||||
hooks: |
|
||||
- id: eslint |
|
||||
verbose: true |
|
||||
args: |
|
||||
- --color |
|
||||
- --fix |
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks |
|
||||
rev: v2.4.0 |
|
||||
hooks: |
|
||||
- id: trailing-whitespace |
|
||||
# exclude autogenerated files |
|
||||
exclude: /README\.rst$ |
|
||||
- id: end-of-file-fixer |
|
||||
# exclude autogenerated files |
|
||||
exclude: /README\.rst$ |
|
||||
- id: debug-statements |
|
||||
- id: fix-encoding-pragma |
|
||||
args: ["--remove"] |
|
||||
- id: check-case-conflict |
|
||||
- id: check-docstring-first |
|
||||
- id: check-executables-have-shebangs |
|
||||
- id: check-merge-conflict |
|
||||
- id: check-symlinks |
|
||||
- id: check-xml |
|
||||
- id: mixed-line-ending |
|
||||
args: ["--fix=lf"] |
|
||||
- repo: https://github.com/PyCQA/pylint |
|
||||
rev: pylint-2.5.3 |
|
||||
hooks: |
|
||||
- id: pylint |
|
||||
name: pylint with optional checks |
|
||||
args: ["--rcfile=.pylintrc", "--exit-zero"] |
|
||||
verbose: true |
|
||||
additional_dependencies: &pylint_deps |
|
||||
- pylint-odoo==3.5.0 |
|
||||
- id: pylint |
|
||||
name: pylint with mandatory checks |
|
||||
args: ["--rcfile=.pylintrc-mandatory"] |
|
||||
additional_dependencies: *pylint_deps |
|
||||
- repo: https://github.com/asottile/pyupgrade |
|
||||
rev: v1.26.2 |
|
||||
hooks: |
|
||||
- id: pyupgrade |
|
||||
args: ["--keep-percent-format"] |
|
||||
- repo: https://github.com/acsone/setuptools-odoo |
|
||||
rev: 2.6.0 |
|
||||
hooks: |
|
||||
- id: setuptools-odoo-make-default |
|
||||
- id: setuptools-odoo-get-requirements |
|
||||
args: |
|
||||
- --output |
|
||||
- requirements.txt |
|
||||
- --header |
|
||||
- "# generated from manifests external_dependencies" |
|
||||
- repo: https://gitlab.com/PyCQA/flake8 |
|
||||
rev: 3.8.3 |
|
||||
hooks: |
|
||||
- id: flake8 |
|
||||
name: flake8 except __init__.py |
|
||||
exclude: /__init__\.py$ |
|
||||
additional_dependencies: ["flake8-bugbear==20.1.4"] |
|
||||
- id: flake8 |
|
||||
name: flake8 only __init__.py |
|
||||
args: ["--extend-ignore=F401"] # ignore unused imports in __init__.py |
|
||||
files: /__init__\.py$ |
|
||||
additional_dependencies: ["flake8-bugbear==20.1.4"] |
|
||||
- repo: https://github.com/PyCQA/isort |
|
||||
rev: 5.5.1 |
|
||||
hooks: |
|
||||
- id: isort |
|
||||
name: isort except __init__.py |
|
||||
args: |
|
||||
- --settings=. |
|
||||
exclude: /__init__\.py$ |
|
@ -1,8 +0,0 @@ |
|||||
# 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" |
|
@ -1,86 +0,0 @@ |
|||||
[MASTER] |
|
||||
load-plugins=pylint_odoo |
|
||||
score=n |
|
||||
|
|
||||
[ODOOLINT] |
|
||||
readme_template_url="https://github.com/OCA/maintainer-tools/blob/master/template/module/README.rst" |
|
||||
manifest_required_authors=IT Projects Labs |
|
||||
manifest_required_keys=license |
|
||||
manifest_deprecated_keys=description,active |
|
||||
license_allowed=AGPL-3,GPL-2,GPL-2 or any later version,GPL-3,GPL-3 or any later version,LGPL-3,MIT,Other OSI approved licence,OPL-1 |
|
||||
valid_odoo_versions=14.0 |
|
||||
|
|
||||
[MESSAGES CONTROL] |
|
||||
disable=all |
|
||||
|
|
||||
# This .pylintrc contains optional AND mandatory checks and is meant to be |
|
||||
# loaded in an IDE to have it check everything, in the hope this will make |
|
||||
# optional checks more visible to contributors who otherwise never look at a |
|
||||
# green travis to see optional checks that failed. |
|
||||
# .pylintrc-mandatory containing only mandatory checks is used the pre-commit |
|
||||
# config as a blocking check. |
|
||||
|
|
||||
enable=anomalous-backslash-in-string, |
|
||||
api-one-deprecated, |
|
||||
api-one-multi-together, |
|
||||
assignment-from-none, |
|
||||
attribute-deprecated, |
|
||||
class-camelcase, |
|
||||
dangerous-default-value, |
|
||||
dangerous-view-replace-wo-priority, |
|
||||
duplicate-id-csv, |
|
||||
duplicate-key, |
|
||||
duplicate-xml-fields, |
|
||||
duplicate-xml-record-id, |
|
||||
eval-referenced, |
|
||||
eval-used, |
|
||||
incoherent-interpreter-exec-perm, |
|
||||
license-allowed, |
|
||||
manifest-author-string, |
|
||||
manifest-deprecated-key, |
|
||||
#manifest-required-author, |
|
||||
manifest-required-key, |
|
||||
manifest-version-format, |
|
||||
method-compute, |
|
||||
method-inverse, |
|
||||
method-required-super, |
|
||||
method-search, |
|
||||
missing-manifest-dependency, |
|
||||
openerp-exception-warning, |
|
||||
pointless-statement, |
|
||||
pointless-string-statement, |
|
||||
print-used, |
|
||||
redundant-keyword-arg, |
|
||||
redundant-modulename-xml, |
|
||||
reimported, |
|
||||
relative-import, |
|
||||
return-in-init, |
|
||||
rst-syntax-error, |
|
||||
sql-injection, |
|
||||
too-few-format-args, |
|
||||
translation-field, |
|
||||
translation-required, |
|
||||
unreachable, |
|
||||
use-vim-comment, |
|
||||
wrong-tabs-instead-of-spaces, |
|
||||
xml-syntax-error, |
|
||||
# messages that do not cause the lint step to fail |
|
||||
consider-merging-classes-inherited, |
|
||||
create-user-wo-reset-password, |
|
||||
dangerous-filter-wo-user, |
|
||||
deprecated-module, |
|
||||
file-not-used, |
|
||||
invalid-commit, |
|
||||
missing-newline-extrafiles, |
|
||||
missing-readme, |
|
||||
no-utf8-coding-comment, |
|
||||
odoo-addons-relative-import, |
|
||||
old-api7-method-defined, |
|
||||
redefined-builtin, |
|
||||
too-complex, |
|
||||
unnecessary-utf8-coding-comment |
|
||||
|
|
||||
[REPORTS] |
|
||||
msg-template={path}:{line}: [{msg_id}({symbol}), {obj}] {msg} |
|
||||
output-format=colorized |
|
||||
reports=no |
|
@ -1,65 +0,0 @@ |
|||||
[MASTER] |
|
||||
load-plugins=pylint_odoo |
|
||||
score=n |
|
||||
|
|
||||
[ODOOLINT] |
|
||||
readme_template_url="https://github.com/OCA/maintainer-tools/blob/master/template/module/README.rst" |
|
||||
manifest_required_authors=IT Projects Labs |
|
||||
manifest_required_keys=license |
|
||||
manifest_deprecated_keys=description,active |
|
||||
license_allowed=AGPL-3,GPL-2,GPL-2 or any later version,GPL-3,GPL-3 or any later version,LGPL-3,MIT,Other OSI approved licence,OPL-1 |
|
||||
valid_odoo_versions=14.0 |
|
||||
|
|
||||
[MESSAGES CONTROL] |
|
||||
disable=all |
|
||||
|
|
||||
enable=anomalous-backslash-in-string, |
|
||||
api-one-deprecated, |
|
||||
api-one-multi-together, |
|
||||
assignment-from-none, |
|
||||
attribute-deprecated, |
|
||||
class-camelcase, |
|
||||
dangerous-default-value, |
|
||||
dangerous-view-replace-wo-priority, |
|
||||
duplicate-id-csv, |
|
||||
duplicate-key, |
|
||||
duplicate-xml-fields, |
|
||||
duplicate-xml-record-id, |
|
||||
eval-referenced, |
|
||||
eval-used, |
|
||||
incoherent-interpreter-exec-perm, |
|
||||
license-allowed, |
|
||||
manifest-author-string, |
|
||||
manifest-deprecated-key, |
|
||||
#manifest-required-author, |
|
||||
manifest-required-key, |
|
||||
manifest-version-format, |
|
||||
method-compute, |
|
||||
method-inverse, |
|
||||
method-required-super, |
|
||||
method-search, |
|
||||
missing-import-error, |
|
||||
missing-manifest-dependency, |
|
||||
openerp-exception-warning, |
|
||||
pointless-statement, |
|
||||
pointless-string-statement, |
|
||||
print-used, |
|
||||
redundant-keyword-arg, |
|
||||
redundant-modulename-xml, |
|
||||
reimported, |
|
||||
relative-import, |
|
||||
return-in-init, |
|
||||
rst-syntax-error, |
|
||||
sql-injection, |
|
||||
too-few-format-args, |
|
||||
translation-field, |
|
||||
translation-required, |
|
||||
unreachable, |
|
||||
use-vim-comment, |
|
||||
wrong-tabs-instead-of-spaces, |
|
||||
xml-syntax-error |
|
||||
|
|
||||
[REPORTS] |
|
||||
msg-template={path}:{line}: [{msg_id}({symbol}), {obj}] {msg} |
|
||||
output-format=colorized |
|
||||
reports=no |
|
@ -0,0 +1,45 @@ |
|||||
|
language: python |
||||
|
|
||||
|
python: |
||||
|
- "2.7" |
||||
|
|
||||
|
#dist: trusty |
||||
|
sudo: false |
||||
|
cache: pip |
||||
|
|
||||
|
addons: |
||||
|
apt: |
||||
|
packages: |
||||
|
- expect-dev # provides unbuffer utility |
||||
|
- python-lxml # because pip installation is slow |
||||
|
|
||||
|
env: |
||||
|
global: |
||||
|
- VERSION="9.0" TESTS="0" LINT_CHECK="0" TRANSIFEX="0" UNIT_TEST="0" |
||||
|
- EXCLUDE="hw_printer_network" |
||||
|
- PYLINT_ODOO_JSLINTRC="/home/travis/maintainer-quality-tools/travis/cfg/.jslintrc" |
||||
|
|
||||
|
|
||||
|
matrix: |
||||
|
- LINT_CHECK="1" |
||||
|
- TESTS="1" ODOO_REPO="odoo/odoo" |
||||
|
- TESTS="1" ODOO_REPO="OCA/OCB" |
||||
|
- TESTS="1" UNIT_TEST="1" |
||||
|
|
||||
|
virtualenv: |
||||
|
system_site_packages: true |
||||
|
|
||||
|
install: |
||||
|
- pip install anybox.testing.openerp |
||||
|
- git clone https://github.com/it-projects-llc/maintainer-quality-tools.git ${HOME}/maintainer-quality-tools |
||||
|
- export PATH=${HOME}/maintainer-quality-tools/travis:${PATH} |
||||
|
- travis_install_nightly |
||||
|
|
||||
|
script: |
||||
|
- travis_run_tests |
||||
|
|
||||
|
after_success: |
||||
|
coveralls |
||||
|
|
||||
|
notifications: |
||||
|
email: false |
@ -0,0 +1,166 @@ |
|||||
|
GNU LESSER GENERAL PUBLIC LICENSE |
||||
|
Version 3, 29 June 2007 |
||||
|
|
||||
|
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> |
||||
|
Everyone is permitted to copy and distribute verbatim copies |
||||
|
of this license document, but changing it is not allowed. |
||||
|
|
||||
|
|
||||
|
This version of the GNU Lesser General Public License incorporates |
||||
|
the terms and conditions of version 3 of the GNU General Public |
||||
|
License, supplemented by the additional permissions listed below. |
||||
|
|
||||
|
0. Additional Definitions. |
||||
|
|
||||
|
As used herein, "this License" refers to version 3 of the GNU Lesser |
||||
|
General Public License, and the "GNU GPL" refers to version 3 of the GNU |
||||
|
General Public License. |
||||
|
|
||||
|
"The Library" refers to a covered work governed by this License, |
||||
|
other than an Application or a Combined Work as defined below. |
||||
|
|
||||
|
An "Application" is any work that makes use of an interface provided |
||||
|
by the Library, but which is not otherwise based on the Library. |
||||
|
Defining a subclass of a class defined by the Library is deemed a mode |
||||
|
of using an interface provided by the Library. |
||||
|
|
||||
|
A "Combined Work" is a work produced by combining or linking an |
||||
|
Application with the Library. The particular version of the Library |
||||
|
with which the Combined Work was made is also called the "Linked |
||||
|
Version". |
||||
|
|
||||
|
The "Minimal Corresponding Source" for a Combined Work means the |
||||
|
Corresponding Source for the Combined Work, excluding any source code |
||||
|
for portions of the Combined Work that, considered in isolation, are |
||||
|
based on the Application, and not on the Linked Version. |
||||
|
|
||||
|
The "Corresponding Application Code" for a Combined Work means the |
||||
|
object code and/or source code for the Application, including any data |
||||
|
and utility programs needed for reproducing the Combined Work from the |
||||
|
Application, but excluding the System Libraries of the Combined Work. |
||||
|
|
||||
|
1. Exception to Section 3 of the GNU GPL. |
||||
|
|
||||
|
You may convey a covered work under sections 3 and 4 of this License |
||||
|
without being bound by section 3 of the GNU GPL. |
||||
|
|
||||
|
2. Conveying Modified Versions. |
||||
|
|
||||
|
If you modify a copy of the Library, and, in your modifications, a |
||||
|
facility refers to a function or data to be supplied by an Application |
||||
|
that uses the facility (other than as an argument passed when the |
||||
|
facility is invoked), then you may convey a copy of the modified |
||||
|
version: |
||||
|
|
||||
|
a) under this License, provided that you make a good faith effort to |
||||
|
ensure that, in the event an Application does not supply the |
||||
|
function or data, the facility still operates, and performs |
||||
|
whatever part of its purpose remains meaningful, or |
||||
|
|
||||
|
b) under the GNU GPL, with none of the additional permissions of |
||||
|
this License applicable to that copy. |
||||
|
|
||||
|
3. Object Code Incorporating Material from Library Header Files. |
||||
|
|
||||
|
The object code form of an Application may incorporate material from |
||||
|
a header file that is part of the Library. You may convey such object |
||||
|
code under terms of your choice, provided that, if the incorporated |
||||
|
material is not limited to numerical parameters, data structure |
||||
|
layouts and accessors, or small macros, inline functions and templates |
||||
|
(ten or fewer lines in length), you do both of the following: |
||||
|
|
||||
|
a) Give prominent notice with each copy of the object code that the |
||||
|
Library is used in it and that the Library and its use are |
||||
|
covered by this License. |
||||
|
|
||||
|
b) Accompany the object code with a copy of the GNU GPL and this license |
||||
|
document. |
||||
|
|
||||
|
4. Combined Works. |
||||
|
|
||||
|
You may convey a Combined Work under terms of your choice that, |
||||
|
taken together, effectively do not restrict modification of the |
||||
|
portions of the Library contained in the Combined Work and reverse |
||||
|
engineering for debugging such modifications, if you also do each of |
||||
|
the following: |
||||
|
|
||||
|
a) Give prominent notice with each copy of the Combined Work that |
||||
|
the Library is used in it and that the Library and its use are |
||||
|
covered by this License. |
||||
|
|
||||
|
b) Accompany the Combined Work with a copy of the GNU GPL and this license |
||||
|
document. |
||||
|
|
||||
|
c) For a Combined Work that displays copyright notices during |
||||
|
execution, include the copyright notice for the Library among |
||||
|
these notices, as well as a reference directing the user to the |
||||
|
copies of the GNU GPL and this license document. |
||||
|
|
||||
|
d) Do one of the following: |
||||
|
|
||||
|
0) Convey the Minimal Corresponding Source under the terms of this |
||||
|
License, and the Corresponding Application Code in a form |
||||
|
suitable for, and under terms that permit, the user to |
||||
|
recombine or relink the Application with a modified version of |
||||
|
the Linked Version to produce a modified Combined Work, in the |
||||
|
manner specified by section 6 of the GNU GPL for conveying |
||||
|
Corresponding Source. |
||||
|
|
||||
|
1) Use a suitable shared library mechanism for linking with the |
||||
|
Library. A suitable mechanism is one that (a) uses at run time |
||||
|
a copy of the Library already present on the user's computer |
||||
|
system, and (b) will operate properly with a modified version |
||||
|
of the Library that is interface-compatible with the Linked |
||||
|
Version. |
||||
|
|
||||
|
e) Provide Installation Information, but only if you would otherwise |
||||
|
be required to provide such information under section 6 of the |
||||
|
GNU GPL, and only to the extent that such information is |
||||
|
necessary to install and execute a modified version of the |
||||
|
Combined Work produced by recombining or relinking the |
||||
|
Application with a modified version of the Linked Version. (If |
||||
|
you use option 4d0, the Installation Information must accompany |
||||
|
the Minimal Corresponding Source and Corresponding Application |
||||
|
Code. If you use option 4d1, you must provide the Installation |
||||
|
Information in the manner specified by section 6 of the GNU GPL |
||||
|
for conveying Corresponding Source.) |
||||
|
|
||||
|
5. Combined Libraries. |
||||
|
|
||||
|
You may place library facilities that are a work based on the |
||||
|
Library side by side in a single library together with other library |
||||
|
facilities that are not Applications and are not covered by this |
||||
|
License, and convey such a combined library under terms of your |
||||
|
choice, if you do both of the following: |
||||
|
|
||||
|
a) Accompany the combined library with a copy of the same work based |
||||
|
on the Library, uncombined with any other library facilities, |
||||
|
conveyed under the terms of this License. |
||||
|
|
||||
|
b) Give prominent notice with the combined library that part of it |
||||
|
is a work based on the Library, and explaining where to find the |
||||
|
accompanying uncombined form of the same work. |
||||
|
|
||||
|
6. Revised Versions of the GNU Lesser General Public License. |
||||
|
|
||||
|
The Free Software Foundation may publish revised and/or new versions |
||||
|
of the GNU Lesser General Public License from time to time. Such new |
||||
|
versions will be similar in spirit to the present version, but may |
||||
|
differ in detail to address new problems or concerns. |
||||
|
|
||||
|
Each version is given a distinguishing version number. If the |
||||
|
Library as you received it specifies that a certain numbered version |
||||
|
of the GNU Lesser General Public License "or any later version" |
||||
|
applies to it, you have the option of following the terms and |
||||
|
conditions either of that published version or of any later version |
||||
|
published by the Free Software Foundation. If the Library as you |
||||
|
received it does not specify a version number of the GNU Lesser |
||||
|
General Public License, you may choose any version of the GNU Lesser |
||||
|
General Public License ever published by the Free Software Foundation. |
||||
|
|
||||
|
If the Library as you received it specifies that a proxy can decide |
||||
|
whether future versions of the GNU Lesser General Public License shall |
||||
|
apply, that proxy's public statement of acceptance of any version is |
||||
|
permanent authorization for you to choose that version for the |
||||
|
Library. |
||||
|
|
@ -0,0 +1,17 @@ |
|||||
|
.. image:: https://itpp.dev/images/infinity-readme.png |
||||
|
:alt: Tested and maintained by IT Projects Labs |
||||
|
:target: https://itpp.dev |
||||
|
|
||||
|
=================== |
||||
|
Show all messages |
||||
|
=================== |
||||
|
|
||||
|
Adds ``Discuss / All`` menu, that shows all messages accesable by current user |
||||
|
|
||||
|
Further information |
||||
|
------------------- |
||||
|
|
||||
|
Odoo Apps Store: https://apps.odoo.com/apps/modules/9.0/mail_all/ |
||||
|
|
||||
|
|
||||
|
Tested on `Odoo 9.0 <https://github.com/odoo/odoo/commit/d3dd4161ad0598ebaa659fbd083457c77aa9448d>`_ |
@ -0,0 +1,3 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
|
||||
|
from . import models |
@ -0,0 +1,26 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
{ |
||||
|
"name": "Show all messages", |
||||
|
"summary": """Checkout all messages where you have access""", |
||||
|
"category": "Discuss", |
||||
|
"images": ['images/1.jpg'], |
||||
|
"version": "1.0.0", |
||||
|
|
||||
|
"author": "IT-Projects LLC, Pavel Romanchenko", |
||||
|
"website": "https://twitter.com/OdooFree", |
||||
|
"license": "LGPL-3", |
||||
|
|
||||
|
"depends": [ |
||||
|
"mail_base" |
||||
|
], |
||||
|
"external_dependencies": {"python": [], "bin": []}, |
||||
|
"data": [ |
||||
|
"views/templates.xml", |
||||
|
], |
||||
|
"qweb": [ |
||||
|
"static/src/xml/menu.xml", |
||||
|
], |
||||
|
"demo": [], |
||||
|
'installable': True, |
||||
|
"auto_install": False, |
||||
|
} |
@ -0,0 +1,7 @@ |
|||||
|
Updates |
||||
|
======= |
||||
|
|
||||
|
`1.0.0` |
||||
|
------- |
||||
|
|
||||
|
- Init version |
@ -0,0 +1,9 @@ |
|||||
|
=================== |
||||
|
Show all messages |
||||
|
=================== |
||||
|
|
||||
|
Usage |
||||
|
===== |
||||
|
|
||||
|
* Open menu ``Discuss / All messages`` |
||||
|
* You see all messages |
After Width: 334 | Height: 171 | Size: 11 KiB |
@ -0,0 +1 @@ |
|||||
|
# -*- coding: utf-8 -*- |
After Width: 300 | Height: 270 | Size: 15 KiB |
After Width: 765 | Height: 400 | Size: 22 KiB |
After Width: 100 | Height: 100 | Size: 2.1 KiB |
@ -0,0 +1,45 @@ |
|||||
|
<section class="oe_container"> |
||||
|
<div class="oe_row oe_spaced"> |
||||
|
<div class="oe_span12"> |
||||
|
<h2 class="oe_slogan">Show all messages</h2> |
||||
|
<h3 class="oe_slogan">Checkout all messages where you have access</h3> |
||||
|
</div> |
||||
|
<div class="oe_span6"> |
||||
|
<div class="oe_row_img oe_centered"> |
||||
|
<img class="oe_picture oe_screenshot" src="1.png"/> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="oe_span6"> |
||||
|
<p class="oe_mt32"> |
||||
|
The module adds usual menu. |
||||
|
</p> |
||||
|
</div> |
||||
|
</div> |
||||
|
</section> |
||||
|
|
||||
|
<section class="oe_container oe_dark"> |
||||
|
<div class="oe_row oe_spaced"> |
||||
|
<div class="oe_span12"> |
||||
|
<p class="oe_mt32"> |
||||
|
This menu shows all messages. |
||||
|
</p> |
||||
|
</div> |
||||
|
<div class="oe_row_img oe_centered"> |
||||
|
<img class="oe_picture oe_screenshot" src="2.png"/> |
||||
|
</div> |
||||
|
</div> |
||||
|
</section> |
||||
|
|
||||
|
<section class="oe_container"> |
||||
|
<div class="oe_row oe_spaced"> |
||||
|
<div class="oe_span12"> |
||||
|
<h2>Need our service?</h2> |
||||
|
<p class="oe_mt32">Contact us by <a href="mailto:it@it-projects.info">email</a> or fill out <a href="https://www.it-projects.info/page/website.contactus " target="_blank">request form</a></p> |
||||
|
<ul> |
||||
|
<li><a href="mailto:it@it-projects.info">it@it-projects.info <i class="fa fa-envelope-o"></i></a></li> |
||||
|
<li><a href="https://www.it-projects.info/page/website.contactus " target="_blank"> |
||||
|
https://www.it-projects.info/page/website.contactus <i class="fa fa-list-alt"></i></a></li> |
||||
|
</ul> |
||||
|
</div> |
||||
|
</div> |
||||
|
</section> |
@ -0,0 +1,65 @@ |
|||||
|
odoo.define('mail_all.all', function (require) { |
||||
|
"use strict"; |
||||
|
|
||||
|
var base_obj = require('mail_base.base'); |
||||
|
|
||||
|
//-------------------------------------------------------------------------------
|
||||
|
var bus = require('bus.bus').bus; |
||||
|
var config = require('web.config'); |
||||
|
var core = require('web.core'); |
||||
|
var data = require('web.data'); |
||||
|
var Model = require('web.Model'); |
||||
|
var session = require('web.session'); |
||||
|
var time = require('web.time'); |
||||
|
var web_client = require('web.web_client'); |
||||
|
|
||||
|
var _lt = core._lt; |
||||
|
//-------------------------------------------------------------------------------
|
||||
|
|
||||
|
var ChatAction = core.action_registry.get('mail.chat.instant_messaging'); |
||||
|
ChatAction.include({ |
||||
|
get_thread_rendering_options: function (messages) { |
||||
|
var options = this._super.apply(this, arguments); |
||||
|
options.display_subject = options.display_subject || this.channel.id === "channel_all"; |
||||
|
return options; |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
// Inherit class and override methods
|
||||
|
base_obj.MailTools.include({ |
||||
|
get_properties: function(msg){ |
||||
|
var properties = this._super.apply(this, arguments); |
||||
|
properties.is_all = this.property_descr("channel_all", msg, this); |
||||
|
return properties; |
||||
|
}, |
||||
|
|
||||
|
set_channel_flags: function(data, msg){ |
||||
|
this._super.apply(this, arguments); |
||||
|
msg.is_all = true; |
||||
|
return msg; |
||||
|
}, |
||||
|
|
||||
|
get_channel_array: function(msg){ |
||||
|
var arr = this._super.apply(this, arguments); |
||||
|
return arr.concat('channel_all'); |
||||
|
}, |
||||
|
|
||||
|
get_domain: function(channel){ |
||||
|
return (channel.id === "channel_all") ? [] : this._super.apply(this, arguments); |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
base_obj.chat_manager.is_ready.then(function(){ |
||||
|
// Add all channel
|
||||
|
base_obj.chat_manager.mail_tools.add_channel({ |
||||
|
id: "channel_all", |
||||
|
name: _lt("All messages"), |
||||
|
type: "static" |
||||
|
}); |
||||
|
|
||||
|
return $.when(); |
||||
|
}); |
||||
|
|
||||
|
return base_obj.chat_manager; |
||||
|
|
||||
|
}); |
@ -0,0 +1,19 @@ |
|||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||
|
<template> |
||||
|
<!--Inherit Sidebar and add All messages menu item after Starred --> |
||||
|
<t t-extend="mail.chat.Sidebar"> |
||||
|
<t t-jquery="div[data-channel-id=channel_starred]" t-operation="after"> |
||||
|
<div t-attf-class="o_mail_chat_channel_item #{(active_channel_id == 'channel_all') ? 'o_active': ''}" data-channel-id="channel_all"> |
||||
|
<span class="o_channel_name mail_all"> <i class="fa fa-database"/> All messages </span> |
||||
|
</div> |
||||
|
</t> |
||||
|
</t> |
||||
|
<!--Add message about empty all messages page--> |
||||
|
<t t-extend="mail.EmptyChannel"> |
||||
|
<t t-jquery="t:last-child" t-operation="after"> |
||||
|
<t t-if="options.channel_id==='channel_all'"> |
||||
|
<div class="o_thread_title">No messages</div> |
||||
|
</t> |
||||
|
</t> |
||||
|
</t> |
||||
|
</template> |
@ -0,0 +1,3 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
|
||||
|
from . import test_js |
@ -0,0 +1,18 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
import openerp.tests |
||||
|
|
||||
|
|
||||
|
@openerp.tests.common.at_install(False) |
||||
|
@openerp.tests.common.post_install(True) |
||||
|
class TestUi(openerp.tests.HttpCase): |
||||
|
|
||||
|
def test_01_mail_all(self): |
||||
|
# wait till page loaded and then click and wait again |
||||
|
code = """ |
||||
|
setTimeout(function () { |
||||
|
$(".mail_all").click(); |
||||
|
setTimeout(function () {console.log('ok');}, 3000); |
||||
|
}, 1000); |
||||
|
""" |
||||
|
link = '/web#action=%s' % self.ref('mail.mail_channel_action_client_chat') |
||||
|
self.phantom_js(link, code, "odoo.__DEBUG__.services['mail_all.all']", login="admin") |
@ -0,0 +1,12 @@ |
|||||
|
<?xml version="1.0"?> |
||||
|
<openerp> |
||||
|
<data> |
||||
|
<template id="mail_all_assets_backend" |
||||
|
name="mail_all_assets_backend" |
||||
|
inherit_id="web.assets_backend"> |
||||
|
<xpath expr="." position="inside"> |
||||
|
<script src="/mail_all/static/src/js/mail_all.js" type="text/javascript"></script> |
||||
|
</xpath> |
||||
|
</template> |
||||
|
</data> |
||||
|
</openerp> |
@ -0,0 +1,19 @@ |
|||||
|
.. image:: https://itpp.dev/images/infinity-readme.png |
||||
|
:alt: Tested and maintained by IT Projects Labs |
||||
|
:target: https://itpp.dev |
||||
|
|
||||
|
Mail Archives |
||||
|
============= |
||||
|
|
||||
|
Adds Archive menu, which shows sent/received messages |
||||
|
|
||||
|
Usage |
||||
|
----- |
||||
|
Click Discuss/Archive menu -- sent/received messages are displayed |
||||
|
|
||||
|
Further information |
||||
|
------------------- |
||||
|
|
||||
|
Odoo Apps Store: https://apps.odoo.com/apps/modules/9.0/mail_archives/ |
||||
|
|
||||
|
Tested on `Odoo 9.0 <https://github.com/odoo/odoo/commit/b9f206953e3f877adf18643f154d1262842564ee>`_ |
@ -0,0 +1 @@ |
|||||
|
# -*- coding: utf-8 -*- |
@ -0,0 +1,24 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
{ |
||||
|
"name": "Mail archives", |
||||
|
"summary": """Adds menu to find old messages""", |
||||
|
"category": "Discuss", |
||||
|
"images": ['images/1.jpg'], |
||||
|
"version": "1.0.0", |
||||
|
|
||||
|
"author": "IT-Projects LLC, Pavel Romanchenko", |
||||
|
"website": "https://twitter.com/OdooFree", |
||||
|
"license": "LGPL-3", |
||||
|
|
||||
|
"depends": [ |
||||
|
"mail_base", |
||||
|
], |
||||
|
|
||||
|
"data": [ |
||||
|
"views/templates.xml", |
||||
|
], |
||||
|
"qweb": [ |
||||
|
"static/src/xml/menu.xml", |
||||
|
], |
||||
|
'installable': True, |
||||
|
} |
After Width: 334 | Height: 171 | Size: 18 KiB |
After Width: 300 | Height: 270 | Size: 24 KiB |
After Width: 765 | Height: 400 | Size: 40 KiB |
After Width: 100 | Height: 100 | Size: 2.1 KiB |
@ -0,0 +1,82 @@ |
|||||
|
<section class="oe_container"> |
||||
|
<div class="oe_row oe_spaced"> |
||||
|
<div class="oe_span12"> |
||||
|
<h2 class="oe_slogan">Look up old mails</h2> |
||||
|
<h3 class="oe_slogan">Browse archived mails like in odoo 8</h3> |
||||
|
</div> |
||||
|
<div class="oe_span6"> |
||||
|
<div class="oe_row_img oe_centered"> |
||||
|
<img class="oe_picture oe_screenshot" src="1.png"/> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="oe_span6"> |
||||
|
<p class="oe_mt32"> |
||||
|
The module adds usual menu. |
||||
|
</p> |
||||
|
</div> |
||||
|
</div> |
||||
|
</section> |
||||
|
|
||||
|
<section class="oe_container oe_dark"> |
||||
|
<div class="oe_row oe_spaced"> |
||||
|
<div class="oe_span12"> |
||||
|
<p class="oe_mt32"> |
||||
|
This menu shows archive messages, i.e. ones you sent or received. |
||||
|
</p> |
||||
|
</div> |
||||
|
<div class="oe_row_img oe_centered"> |
||||
|
<img class="oe_picture oe_screenshot" src="2.png"/> |
||||
|
</div> |
||||
|
</div> |
||||
|
</section> |
||||
|
|
||||
|
<section class="oe_container"> |
||||
|
<div class="oe_row oe_spaced"> |
||||
|
<div class="oe_span8"> |
||||
|
<h2>Need our service?</h2> |
||||
|
<p class="oe_mt32">Contact us by <a href="mailto:it@it-projects.info">email</a> or fill out <a href="https://www.it-projects.info/page/website.contactus " target="_blank">request form</a></p> |
||||
|
<ul> |
||||
|
<li><a href="mailto:it@it-projects.info">it@it-projects.info <i class="fa fa-envelope-o"></i></a></li> |
||||
|
<li><a href="https://www.it-projects.info/page/website.contactus " target="_blank">https://www.it-projects.info/page/website.contactus <i class="fa fa-list-alt"></i></a></li> |
||||
|
</ul> |
||||
|
</div> |
||||
|
<div class="oe_span4"> |
||||
|
<div class="stamp" style="width:200px;"> |
||||
|
<div style="margin-top: 15px; |
||||
|
position: relative; |
||||
|
font-family:'Vollkorn', serif; |
||||
|
font-size: 16px; |
||||
|
line-height: 25px; |
||||
|
text-transform: uppercase; |
||||
|
font-weight: bold; |
||||
|
color: #75526b; |
||||
|
border: 3px dashed #75526b; |
||||
|
float: left; |
||||
|
padding: 4px 12px; |
||||
|
-webkit-transform: rotate(6deg); |
||||
|
-o-transform: rotate(6deg); |
||||
|
-moz-transform: rotate(6deg); |
||||
|
-ms-transform: rotate(6deg);"> |
||||
|
Tested on Odoo<br/>9.0 community |
||||
|
</div> |
||||
|
<div style="margin-top: 15px; |
||||
|
position: relative; |
||||
|
font-family:'Vollkorn', serif; |
||||
|
font-size: 16px; |
||||
|
line-height: 25px; |
||||
|
text-transform: uppercase; |
||||
|
font-weight: bold; |
||||
|
color: #75526b; |
||||
|
border: 3px dashed #75526b; |
||||
|
float: left; |
||||
|
padding: 4px 12px; |
||||
|
-webkit-transform: rotate(-7deg); |
||||
|
-o-transform: rotate(-7deg); |
||||
|
-moz-transform: rotate(-7deg); |
||||
|
-ms-transform: rotate(-7deg);"> |
||||
|
Tested on Odoo<br/>9.0 enterprise |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</section> |
@ -0,0 +1,85 @@ |
|||||
|
odoo.define('mail_archives.archives', function (require) { |
||||
|
"use strict"; |
||||
|
|
||||
|
var base_obj = require('mail_base.base'); |
||||
|
|
||||
|
//-------------------------------------------------------------------------------
|
||||
|
var bus = require('bus.bus').bus; |
||||
|
var config = require('web.config'); |
||||
|
var core = require('web.core'); |
||||
|
var data = require('web.data'); |
||||
|
var Model = require('web.Model'); |
||||
|
var session = require('web.session'); |
||||
|
var time = require('web.time'); |
||||
|
var web_client = require('web.web_client'); |
||||
|
|
||||
|
var _lt = core._lt; |
||||
|
//-------------------------------------------------------------------------------
|
||||
|
|
||||
|
var ChatAction = core.action_registry.get('mail.chat.instant_messaging'); |
||||
|
ChatAction.include({ |
||||
|
init: function(parent, action, options) { |
||||
|
this._super.apply(this, arguments); |
||||
|
var channel_name = 'channel_archive'; |
||||
|
// Add channel Archive for enable "display_subject" option
|
||||
|
this.channels_display_subject.push(channel_name); |
||||
|
}, |
||||
|
|
||||
|
update_message_on_current_channel: function(current_channel_id, message){ |
||||
|
var result = this._super.apply(this, arguments); |
||||
|
var archive = current_channel_id === "channel_archive" && !message.is_archive; |
||||
|
return archive || result; |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
// Inherit class and override methods
|
||||
|
base_obj.MailTools.include({ |
||||
|
get_properties: function(msg){ |
||||
|
var properties = this._super.apply(this, arguments); |
||||
|
properties.is_archive = this.property_descr("channel_archive", msg, this); |
||||
|
return properties; |
||||
|
}, |
||||
|
|
||||
|
set_channel_flags: function(data, msg){ |
||||
|
this._super.apply(this, arguments); |
||||
|
// Get recipients ids
|
||||
|
var recipients_ids = []; |
||||
|
for (var i = 0; i < data.partner_ids.length; i++){ |
||||
|
recipients_ids.push(data.partner_ids[i][0]); |
||||
|
} |
||||
|
|
||||
|
// If author or recipient
|
||||
|
if (data.author_id[0] == session.partner_id || recipients_ids.indexOf(session.partner_id) != -1) { |
||||
|
msg.is_archive = true; |
||||
|
} |
||||
|
|
||||
|
return msg; |
||||
|
}, |
||||
|
|
||||
|
get_channel_array: function(msg){ |
||||
|
var arr = this._super.apply(this, arguments); |
||||
|
return arr.concat('channel_archive'); |
||||
|
}, |
||||
|
|
||||
|
get_domain: function(channel){ |
||||
|
return (channel.id === "channel_archive") ? [ |
||||
|
'|', ['partner_ids', 'in', [openerp.session.partner_id]], |
||||
|
['author_id.user_ids', 'in', [openerp.session.uid]] |
||||
|
] : this._super.apply(this, arguments); |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
base_obj.chat_manager.is_ready.then(function(){ |
||||
|
// Add archive channel
|
||||
|
base_obj.chat_manager.mail_tools.add_channel({ |
||||
|
id: "channel_archive", |
||||
|
name: _lt("Archive"), |
||||
|
type: "static" |
||||
|
}); |
||||
|
|
||||
|
return $.when(); |
||||
|
}); |
||||
|
|
||||
|
return base_obj.chat_manager; |
||||
|
|
||||
|
}); |
@ -0,0 +1,19 @@ |
|||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||
|
<template> |
||||
|
<!--Inherit Sidebar and add Archive menu item after Starred --> |
||||
|
<t t-extend="mail.chat.Sidebar"> |
||||
|
<t t-jquery="div[data-channel-id=channel_starred]" t-operation="after"> |
||||
|
<div t-attf-class="o_mail_chat_channel_item #{(active_channel_id == 'channel_archive') ? 'o_active': ''}" data-channel-id="channel_archive"> |
||||
|
<span class="o_channel_name mail_archives"> <i class="fa fa-archive"/> Archive </span> |
||||
|
</div> |
||||
|
</t> |
||||
|
</t> |
||||
|
<!--Add message about empty archive page--> |
||||
|
<t t-extend="mail.EmptyChannel"> |
||||
|
<t t-jquery="t:last-child" t-operation="after"> |
||||
|
<t t-if="options.channel_id==='channel_archive'"> |
||||
|
<div class="o_thread_title">Archive is empty</div> |
||||
|
</t> |
||||
|
</t> |
||||
|
</t> |
||||
|
</template> |
@ -0,0 +1,3 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
|
||||
|
from . import test_js |
@ -0,0 +1,18 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
import openerp.tests |
||||
|
|
||||
|
|
||||
|
@openerp.tests.common.at_install(False) |
||||
|
@openerp.tests.common.post_install(True) |
||||
|
class TestUi(openerp.tests.HttpCase): |
||||
|
|
||||
|
def test_01_mail_archives(self): |
||||
|
# wait till page loaded and then click and wait again |
||||
|
code = """ |
||||
|
setTimeout(function () { |
||||
|
$(".mail_archives").click(); |
||||
|
setTimeout(function () {console.log('ok');}, 3000); |
||||
|
}, 1000); |
||||
|
""" |
||||
|
link = '/web#action=%s' % self.ref('mail.mail_channel_action_client_chat') |
||||
|
self.phantom_js(link, code, "odoo.__DEBUG__.services['mail_archives.archives']", login="admin") |
@ -0,0 +1,12 @@ |
|||||
|
<?xml version="1.0"?> |
||||
|
<openerp> |
||||
|
<data> |
||||
|
<template id="res_partner_mails_count_assets_backend" |
||||
|
name="res_partner_mails_count_assets_backend" |
||||
|
inherit_id="web.assets_backend"> |
||||
|
<xpath expr="." position="inside"> |
||||
|
<script src="/mail_archives/static/src/js/archives.js" type="text/javascript"></script> |
||||
|
</xpath> |
||||
|
</template> |
||||
|
</data> |
||||
|
</openerp> |
@ -0,0 +1,26 @@ |
|||||
|
.. image:: https://itpp.dev/images/infinity-readme.png |
||||
|
:alt: Tested and maintained by IT Projects Labs |
||||
|
:target: https://itpp.dev |
||||
|
|
||||
|
=================== |
||||
|
Popup Attachments |
||||
|
=================== |
||||
|
|
||||
|
The module opens attached mail images in popup. |
||||
|
|
||||
|
Questions? |
||||
|
========== |
||||
|
|
||||
|
To get an assistance on this module contact us by email :arrow_right: help@itpp.dev |
||||
|
|
||||
|
Contributors |
||||
|
============ |
||||
|
* Dinar Gabbasov <gabbasov@it-projects.info> |
||||
|
|
||||
|
Further information |
||||
|
=================== |
||||
|
|
||||
|
Odoo Apps Store: https://apps.odoo.com/apps/modules/9.0/mail_attachment_popup/ |
||||
|
|
||||
|
|
||||
|
Tested on `Odoo 9.0 <https://github.com/odoo/odoo/commit/021878f9c41c6d652abf345c3c5537fe92f8bc5b>`_ |
@ -0,0 +1,26 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
{ |
||||
|
"name": """Popup Attachments""", |
||||
|
"summary": """Open attached mail images in popup""", |
||||
|
"category": "Extra Tools", |
||||
|
"version": "1.0.0", |
||||
|
"images": ['images/popup_image.png'], |
||||
|
|
||||
|
"author": "IT-Projects LLC, Dinar Gabbasov", |
||||
|
'website': "https://twitter.com/gabbasov_dinar", |
||||
|
"license": "GPL-3", |
||||
|
|
||||
|
"depends": [ |
||||
|
"mail", |
||||
|
], |
||||
|
"external_dependencies": {"python": [], "bin": []}, |
||||
|
"data": [ |
||||
|
"views/mail_attachment_popup_template.xml", |
||||
|
], |
||||
|
"qweb": [ |
||||
|
"static/src/xml/mail_attachment_popup.xml", |
||||
|
], |
||||
|
|
||||
|
"installable": True, |
||||
|
'auto_install': False, |
||||
|
} |
@ -0,0 +1,4 @@ |
|||||
|
`1.0.0` |
||||
|
------- |
||||
|
|
||||
|
- Init version |
@ -0,0 +1,16 @@ |
|||||
|
=================== |
||||
|
Popup Attachments |
||||
|
=================== |
||||
|
|
||||
|
Installation |
||||
|
============ |
||||
|
|
||||
|
* `Install <https://odoo-development.readthedocs.io/en/latest/odoo/usage/install-module.html>`__ this module in a usual way |
||||
|
|
||||
|
Usage |
||||
|
===== |
||||
|
|
||||
|
* Open 'Messaging' menu |
||||
|
* Find any message with image in attachments |
||||
|
* Click on the image |
||||
|
* Browser opens image in popup instead of downloading it |
After Width: 749 | Height: 371 | Size: 128 KiB |
After Width: 361 | Height: 382 | Size: 63 KiB |
After Width: 361 | Height: 381 | Size: 131 KiB |
After Width: 100 | Height: 100 | Size: 2.1 KiB |
@ -0,0 +1,84 @@ |
|||||
|
<section class="oe_container"> |
||||
|
<div class="oe_row oe_spaced"> |
||||
|
<div class="oe_span12"> |
||||
|
<h2 class="oe_slogan">Popup Attachments</h2> |
||||
|
<h3 class="oe_slogan">Open attachments in popup</h3> |
||||
|
</div> |
||||
|
</div> |
||||
|
</section> |
||||
|
|
||||
|
<section class="oe_container"> |
||||
|
<div class="oe_row oe_spaced"> |
||||
|
<div class="oe_span12"> |
||||
|
<p class="oe_mt32"> |
||||
|
The module allows to open attachments (images) in popup. It is convenient if you want to display them only without downloading. |
||||
|
</p> |
||||
|
</div> |
||||
|
</div> |
||||
|
</section> |
||||
|
|
||||
|
<section class="oe_container oe_dark"> |
||||
|
<div class="oe_row oe_spaced"> |
||||
|
<div class="oe_span12"> |
||||
|
<h3 class="oe_slogan">How it works</h3> |
||||
|
</div> |
||||
|
</div> |
||||
|
</section> |
||||
|
|
||||
|
<section class="oe_container"> |
||||
|
<div class="oe_row oe_spaced"> |
||||
|
<div class="oe_span6"> |
||||
|
<p class="oe_mt32"> |
||||
|
Go to "Messaging" menu and open email that contains image(s) in attachment. |
||||
|
</p> |
||||
|
</div> |
||||
|
<div class="oe_span6"> |
||||
|
<div class="oe_row_img oe_centered"> |
||||
|
<img class="oe_demo oe_picture oe_screenshot" src="attach_image.png"/> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</section> |
||||
|
|
||||
|
<section class="oe_container oe_dark"> |
||||
|
<div class="oe_row oe_spaced"> |
||||
|
<div class="oe_span6"> |
||||
|
<div class="oe_row_img oe_centered"> |
||||
|
<img class="oe_demo oe_picture oe_screenshot" src="popup.png"/> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="oe_span6"> |
||||
|
<p class="oe_mt32"> |
||||
|
Click on the image and see how popup is appear. |
||||
|
</p> |
||||
|
</div> |
||||
|
</div> |
||||
|
</section> |
||||
|
|
||||
|
<section class="oe_container"> |
||||
|
<div class="oe_row oe_spaced"> |
||||
|
<div class="oe_span6"> |
||||
|
<p class="oe_mt32"> |
||||
|
Moreover, you can download it to your device by clicking on the "Download" button if needed. |
||||
|
</p> |
||||
|
</div> |
||||
|
<div class="oe_span6"> |
||||
|
<div class="oe_row_img oe_centered"> |
||||
|
<img class="oe_demo oe_picture oe_screenshot" src="download.png"/> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</section> |
||||
|
|
||||
|
<section class="oe_container"> |
||||
|
<div class="oe_row oe_spaced"> |
||||
|
<div class="oe_span12"> |
||||
|
<h2>Need our service?</h2> |
||||
|
<p class="oe_mt32">Contact us by <a href="mailto:it@it-projects.info">email</a> or fill out <a href="https://www.it-projects.info/page/website.contactus " target="_blank">request form</a></p> |
||||
|
<ul> |
||||
|
<li><a href="mailto:it@it-projects.info">it@it-projects.info <i class="fa fa-envelope-o"></i></a></li> |
||||
|
<li><a href="https://www.it-projects.info/page/website.contactus " target="_blank">https://www.it-projects.info/page/website.contactus <i class="fa fa-list-alt"></i></a></li> |
||||
|
</ul> |
||||
|
</div> |
||||
|
</div> |
||||
|
</section> |
After Width: 365 | Height: 382 | Size: 150 KiB |
@ -0,0 +1,429 @@ |
|||||
|
/* |
||||
|
|
||||
|
arcticModal — jQuery plugin |
||||
|
Version: 0.3 |
||||
|
Author: Sergey Predvoditelev (sergey.predvoditelev@gmail.com) |
||||
|
Company: Arctic Laboratory (http://arcticlab.ru/)
|
||||
|
|
||||
|
Docs & Examples: http://arcticlab.ru/arcticmodal/
|
||||
|
|
||||
|
*/ |
||||
|
(function($) { |
||||
|
|
||||
|
|
||||
|
var default_options = { |
||||
|
|
||||
|
type: 'html', // ajax или html
|
||||
|
content: '', |
||||
|
url: '', |
||||
|
ajax: {}, |
||||
|
ajax_request: null, |
||||
|
|
||||
|
closeOnEsc: true, |
||||
|
closeOnOverlayClick: true, |
||||
|
|
||||
|
clone: false, |
||||
|
|
||||
|
overlay: { |
||||
|
block: undefined, |
||||
|
tpl: '<div class="arcticmodal-overlay"></div>', |
||||
|
css: { |
||||
|
backgroundColor: '#000', |
||||
|
opacity: .6 |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
container: { |
||||
|
block: undefined, |
||||
|
tpl: '<div class="arcticmodal-container"><table class="arcticmodal-container_i"><tr><td class="arcticmodal-container_i2"></td></tr></table></div>' |
||||
|
}, |
||||
|
|
||||
|
wrap: undefined, |
||||
|
body: undefined, |
||||
|
|
||||
|
errors: { |
||||
|
tpl: '<div class="arcticmodal-error arcticmodal-close"></div>', |
||||
|
autoclose_delay: 2000, |
||||
|
ajax_unsuccessful_load: 'Error' |
||||
|
}, |
||||
|
|
||||
|
openEffect: { |
||||
|
type: 'fade', |
||||
|
speed: 400 |
||||
|
}, |
||||
|
closeEffect: { |
||||
|
type: 'fade', |
||||
|
speed: 400 |
||||
|
}, |
||||
|
|
||||
|
beforeOpen: $.noop, |
||||
|
afterOpen: $.noop, |
||||
|
beforeClose: $.noop, |
||||
|
afterClose: $.noop, |
||||
|
afterLoading: $.noop, |
||||
|
afterLoadingOnShow: $.noop, |
||||
|
errorLoading: $.noop |
||||
|
|
||||
|
}; |
||||
|
|
||||
|
|
||||
|
var modalID = 0; |
||||
|
var modals = $([]); |
||||
|
|
||||
|
|
||||
|
var utils = { |
||||
|
|
||||
|
|
||||
|
// Определяет произошло ли событие e вне блока block
|
||||
|
isEventOut: function(blocks, e) { |
||||
|
var r = true; |
||||
|
$(blocks).each(function() { |
||||
|
if ($(e.target).get(0)==$(this).get(0)) r = false; |
||||
|
if ($(e.target).closest('HTML', $(this).get(0)).length==0) r = false; |
||||
|
}); |
||||
|
return r; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
}; |
||||
|
|
||||
|
|
||||
|
var modal = { |
||||
|
|
||||
|
|
||||
|
// Возвращает элемент, которым был вызван плагин
|
||||
|
getParentEl: function(el) { |
||||
|
var r = $(el); |
||||
|
if (r.data('arcticmodal')) return r; |
||||
|
r = $(el).closest('.arcticmodal-container').data('arcticmodalParentEl'); |
||||
|
if (r) return r; |
||||
|
return false; |
||||
|
}, |
||||
|
|
||||
|
|
||||
|
// Переход
|
||||
|
transition: function(el, action, options, callback) { |
||||
|
callback = callback==undefined ? $.noop : callback; |
||||
|
switch (options.type) { |
||||
|
case 'fade': |
||||
|
action=='show' ? el.fadeIn(options.speed, callback) : el.fadeOut(options.speed, callback); |
||||
|
break; |
||||
|
case 'none': |
||||
|
action=='show' ? el.show() : el.hide(); |
||||
|
callback(); |
||||
|
break; |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
|
||||
|
// Подготвка содержимого окна
|
||||
|
prepare_body: function(D, $this) { |
||||
|
|
||||
|
// Обработчик закрытия
|
||||
|
$('.arcticmodal-close', D.body).unbind('click.arcticmodal').bind('click.arcticmodal', function() { |
||||
|
$this.arcticmodal('close'); |
||||
|
return false; |
||||
|
}); |
||||
|
|
||||
|
}, |
||||
|
|
||||
|
|
||||
|
// Инициализация элемента
|
||||
|
init_el: function($this, options) { |
||||
|
var D = $this.data('arcticmodal'); |
||||
|
if (D) return; |
||||
|
|
||||
|
D = options; |
||||
|
modalID++; |
||||
|
D.modalID = modalID; |
||||
|
|
||||
|
// Overlay
|
||||
|
D.overlay.block = $(D.overlay.tpl); |
||||
|
D.overlay.block.css(D.overlay.css); |
||||
|
|
||||
|
// Container
|
||||
|
D.container.block = $(D.container.tpl); |
||||
|
|
||||
|
// BODY
|
||||
|
D.body = $('.arcticmodal-container_i2', D.container.block); |
||||
|
if (options.clone) { |
||||
|
D.body.html($this.clone(true)); |
||||
|
} else { |
||||
|
$this.before('<div id="arcticmodalReserve' + D.modalID + '" style="display: none" />'); |
||||
|
D.body.html($this); |
||||
|
} |
||||
|
|
||||
|
// Подготовка содержимого
|
||||
|
modal.prepare_body(D, $this); |
||||
|
|
||||
|
// Закрытие при клике на overlay
|
||||
|
if (D.closeOnOverlayClick) |
||||
|
D.overlay.block.add(D.container.block).click(function(e) { |
||||
|
if (utils.isEventOut($('>*', D.body), e)) |
||||
|
$this.arcticmodal('close'); |
||||
|
}); |
||||
|
|
||||
|
// Запомним настройки
|
||||
|
D.container.block.data('arcticmodalParentEl', $this); |
||||
|
$this.data('arcticmodal', D); |
||||
|
modals = $.merge(modals, $this); |
||||
|
|
||||
|
// Показать
|
||||
|
$.proxy(actions.show, $this)(); |
||||
|
if (D.type=='html') return $this; |
||||
|
|
||||
|
// Ajax-загрузка
|
||||
|
if (D.ajax.beforeSend!=undefined) { |
||||
|
var fn_beforeSend = D.ajax.beforeSend; |
||||
|
delete D.ajax.beforeSend; |
||||
|
} |
||||
|
if (D.ajax.success!=undefined) { |
||||
|
var fn_success = D.ajax.success; |
||||
|
delete D.ajax.success; |
||||
|
} |
||||
|
if (D.ajax.error!=undefined) { |
||||
|
var fn_error = D.ajax.error; |
||||
|
delete D.ajax.error; |
||||
|
} |
||||
|
var o = $.extend(true, { |
||||
|
url: D.url, |
||||
|
beforeSend: function() { |
||||
|
if (fn_beforeSend==undefined) { |
||||
|
D.body.html('<div class="arcticmodal-loading" />'); |
||||
|
} else { |
||||
|
fn_beforeSend(D, $this); |
||||
|
} |
||||
|
}, |
||||
|
success: function(responce) { |
||||
|
|
||||
|
// Событие после загрузки до показа содержимого
|
||||
|
$this.trigger('afterLoading'); |
||||
|
D.afterLoading(D, $this, responce); |
||||
|
|
||||
|
if (fn_success==undefined) { |
||||
|
D.body.html(responce); |
||||
|
} else { |
||||
|
fn_success(D, $this, responce); |
||||
|
} |
||||
|
modal.prepare_body(D, $this); |
||||
|
|
||||
|
// Событие после загрузки после отображения содержимого
|
||||
|
$this.trigger('afterLoadingOnShow'); |
||||
|
D.afterLoadingOnShow(D, $this, responce); |
||||
|
|
||||
|
}, |
||||
|
error: function() { |
||||
|
|
||||
|
// Событие при ошибке загрузки
|
||||
|
$this.trigger('errorLoading'); |
||||
|
D.errorLoading(D, $this); |
||||
|
|
||||
|
if (fn_error==undefined) { |
||||
|
D.body.html(D.errors.tpl); |
||||
|
$('.arcticmodal-error', D.body).html(D.errors.ajax_unsuccessful_load); |
||||
|
$('.arcticmodal-close', D.body).click(function() { |
||||
|
$this.arcticmodal('close'); |
||||
|
return false; |
||||
|
}); |
||||
|
if (D.errors.autoclose_delay) |
||||
|
setTimeout(function() { |
||||
|
$this.arcticmodal('close'); |
||||
|
}, D.errors.autoclose_delay); |
||||
|
} else { |
||||
|
fn_error(D, $this); |
||||
|
} |
||||
|
} |
||||
|
}, D.ajax); |
||||
|
D.ajax_request = $.ajax(o); |
||||
|
|
||||
|
// Запомнить настройки
|
||||
|
$this.data('arcticmodal', D); |
||||
|
|
||||
|
}, |
||||
|
|
||||
|
|
||||
|
// Инициализация
|
||||
|
init: function(options) { |
||||
|
options = $.extend(true, {}, default_options, options); |
||||
|
if ($.isFunction(this)) { |
||||
|
if (options==undefined) { |
||||
|
$.error('jquery.arcticmodal: Uncorrect parameters'); |
||||
|
return; |
||||
|
} |
||||
|
if (options.type=='') { |
||||
|
$.error('jquery.arcticmodal: Don\'t set parameter "type"'); |
||||
|
return; |
||||
|
} |
||||
|
switch (options.type) { |
||||
|
case 'html': |
||||
|
if (options.content=='') { |
||||
|
$.error('jquery.arcticmodal: Don\'t set parameter "content"'); |
||||
|
return |
||||
|
} |
||||
|
var c = options.content; |
||||
|
options.content = ''; |
||||
|
|
||||
|
return modal.init_el($(c), options); |
||||
|
break; |
||||
|
case 'ajax': |
||||
|
if (options.url=='') { |
||||
|
$.error('jquery.arcticmodal: Don\'t set parameter "url"'); |
||||
|
return; |
||||
|
} |
||||
|
return modal.init_el($('<div />'), options); |
||||
|
break; |
||||
|
} |
||||
|
} else { |
||||
|
return this.each(function() { |
||||
|
modal.init_el($(this), $.extend(true, {}, options)); |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
}; |
||||
|
|
||||
|
|
||||
|
var actions = { |
||||
|
|
||||
|
|
||||
|
// Показать
|
||||
|
show: function() { |
||||
|
var $this = modal.getParentEl(this); |
||||
|
if ($this===false) { |
||||
|
$.error('jquery.arcticmodal: Uncorrect call'); |
||||
|
return; |
||||
|
} |
||||
|
var D = $this.data('arcticmodal'); |
||||
|
|
||||
|
// Добавить overlay и container
|
||||
|
D.overlay.block.hide(); |
||||
|
D.container.block.hide(); |
||||
|
$('BODY').append(D.overlay.block); |
||||
|
$('BODY').append(D.container.block); |
||||
|
|
||||
|
// Событие
|
||||
|
D.beforeOpen(D, $this); |
||||
|
$this.trigger('beforeOpen'); |
||||
|
|
||||
|
// Wrap
|
||||
|
if (D.wrap.css('overflow')!='hidden') { |
||||
|
D.wrap.data('arcticmodalOverflow', D.wrap.css('overflow')); |
||||
|
var w1 = D.wrap.outerWidth(true); |
||||
|
D.wrap.css('overflow', 'hidden'); |
||||
|
var w2 = D.wrap.outerWidth(true); |
||||
|
if (w2!=w1) |
||||
|
D.wrap.css('marginRight', (w2 - w1) + 'px'); |
||||
|
} |
||||
|
|
||||
|
// Скрыть предыдущие оверлеи
|
||||
|
modals.not($this).each(function() { |
||||
|
var d = $(this).data('arcticmodal'); |
||||
|
d.overlay.block.hide(); |
||||
|
}); |
||||
|
|
||||
|
// Показать
|
||||
|
modal.transition(D.overlay.block, 'show', modals.length>1 ? {type: 'none'} : D.openEffect); |
||||
|
modal.transition(D.container.block, 'show', modals.length>1 ? {type: 'none'} : D.openEffect, function() { |
||||
|
D.afterOpen(D, $this); |
||||
|
$this.trigger('afterOpen'); |
||||
|
}); |
||||
|
|
||||
|
return $this; |
||||
|
}, |
||||
|
|
||||
|
|
||||
|
// Закрыть
|
||||
|
close: function() { |
||||
|
if ($.isFunction(this)) { |
||||
|
modals.each(function() { |
||||
|
$(this).arcticmodal('close'); |
||||
|
}); |
||||
|
} else { |
||||
|
return this.each(function() { |
||||
|
var $this = modal.getParentEl(this); |
||||
|
if ($this===false) { |
||||
|
$.error('jquery.arcticmodal: Uncorrect call'); |
||||
|
return; |
||||
|
} |
||||
|
var D = $this.data('arcticmodal'); |
||||
|
|
||||
|
// Событие перед закрытием
|
||||
|
if (D.beforeClose(D, $this)===false) return; |
||||
|
$this.trigger('beforeClose'); |
||||
|
|
||||
|
// Показать предыдущие оверлеи
|
||||
|
modals.not($this).last().each(function() { |
||||
|
var d = $(this).data('arcticmodal'); |
||||
|
d.overlay.block.show(); |
||||
|
}); |
||||
|
|
||||
|
modal.transition(D.overlay.block, 'hide', modals.length>1 ? {type: 'none'} : D.closeEffect); |
||||
|
modal.transition(D.container.block, 'hide', modals.length>1 ? {type: 'none'} : D.closeEffect, function() { |
||||
|
|
||||
|
// Событие после закрытия
|
||||
|
D.afterClose(D, $this); |
||||
|
$this.trigger('afterClose'); |
||||
|
|
||||
|
// Если не клонировали - вернём на место
|
||||
|
if (!D.clone) |
||||
|
$('#arcticmodalReserve' + D.modalID).replaceWith(D.body.find('>*')); |
||||
|
|
||||
|
D.overlay.block.remove(); |
||||
|
D.container.block.remove(); |
||||
|
$this.data('arcticmodal', null); |
||||
|
if (!$('.arcticmodal-container').length) { |
||||
|
if (D.wrap.data('arcticmodalOverflow')) |
||||
|
D.wrap.css('overflow', D.wrap.data('arcticmodalOverflow')); |
||||
|
D.wrap.css('marginRight', 0); |
||||
|
} |
||||
|
|
||||
|
}); |
||||
|
|
||||
|
if (D.type=='ajax') |
||||
|
D.ajax_request.abort(); |
||||
|
|
||||
|
modals = modals.not($this); |
||||
|
}); |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
|
||||
|
// Установить опции по-умолчанию
|
||||
|
setDefault: function(options) { |
||||
|
$.extend(true, default_options, options); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
}; |
||||
|
|
||||
|
|
||||
|
$(function() { |
||||
|
default_options.wrap = $((document.all && !document.querySelector) ? 'html' : 'body'); |
||||
|
}); |
||||
|
|
||||
|
|
||||
|
// Закрытие при нажатии Escape
|
||||
|
$(document).bind('keyup.arcticmodal', function(e) { |
||||
|
var m = modals.last(); |
||||
|
if (!m.length) return; |
||||
|
var D = m.data('arcticmodal'); |
||||
|
if (D.closeOnEsc && (e.keyCode===27)) |
||||
|
m.arcticmodal('close'); |
||||
|
}); |
||||
|
|
||||
|
|
||||
|
$.arcticmodal = $.fn.arcticmodal = function(method) { |
||||
|
|
||||
|
if (actions[method]) { |
||||
|
return actions[method].apply(this, Array.prototype.slice.call(arguments, 1)); |
||||
|
} else if (typeof method==='object' || !method) { |
||||
|
return modal.init.apply(this, arguments); |
||||
|
} else { |
||||
|
$.error('jquery.arcticmodal: Method ' + method + ' does not exist'); |
||||
|
} |
||||
|
|
||||
|
}; |
||||
|
|
||||
|
|
||||
|
})(jQuery); |
@ -0,0 +1,8 @@ |
|||||
|
.arcticmodal-overlay, |
||||
|
.arcticmodal-container { position: fixed; left: 0; top: 0; right: 0; bottom: 0; z-index: 1010; } |
||||
|
.arcticmodal-container { overflow: auto; margin: 0; padding: 0; border: 0; border-collapse: collapse; } |
||||
|
*:first-child+html .arcticmodal-container { height: 100% } |
||||
|
.arcticmodal-container_i { height: 100%; margin: 0 auto; } |
||||
|
.arcticmodal-container_i2 { padding: 24px; margin: 0; border: 0; vertical-align: middle; padding-top: 50px;} |
||||
|
.arcticmodal-error { padding: 20px; border-radius: 10px; background: #000; color: #fff; } |
||||
|
.arcticmodal-loading { width: 80px; height: 80px; border-radius: 10px; background: #000 url(/mail_attachment_popup/static/src/img/loading.gif) no-repeat 50% 50%; } |
@ -0,0 +1,11 @@ |
|||||
|
.box-modal { |
||||
|
position: relative; |
||||
|
padding: 16px; |
||||
|
background: #fff; |
||||
|
color: #3c3c3c; |
||||
|
font: 14px/18px Arial, "Helvetica CY", "Nimbus Sans L", sans-serif; |
||||
|
box-shadow: 0 0 0 6px rgba(153, 153, 153, .3); |
||||
|
border-radius: 6px; |
||||
|
} |
||||
|
.box-modal_close { position: absolute; right: -25px; top: -25px; font-size: 30px; line-height: 15px; color: #ffffff; cursor: pointer; } |
||||
|
.box-modal_close:hover { color: #B1B1B1; } |
@ -0,0 +1,16 @@ |
|||||
|
.g-hidden { |
||||
|
display: none; |
||||
|
} |
||||
|
.box-modal img { |
||||
|
max-width: 900px; |
||||
|
width: 100%; |
||||
|
} |
||||
|
.box-modal-li li { |
||||
|
list-style-type: none; |
||||
|
} |
||||
|
.box-modal-li { |
||||
|
padding-left: 0; |
||||
|
} |
||||
|
.o_attachment .o_image { |
||||
|
cursor: pointer; |
||||
|
} |
After Width: 32 | Height: 32 | Size: 3.1 KiB |
@ -0,0 +1,29 @@ |
|||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||
|
<template> |
||||
|
<t t-extend="mail.Attachment"> |
||||
|
<t t-jquery="div[t-att-title='attachment.name'] .o_image" t-operation="replace"> |
||||
|
<t t-if="attachment.mimetype and attachment.mimetype.search('image/') !== -1"> |
||||
|
<span class="m-dotted" t-attf-onclick="$('#ImageModal{{ attachment.id }}').arcticmodal()"> |
||||
|
<div class="o_image" target="_blank" t-att-data-mimetype="attachment.mimetype" t-attf-data-src="/web/image/#{attachment.id}/100x80"> |
||||
|
<span class='o_attachment_name'><t t-esc='attachment.name'/></span> |
||||
|
</div> |
||||
|
</span> |
||||
|
<div class="g-hidden"> |
||||
|
<div class="box-modal" t-attf-id="ImageModal{{ attachment.id }}"> |
||||
|
<div class="box-modal_close arcticmodal-close">X</div> |
||||
|
<img t-att-data-mimetype="attachment.mimetype" t-attf-src="/web/image/#{attachment.id}"></img> |
||||
|
<ul class="box-modal-li"> |
||||
|
<li><span class='o_attachment_name'><t t-esc='attachment.name'/></span></li> |
||||
|
<li><span class='oe_download_original_img'><a t-att-href='attachment.url' target="_blank">Download</a></span></li> |
||||
|
</ul> |
||||
|
</div> |
||||
|
</div> |
||||
|
</t> |
||||
|
<t t-if="! (attachment.mimetype and attachment.mimetype.search('image/') !== -1)"> |
||||
|
<a class="o_image" t-att-href='attachment.url' target="_blank" t-att-data-mimetype="attachment.mimetype" t-attf-data-src="/web/image/#{attachment.id}/100x80"> |
||||
|
<span class='o_attachment_name'><t t-esc='attachment.name'/></span> |
||||
|
</a> |
||||
|
</t> |
||||
|
</t> |
||||
|
</t> |
||||
|
</template> |
@ -0,0 +1,13 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<openerp> |
||||
|
<data> |
||||
|
<template id="assets_backend" name="mail attachment popup assets" inherit_id="web.assets_backend"> |
||||
|
<xpath expr="." position="inside"> |
||||
|
<link rel="stylesheet" href="/mail_attachment_popup/static/src/css/jquery.arcticmodal.css"/> |
||||
|
<link rel="stylesheet" href="/mail_attachment_popup/static/src/css/simple.css"/> |
||||
|
<link rel="stylesheet" href="/mail_attachment_popup/static/src/css/styles.css"/> |
||||
|
<script type="text/javascript" src="/mail_attachment_popup/static/lib/js/jquery.arcticmodal.js"></script> |
||||
|
</xpath> |
||||
|
</template> |
||||
|
</data> |
||||
|
</openerp> |
@ -0,0 +1,26 @@ |
|||||
|
.. image:: https://itpp.dev/images/infinity-readme.png |
||||
|
:alt: Tested and maintained by IT Projects Labs |
||||
|
:target: https://itpp.dev |
||||
|
|
||||
|
Mail Base |
||||
|
========= |
||||
|
|
||||
|
* makes built-in mail js features extendable. |
||||
|
* handles ``search_default_*`` parameters in context. |
||||
|
* fixes toggling left bar |
||||
|
* fixes Recipients field. Out-of-box this field could be empty. |
||||
|
|
||||
|
One can say, that the module do this todo from `addons/mail/static/src/js/chat_manager.js <https://github.com/odoo/odoo/blob/9.0/addons/mail/static/src/js/chat_manager.js#L57>`__ |
||||
|
|
||||
|
// to do: move this to mail.utils |
||||
|
|
||||
|
|
||||
|
Note. Due to odoo restrictions, module makes mail initialization again. That is browser loads emoji and other chat data twice. This is the only way to make Mail feature extendable. |
||||
|
|
||||
|
Further information |
||||
|
=================== |
||||
|
|
||||
|
.. Odoo Apps Store: https://apps.odoo.com/apps/modules/9.0/mail_base/ |
||||
|
|
||||
|
|
||||
|
Tested on `Odoo 9.0 <https://github.com/odoo/odoo/commit/ed463864366029f8d4289db831fcdc196fc716cb>`_ |
@ -0,0 +1,4 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
|
||||
|
from . import models |
||||
|
from . import controllers |
@ -0,0 +1,22 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
{ |
||||
|
"name": "Mail Base", |
||||
|
"summary": """Makes Mail extendable""", |
||||
|
"category": "Discuss", |
||||
|
"images": [], |
||||
|
"version": "1.0.1", |
||||
|
|
||||
|
"author": "IT-Projects LLC, Pavel Romanchenko", |
||||
|
"website": "https://twitter.com/OdooFree", |
||||
|
"license": "LGPL-3", |
||||
|
|
||||
|
"depends": [ |
||||
|
"base", |
||||
|
"mail" |
||||
|
], |
||||
|
|
||||
|
"data": [ |
||||
|
"views/templates.xml", |
||||
|
], |
||||
|
'installable': True, |
||||
|
} |
@ -0,0 +1 @@ |
|||||
|
from . import main |
@ -0,0 +1,15 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
from openerp.http import request |
||||
|
from openerp.addons.bus.controllers.main import BusController |
||||
|
|
||||
|
|
||||
|
class MailChatController(BusController): |
||||
|
# ----------------------------- |
||||
|
# Extends BUS Controller Poll |
||||
|
# ----------------------------- |
||||
|
|
||||
|
def _poll(self, dbname, channels, last, options): |
||||
|
if request.session.uid: |
||||
|
channels.append((request.db, 'mail_base.mail_sent')) |
||||
|
|
||||
|
return super(MailChatController, self)._poll(dbname, channels, last, options) |
@ -0,0 +1,9 @@ |
|||||
|
`1.0.1` |
||||
|
------- |
||||
|
|
||||
|
- **FIX**: clear messages cache on sending message via Mail Composer. Otherwise Sent, Arhives menus will have new message until user refresh whole web page |
||||
|
|
||||
|
`1.0.0` |
||||
|
------- |
||||
|
|
||||
|
- Init version |
@ -0,0 +1,4 @@ |
|||||
|
Mail Base |
||||
|
========= |
||||
|
|
||||
|
To use this module you need either install module that depends on it or create new module. |
@ -0,0 +1,31 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
|
||||
|
from openerp import api, models |
||||
|
|
||||
|
|
||||
|
class MailMessage(models.Model): |
||||
|
_inherit = 'mail.message' |
||||
|
|
||||
|
@api.multi |
||||
|
def write(self, values): |
||||
|
if values.get('needaction_partner_ids'): |
||||
|
if not values.get('partner_ids'): |
||||
|
values['partner_ids'] = [] |
||||
|
for triplet in values.get('needaction_partner_ids'): |
||||
|
if triplet[0] == 6: |
||||
|
for id in triplet[2]: |
||||
|
values['partner_ids'].append((4, id, False)) |
||||
|
return super(MailMessage, self).write(values) |
||||
|
|
||||
|
|
||||
|
class MailComposer(models.TransientModel): |
||||
|
|
||||
|
_inherit = 'mail.compose.message' |
||||
|
|
||||
|
@api.multi |
||||
|
def send_mail(self, auto_commit=False): |
||||
|
res = super(MailComposer, self).send_mail(auto_commit=auto_commit) |
||||
|
notification = {} |
||||
|
self.env['bus.bus'].sendone((self._cr.dbname, 'mail_base.mail_sent'), notification) |
||||
|
|
||||
|
return res |
After Width: 100 | Height: 100 | Size: 2.1 KiB |
1197
mail_base/static/lib/base.js
File diff suppressed because it is too large
View File
@ -0,0 +1,12 @@ |
|||||
|
<?xml version="1.0"?> |
||||
|
<openerp> |
||||
|
<data> |
||||
|
<template id="mail_base_assets_backend" |
||||
|
name="mail_base_assets_backend" |
||||
|
inherit_id="web.assets_backend"> |
||||
|
<xpath expr="." position="inside"> |
||||
|
<script src="/mail_base/static/lib/base.js" type="text/javascript"></script> |
||||
|
</xpath> |
||||
|
</template> |
||||
|
</data> |
||||
|
</openerp> |
@ -0,0 +1,12 @@ |
|||||
|
.. image:: https://itpp.dev/images/infinity-readme.png |
||||
|
:alt: Tested and maintained by IT Projects Labs |
||||
|
:target: https://itpp.dev |
||||
|
|
||||
|
Mail relocation |
||||
|
=============== |
||||
|
|
||||
|
Description: https://www.odoo.com/apps/modules/9.0/mail_move_message/ |
||||
|
|
||||
|
Further information and discussion: http://yelizariev.github.io/odoo/module/2015/04/10/mail-relocation.html |
||||
|
|
||||
|
Tested on `Odoo 8.0 <https://github.com/odoo/odoo/commit/d023c079ed86468436f25da613bf486a4a17d625>`_ |
@ -0,0 +1,3 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
from . import controllers |
||||
|
from . import mail_move_message_models |
@ -0,0 +1,19 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
{ |
||||
|
'name': 'Mail relocation', |
||||
|
'version': '1.0.4', |
||||
|
'author': 'IT-Projects LLC, Ivan Yelizariev, Pavel Romanchenko', |
||||
|
'license': 'LGPL-3', |
||||
|
'category': 'Discuss', |
||||
|
'website': 'https://twitter.com/yelizariev', |
||||
|
'depends': ['mail_all', 'web_polymorphic_field'], |
||||
|
'images': ['images/inbox.png'], |
||||
|
'data': [ |
||||
|
'mail_move_message_views.xml', |
||||
|
'data/mail_move_message_data.xml', |
||||
|
], |
||||
|
'qweb': [ |
||||
|
'static/src/xml/mail_move_message_main.xml', |
||||
|
], |
||||
|
'installable': True, |
||||
|
} |
@ -0,0 +1,2 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
from . import main |
@ -0,0 +1,69 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
from openerp.addons.web.controllers.main import DataSet |
||||
|
from openerp.tools.translate import _ |
||||
|
from openerp import http |
||||
|
from openerp.http import request |
||||
|
import openerp |
||||
|
|
||||
|
|
||||
|
class MailChatController(openerp.addons.bus.controllers.main.BusController): |
||||
|
# ----------------------------- |
||||
|
# Extends BUS Controller Poll |
||||
|
# ----------------------------- |
||||
|
|
||||
|
def _poll(self, dbname, channels, last, options): |
||||
|
if request.session.uid: |
||||
|
channels.append((request.db, 'mail_move_message')) |
||||
|
channels.append((request.db, 'mail_move_message.delete_message')) |
||||
|
return super(MailChatController, self)._poll(dbname, channels, last, options) |
||||
|
|
||||
|
|
||||
|
class DataSetCustom(DataSet): |
||||
|
|
||||
|
def _extend_name(self, model, records): |
||||
|
cr, uid, context = request.cr, request.uid, request.context |
||||
|
Model = request.registry[model] |
||||
|
fields = Model.fields_get(cr, uid, False, context) |
||||
|
contact_field = False |
||||
|
for n, f in fields.iteritems(): |
||||
|
if f['type'] == 'many2one' and f['relation'] == 'res.partner': |
||||
|
contact_field = n |
||||
|
break |
||||
|
partner_info = {} |
||||
|
if contact_field: |
||||
|
partner_info = Model.read(cr, uid, [r[0] for r in records], [contact_field], context) |
||||
|
partner_info = dict([(p['id'], p[contact_field]) for p in partner_info]) |
||||
|
res = [] |
||||
|
for r in records: |
||||
|
if partner_info.get(r[0]): |
||||
|
res.append((r[0], _('%s [%s] ID %s') % (r[1], partner_info.get(r[0])[1], r[0]))) |
||||
|
else: |
||||
|
res.append((r[0], _('%s ID %s') % (r[1], r[0]))) |
||||
|
return res |
||||
|
|
||||
|
@http.route('/web/dataset/call_kw/<model>/name_search', type='json', auth="user") |
||||
|
def name_search(self, model, method, args, kwargs): |
||||
|
context = kwargs.get('context') |
||||
|
if context and context.get('extended_name_with_contact'): |
||||
|
# add order by ID desc |
||||
|
cr, uid = request.cr, request.uid |
||||
|
Model = request.registry[model] |
||||
|
search_args = list(kwargs.get('args') or []) |
||||
|
limit = int(kwargs.get('limit') or 100) |
||||
|
operator = kwargs.get('operator') |
||||
|
name = kwargs.get('name') |
||||
|
if Model._rec_name and (not name == '' and operator == 'ilike'): |
||||
|
search_args += [(Model._rec_name, operator, name)] |
||||
|
ids = Model.search(cr, uid, search_args, limit=limit, order='id desc', context=context) |
||||
|
res = Model.name_get(cr, uid, ids, context) |
||||
|
return self._extend_name(model, res) |
||||
|
|
||||
|
return self._call_kw(model, method, args, kwargs) |
||||
|
|
||||
|
@http.route('/web/dataset/call_kw/<model>/name_get', type='json', auth="user") |
||||
|
def name_get(self, model, method, args, kwargs): |
||||
|
res = self._call_kw(model, method, args, kwargs) |
||||
|
context = kwargs.get('context') |
||||
|
if context and context.get('extended_name_with_contact'): |
||||
|
res = self._extend_name(model, res) |
||||
|
return res |
@ -0,0 +1,9 @@ |
|||||
|
<?xml version="1.0"?> |
||||
|
<openerp> |
||||
|
<data noupdate="1"> |
||||
|
<record id="mail_relocation_models" model="ir.config_parameter"> |
||||
|
<field name="key">mail_relocation_models</field> |
||||
|
<field name="value">crm.lead,project.task</field> |
||||
|
</record> |
||||
|
</data> |
||||
|
</openerp> |
@ -0,0 +1,21 @@ |
|||||
|
`1.0.4` |
||||
|
------- |
||||
|
|
||||
|
- FIX: don't allow to relocate message to itself as it cause infinitive loop |
||||
|
- ADD: 'Move Followers' option -- Add followers of current record to a new record. |
||||
|
|
||||
|
`1.0.3` |
||||
|
------- |
||||
|
|
||||
|
- FIX email_from parsing. There was an error with specific email_from value (e.g. '"name @ example" <name@example.com>') |
||||
|
|
||||
|
`1.0.2` |
||||
|
------- |
||||
|
|
||||
|
- big improvements in interface |
||||
|
|
||||
|
`1.0.1` |
||||
|
------- |
||||
|
|
||||
|
- fix bug "some messages are not shown in inbox after relocation" |
||||
|
- improve "Move back" tool |
@ -0,0 +1,173 @@ |
|||||
|
# Translation of Odoo Server. |
||||
|
# This file contains the translation of the following modules: |
||||
|
# * mail_move_message |
||||
|
# |
||||
|
msgid "" |
||||
|
msgstr "" |
||||
|
"Project-Id-Version: Odoo Server 8.0\n" |
||||
|
"Report-Msgid-Bugs-To: \n" |
||||
|
"POT-Creation-Date: 2015-08-11 06:53+0000\n" |
||||
|
"PO-Revision-Date: 2015-08-11 06:53+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: mail_move_message |
||||
|
#: view:mail_move_message.wizard:mail_move_message.view_wizard |
||||
|
msgid "Cancel" |
||||
|
msgstr "" |
||||
|
|
||||
|
#. module: mail_move_message |
||||
|
#: field:mail_move_message.wizard,create_uid:0 |
||||
|
msgid "Created by" |
||||
|
msgstr "" |
||||
|
|
||||
|
#. module: mail_move_message |
||||
|
#: field:mail_move_message.wizard,create_date:0 |
||||
|
msgid "Created on" |
||||
|
msgstr "" |
||||
|
|
||||
|
#. module: mail_move_message |
||||
|
#: field:mail_move_message.wizard,id:0 |
||||
|
msgid "ID" |
||||
|
msgstr "" |
||||
|
|
||||
|
#. module: mail_move_message |
||||
|
#: field:mail.message,is_moved:0 |
||||
|
msgid "Is moved" |
||||
|
msgstr "" |
||||
|
|
||||
|
#. module: mail_move_message |
||||
|
#: field:mail_move_message.wizard,write_uid:0 |
||||
|
msgid "Last Updated by" |
||||
|
msgstr "" |
||||
|
|
||||
|
#. module: mail_move_message |
||||
|
#: field:mail_move_message.wizard,write_date:0 |
||||
|
msgid "Last Updated on" |
||||
|
msgstr "" |
||||
|
|
||||
|
#. module: mail_move_message |
||||
|
#: field:mail_move_message.wizard,record_url:0 |
||||
|
msgid "Link to record" |
||||
|
msgstr "" |
||||
|
|
||||
|
#. module: mail_move_message |
||||
|
#: model:ir.model,name:mail_move_message.model_mail_message |
||||
|
#: view:mail_move_message.wizard:mail_move_message.view_wizard |
||||
|
#: field:mail_move_message.wizard,message_id:0 |
||||
|
msgid "Message" |
||||
|
msgstr "" |
||||
|
|
||||
|
#. module: mail_move_message |
||||
|
#: view:mail_move_message.wizard:mail_move_message.view_wizard |
||||
|
msgid "Move" |
||||
|
msgstr "" |
||||
|
|
||||
|
#. module: mail_move_message |
||||
|
#: help:mail_move_message.wizard,move_back:0 |
||||
|
msgid "Move message and submessages to original place" |
||||
|
msgstr "" |
||||
|
|
||||
|
#. module: mail_move_message |
||||
|
#: view:mail_move_message.wizard:mail_move_message.view_wizard |
||||
|
msgid "Move Message" |
||||
|
msgstr "" |
||||
|
|
||||
|
#. module: mail_move_message |
||||
|
#: field:mail_move_message.wizard,move_back:0 |
||||
|
msgid "Move to origin" |
||||
|
msgstr "" |
||||
|
|
||||
|
#. module: mail_move_message |
||||
|
#. openerp-web |
||||
|
#: code:addons/mail_move_message/static/src/xml/mail_move_message_main.xml:5 |
||||
|
#, python-format |
||||
|
msgid "Move to thread" |
||||
|
msgstr "" |
||||
|
|
||||
|
#. module: mail_move_message |
||||
|
#: field:mail.message,moved_by_message_id:0 |
||||
|
msgid "Moved by message" |
||||
|
msgstr "" |
||||
|
|
||||
|
#. module: mail_move_message |
||||
|
#: field:mail.message,moved_by_user_id:0 |
||||
|
msgid "Moved by user" |
||||
|
msgstr "" |
||||
|
|
||||
|
#. module: mail_move_message |
||||
|
#: view:mail_move_message.wizard:mail_move_message.view_wizard |
||||
|
msgid "Open message" |
||||
|
msgstr "" |
||||
|
|
||||
|
#. module: mail_move_message |
||||
|
#: field:mail.message,moved_from_parent_id:0 |
||||
|
msgid "Parent Message (Original)" |
||||
|
msgstr "" |
||||
|
|
||||
|
#. module: mail_move_message |
||||
|
#: code:addons/mail_move_message/mail_move_message_models.py:107 |
||||
|
#, python-format |
||||
|
msgid "Record" |
||||
|
msgstr "" |
||||
|
|
||||
|
#. module: mail_move_message |
||||
|
#: field:mail_move_message.wizard,res_id:0 |
||||
|
msgid "Record ID" |
||||
|
msgstr "" |
||||
|
|
||||
|
#. module: mail_move_message |
||||
|
#: field:mail_move_message.wizard,model_id:0 |
||||
|
msgid "Record type" |
||||
|
msgstr "" |
||||
|
|
||||
|
#. module: mail_move_message |
||||
|
#: field:mail.message,moved_from_res_id:0 |
||||
|
msgid "Related Document ID (Original)" |
||||
|
msgstr "" |
||||
|
|
||||
|
#. module: mail_move_message |
||||
|
#: field:mail.message,moved_from_model:0 |
||||
|
msgid "Related Document Model (Original)" |
||||
|
msgstr "" |
||||
|
|
||||
|
#. module: mail_move_message |
||||
|
#. openerp-web |
||||
|
#: code:addons/mail_move_message/static/src/js/mail_move_message.js:17 |
||||
|
#, python-format |
||||
|
msgid "Relocate Message" |
||||
|
msgstr "" |
||||
|
|
||||
|
#. module: mail_move_message |
||||
|
#: field:mail_move_message.wizard,parent_id:0 |
||||
|
msgid "Search by name" |
||||
|
msgstr "" |
||||
|
|
||||
|
#. module: mail_move_message |
||||
|
#: help:mail.message,moved_by_message_id:0 |
||||
|
msgid "Top message, that initate moving this message" |
||||
|
msgstr "" |
||||
|
|
||||
|
#. module: mail_move_message |
||||
|
#: view:mail_move_message.wizard:mail_move_message.view_wizard |
||||
|
msgid "You cannot move this message. It was already moved with a message bellow. Open one and apply changes there." |
||||
|
msgstr "" |
||||
|
|
||||
|
#. module: mail_move_message |
||||
|
#: help:mail_move_message.wizard,model_id:0 |
||||
|
msgid "List available Models is configured at Settings\Technical\Emails\Mail Relocation. Empty for unassigned email" |
||||
|
msgstr "" |
||||
|
|
||||
|
#. module: mail_move_message |
||||
|
#: help:mail_move_message.wizard,filter_by_partner:0 |
||||
|
msgid "Show only records with the same partner as email author" |
||||
|
msgstr "" |
||||
|
|
||||
|
#. module: mail_move_message |
||||
|
#: help:mail_move_message.wizard,move_followers:0 |
||||
|
msgid "Add followers of current record to a new record.\nYou must use this option, if new record has restricted access.\nYou can change default value for this option at Settings/System Parameters" |
||||
|
msgstr "" |
@ -0,0 +1,160 @@ |
|||||
|
# Translation of Odoo Server. |
||||
|
# This file contains the translation of the following modules: |
||||
|
# * mail_move_message |
||||
|
# |
||||
|
msgid "" |
||||
|
msgstr "" |
||||
|
"Project-Id-Version: Odoo Server 8.0\n" |
||||
|
"Report-Msgid-Bugs-To: \n" |
||||
|
"POT-Creation-Date: 2015-08-11 06:53+0000\n" |
||||
|
"PO-Revision-Date: 2015-08-11 08:58+0200\n" |
||||
|
"Last-Translator: Matjaz Mozetic <m.mozetic@matmoz.si>\n" |
||||
|
"Language-Team: \n" |
||||
|
"MIME-Version: 1.0\n" |
||||
|
"Content-Type: text/plain; charset=UTF-8\n" |
||||
|
"Content-Transfer-Encoding: 8bit\n" |
||||
|
"Plural-Forms: \n" |
||||
|
"Language: sl\n" |
||||
|
"X-Generator: Poedit 1.8.2\n" |
||||
|
|
||||
|
#. module: mail_move_message |
||||
|
#: view:mail_move_message.wizard:mail_move_message.view_wizard |
||||
|
msgid "Cancel" |
||||
|
msgstr "Preklic" |
||||
|
|
||||
|
#. module: mail_move_message |
||||
|
#: field:mail_move_message.wizard,create_uid:0 |
||||
|
msgid "Created by" |
||||
|
msgstr "Ustvaril" |
||||
|
|
||||
|
#. module: mail_move_message |
||||
|
#: field:mail_move_message.wizard,create_date:0 |
||||
|
msgid "Created on" |
||||
|
msgstr "Ustvarjeno" |
||||
|
|
||||
|
#. module: mail_move_message |
||||
|
#: field:mail_move_message.wizard,id:0 |
||||
|
msgid "ID" |
||||
|
msgstr "ID" |
||||
|
|
||||
|
#. module: mail_move_message |
||||
|
#: field:mail.message,is_moved:0 |
||||
|
msgid "Is moved" |
||||
|
msgstr "Je premaknjeno" |
||||
|
|
||||
|
#. module: mail_move_message |
||||
|
#: field:mail_move_message.wizard,write_uid:0 |
||||
|
msgid "Last Updated by" |
||||
|
msgstr "Zadnjič posodobil" |
||||
|
|
||||
|
#. module: mail_move_message |
||||
|
#: field:mail_move_message.wizard,write_date:0 |
||||
|
msgid "Last Updated on" |
||||
|
msgstr "Zadnjič posodobljeno" |
||||
|
|
||||
|
#. module: mail_move_message |
||||
|
#: field:mail_move_message.wizard,record_url:0 |
||||
|
msgid "Link to record" |
||||
|
msgstr "Povezava do zapisa" |
||||
|
|
||||
|
#. module: mail_move_message |
||||
|
#: model:ir.model,name:mail_move_message.model_mail_message |
||||
|
#: view:mail_move_message.wizard:mail_move_message.view_wizard |
||||
|
#: field:mail_move_message.wizard,message_id:0 |
||||
|
msgid "Message" |
||||
|
msgstr "Sporočilo" |
||||
|
|
||||
|
#. module: mail_move_message |
||||
|
#: view:mail_move_message.wizard:mail_move_message.view_wizard |
||||
|
msgid "Move" |
||||
|
msgstr "Premik" |
||||
|
|
||||
|
#. module: mail_move_message |
||||
|
#: help:mail_move_message.wizard,move_back:0 |
||||
|
msgid "Move message and submessages to original place" |
||||
|
msgstr "Premik sporočila in podrejenih sporočil na izvorno mesto" |
||||
|
|
||||
|
#. module: mail_move_message |
||||
|
#: view:mail_move_message.wizard:mail_move_message.view_wizard |
||||
|
msgid "Move Message" |
||||
|
msgstr "Premik sporočila" |
||||
|
|
||||
|
#. module: mail_move_message |
||||
|
#: field:mail_move_message.wizard,move_back:0 |
||||
|
msgid "Move to origin" |
||||
|
msgstr "Premik na izvor" |
||||
|
|
||||
|
#. module: mail_move_message |
||||
|
#. openerp-web |
||||
|
#: code:addons/mail_move_message/static/src/xml/mail_move_message_main.xml:5 |
||||
|
#, python-format |
||||
|
msgid "Move to thread" |
||||
|
msgstr "Premik v nit" |
||||
|
|
||||
|
#. module: mail_move_message |
||||
|
#: field:mail.message,moved_by_message_id:0 |
||||
|
msgid "Moved by message" |
||||
|
msgstr "Premaknjeno s sporočilom" |
||||
|
|
||||
|
#. module: mail_move_message |
||||
|
#: field:mail.message,moved_by_user_id:0 |
||||
|
msgid "Moved by user" |
||||
|
msgstr "Premaknil uporabnik" |
||||
|
|
||||
|
#. module: mail_move_message |
||||
|
#: view:mail_move_message.wizard:mail_move_message.view_wizard |
||||
|
msgid "Open message" |
||||
|
msgstr "Odpri sporočilo" |
||||
|
|
||||
|
#. module: mail_move_message |
||||
|
#: field:mail.message,moved_from_parent_id:0 |
||||
|
msgid "Parent Message (Original)" |
||||
|
msgstr "Nadrejeno sporočilo (original)" |
||||
|
|
||||
|
#. module: mail_move_message |
||||
|
#: code:addons/mail_move_message/mail_move_message_models.py:107 |
||||
|
#, python-format |
||||
|
msgid "Record" |
||||
|
msgstr "Zapis" |
||||
|
|
||||
|
#. module: mail_move_message |
||||
|
#: field:mail_move_message.wizard,res_id:0 |
||||
|
msgid "Record ID" |
||||
|
msgstr "ID zapisa" |
||||
|
|
||||
|
#. module: mail_move_message |
||||
|
#: field:mail_move_message.wizard,model_id:0 |
||||
|
msgid "Record type" |
||||
|
msgstr "Tip zapisa" |
||||
|
|
||||
|
#. module: mail_move_message |
||||
|
#: field:mail.message,moved_from_res_id:0 |
||||
|
msgid "Related Document ID (Original)" |
||||
|
msgstr "ID povezanega dokumenta (original)" |
||||
|
|
||||
|
#. module: mail_move_message |
||||
|
#: field:mail.message,moved_from_model:0 |
||||
|
msgid "Related Document Model (Original)" |
||||
|
msgstr "Model povezanega dokumenta (original)" |
||||
|
|
||||
|
#. module: mail_move_message |
||||
|
#. openerp-web |
||||
|
#: code:addons/mail_move_message/static/src/js/mail_move_message.js:17 |
||||
|
#, python-format |
||||
|
msgid "Relocate Message" |
||||
|
msgstr "Premik sporočila" |
||||
|
|
||||
|
#. module: mail_move_message |
||||
|
#: field:mail_move_message.wizard,parent_id:0 |
||||
|
msgid "Search by name" |
||||
|
msgstr "Iskanje po nazivu" |
||||
|
|
||||
|
#. module: mail_move_message |
||||
|
#: help:mail.message,moved_by_message_id:0 |
||||
|
msgid "Top message, that initate moving this message" |
||||
|
msgstr "Zgornje sporočilo, ki je sprožilo premik tega sporočila" |
||||
|
|
||||
|
#. module: mail_move_message |
||||
|
#: view:mail_move_message.wizard:mail_move_message.view_wizard |
||||
|
msgid "You cannot move this message. It was already moved with a message bellow. Open one and apply changes there." |
||||
|
msgstr "Tega sporočila ne morete premakniti, ker je bilo že premaknjeno s spodnjim sporočilom. Tam lahko uveljavljate spremembe." |
After Width: 861 | Height: 628 | Size: 58 KiB |
@ -0,0 +1,413 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
from openerp import api |
||||
|
from openerp import fields |
||||
|
from openerp import models |
||||
|
from openerp.tools import email_split |
||||
|
from openerp.tools.translate import _ |
||||
|
|
||||
|
|
||||
|
class Wizard(models.TransientModel): |
||||
|
_name = 'mail_move_message.wizard' |
||||
|
|
||||
|
def _model_selection(self): |
||||
|
selection = [] |
||||
|
config_parameters = self.env['ir.config_parameter'] |
||||
|
model_names = config_parameters.get_param('mail_relocation_models') |
||||
|
model_names = model_names.split(',') if model_names else [] |
||||
|
|
||||
|
if 'default_message_id' in self.env.context: |
||||
|
message = self.env['mail.message'].browse(self.env.context['default_message_id']) |
||||
|
if message.model and message.model not in model_names: |
||||
|
model_names.append(message.model) |
||||
|
if message.moved_from_model and message.moved_from_model not in model_names: |
||||
|
model_names.append(message.moved_from_model) |
||||
|
if model_names: |
||||
|
selection = [(m.model, m.display_name) for m in self.env['ir.model'].search([('model', 'in', model_names)])] |
||||
|
|
||||
|
return selection |
||||
|
|
||||
|
@api.model |
||||
|
def default_get(self, fields_list): |
||||
|
res = super(Wizard, self).default_get(fields_list) |
||||
|
|
||||
|
model_fields = self.fields_get() |
||||
|
if model_fields['model']['selection']: |
||||
|
res['model'] = model_fields['model']['selection'] and model_fields['model']['selection'][0][0] |
||||
|
|
||||
|
if 'message_id' in res: |
||||
|
message = self.env['mail.message'].browse(res['message_id']) |
||||
|
email_from = message.email_from |
||||
|
parts = email_split(email_from.replace(' ', ',')) |
||||
|
if parts: |
||||
|
email = parts[0] |
||||
|
name = email_from.find(email) != -1 and email_from[:email_from.index(email)].replace('"', '').replace('<', '').strip() or email_from |
||||
|
else: |
||||
|
name, email = email_from |
||||
|
res['message_name_from'] = name |
||||
|
res['message_email_from'] = email |
||||
|
|
||||
|
res['partner_id'] = message.author_id.id |
||||
|
if message.author_id and self.env.uid not in [u.id for u in message.author_id.user_ids]: |
||||
|
res['filter_by_partner'] = True |
||||
|
if message.author_id and res.get('model'): |
||||
|
res_id = self.env[res['model']].search([], order='id desc', limit=1) |
||||
|
res['res_id'] = res_id and res_id[0].id |
||||
|
|
||||
|
config_parameters = self.env['ir.config_parameter'] |
||||
|
res['move_followers'] = config_parameters.get_param('mail_relocation_move_followers') |
||||
|
|
||||
|
res['uid'] = self.env.uid |
||||
|
|
||||
|
return res |
||||
|
|
||||
|
message_id = fields.Many2one('mail.message', string='Message') |
||||
|
message_body = fields.Html(related='message_id.body', string='Message to move', readonly=True) |
||||
|
message_from = fields.Char(related='message_id.email_from', string='From', readonly=True) |
||||
|
message_subject = fields.Char(related='message_id.subject', string='Subject', readonly=True) |
||||
|
message_moved_by_message_id = fields.Many2one('mail.message', related='message_id.moved_by_message_id', string='Moved with', readonly=True) |
||||
|
message_moved_by_user_id = fields.Many2one('res.users', related='message_id.moved_by_user_id', string='Moved by', readonly=True) |
||||
|
message_is_moved = fields.Boolean(string='Is Moved', related='message_id.is_moved', readonly=True) |
||||
|
parent_id = fields.Many2one('mail.message', string='Search by name', ) |
||||
|
model = fields.Selection(_model_selection, string='Model') |
||||
|
res_id = fields.Integer(string='Record') |
||||
|
can_move = fields.Boolean('Can move', compute='get_can_move') |
||||
|
move_back = fields.Boolean('MOVE TO ORIGIN', help='Move message and submessages to original place') |
||||
|
partner_id = fields.Many2one('res.partner', string='Author') |
||||
|
filter_by_partner = fields.Boolean('Filter Records by partner') |
||||
|
message_email_from = fields.Char() |
||||
|
message_name_from = fields.Char() |
||||
|
# FIXME message_to_read should be True even if current message or any his childs are unread |
||||
|
message_to_read = fields.Boolean(related='message_id.needaction') |
||||
|
uid = fields.Integer() |
||||
|
move_followers = fields.Boolean( |
||||
|
'Move Followers', |
||||
|
help="Add followers of current record to a new record.\n" |
||||
|
"You must use this option, if new record has restricted access.\n" |
||||
|
"You can change default value for this option at Settings/System Parameters") |
||||
|
|
||||
|
@api.depends('message_id') |
||||
|
@api.one |
||||
|
def get_can_move(self): |
||||
|
# message was not moved before OR message is a top message of previous move |
||||
|
self.can_move = not self.message_id.moved_by_message_id or self.message_id.moved_by_message_id.id == self.message_id.id |
||||
|
|
||||
|
@api.onchange('move_back') |
||||
|
def on_change_move_back(self): |
||||
|
if not self.move_back: |
||||
|
return |
||||
|
self.parent_id = self.message_id.moved_from_parent_id |
||||
|
model = self.message_id.moved_from_model |
||||
|
if self.message_id.is_moved: |
||||
|
self.model = model |
||||
|
self.res_id = self.message_id.moved_from_res_id |
||||
|
|
||||
|
@api.onchange('parent_id', 'res_id', 'model') |
||||
|
def update_move_back(self): |
||||
|
model = self.message_id.moved_from_model |
||||
|
self.move_back = self.parent_id == self.message_id.moved_from_parent_id \ |
||||
|
and self.res_id == self.message_id.moved_from_res_id \ |
||||
|
and (self.model == model or (not self.model and not model)) |
||||
|
|
||||
|
@api.onchange('parent_id') |
||||
|
def on_change_parent_id(self): |
||||
|
if self.parent_id and self.parent_id.model: |
||||
|
self.model = self.parent_id.model |
||||
|
self.res_id = self.parent_id.res_id |
||||
|
else: |
||||
|
self.model = None |
||||
|
self.res_id = None |
||||
|
|
||||
|
@api.onchange('model', 'filter_by_partner', 'partner_id') |
||||
|
def on_change_partner(self): |
||||
|
domain = {'res_id': [('id', '!=', self.message_id.res_id)]} |
||||
|
if self.model and self.filter_by_partner and self.partner_id: |
||||
|
fields = self.env[self.model].fields_get(False) |
||||
|
contact_field = False |
||||
|
for n, f in fields.iteritems(): |
||||
|
if f['type'] == 'many2one' and f['relation'] == 'res.partner': |
||||
|
contact_field = n |
||||
|
break |
||||
|
if contact_field: |
||||
|
domain['res_id'].append((contact_field, '=', self.partner_id.id)) |
||||
|
if self.model: |
||||
|
res_id = self.env[self.model].search(domain['res_id'], order='id desc', limit=1) |
||||
|
self.res_id = res_id and res_id[0].id |
||||
|
else: |
||||
|
self.res_id = None |
||||
|
return {'domain': domain} |
||||
|
|
||||
|
@api.one |
||||
|
def check_access(self): |
||||
|
cr = self._cr |
||||
|
uid = self.env.user.id |
||||
|
operation = 'write' |
||||
|
context = self._context |
||||
|
|
||||
|
if not (self.model and self.res_id): |
||||
|
return True |
||||
|
model_obj = self.pool[self.model] |
||||
|
mids = model_obj.exists(cr, uid, [self.res_id]) |
||||
|
if hasattr(model_obj, 'check_mail_message_access'): |
||||
|
model_obj.check_mail_message_access(cr, uid, mids, operation, context=context) |
||||
|
else: |
||||
|
self.pool['mail.thread'].check_mail_message_access(cr, uid, mids, operation, model_obj=model_obj, context=context) |
||||
|
|
||||
|
@api.multi |
||||
|
def open_moved_by_message_id(self): |
||||
|
message_id = None |
||||
|
for r in self: |
||||
|
message_id = r.message_moved_by_message_id.id |
||||
|
return { |
||||
|
'type': 'ir.actions.act_window', |
||||
|
'res_model': 'mail_move_message.wizard', |
||||
|
'view_mode': 'form', |
||||
|
'view_type': 'form', |
||||
|
'views': [[False, 'form']], |
||||
|
'target': 'new', |
||||
|
'context': {'default_message_id': message_id}, |
||||
|
} |
||||
|
|
||||
|
@api.multi |
||||
|
def move(self): |
||||
|
for r in self: |
||||
|
r.check_access() |
||||
|
if not r.parent_id or not (r.parent_id.model == r.model and |
||||
|
r.parent_id.res_id == r.res_id): |
||||
|
# link with the first message of record |
||||
|
parent = self.env['mail.message'].search([('model', '=', r.model), ('res_id', '=', r.res_id)], order='id', limit=1) |
||||
|
r.parent_id = parent.id or None |
||||
|
|
||||
|
r.message_id.move(r.parent_id.id, r.res_id, r.model, r.move_back, r.move_followers) |
||||
|
|
||||
|
if not (r.model and r.res_id): |
||||
|
r.message_id.needaction = False |
||||
|
return { |
||||
|
'type': 'ir.actions.client', |
||||
|
'name': 'All messages', |
||||
|
'tag': 'reload', |
||||
|
} |
||||
|
return { |
||||
|
'name': _('Record'), |
||||
|
'view_type': 'form', |
||||
|
'view_mode': 'form', |
||||
|
'res_model': r.model, |
||||
|
'res_id': r.res_id, |
||||
|
'views': [(False, 'form')], |
||||
|
'type': 'ir.actions.act_window', |
||||
|
} |
||||
|
|
||||
|
@api.one |
||||
|
def delete(self): |
||||
|
msg_id = self.message_id.id |
||||
|
|
||||
|
# Send notification |
||||
|
notification = {'id': msg_id} |
||||
|
self.env['bus.bus'].sendone((self._cr.dbname, 'mail_move_message.delete_message'), notification) |
||||
|
|
||||
|
self.message_id.unlink() |
||||
|
return {} |
||||
|
|
||||
|
@api.model |
||||
|
def create_partner(self, message_id, relation, partner_id, message_name_from, message_email_from): |
||||
|
model = self.env[relation] |
||||
|
message = self.env['mail.message'].browse(message_id) |
||||
|
if not partner_id and message_name_from: |
||||
|
partner_id = self.env['res.partner'].with_context({'update_message_author': True}).create({ |
||||
|
'name': message_name_from, |
||||
|
'email': message_email_from |
||||
|
}).id |
||||
|
|
||||
|
context = {'partner_id': partner_id} |
||||
|
if model._rec_name: |
||||
|
context.update({'default_%s' % model._rec_name: message.subject}) |
||||
|
|
||||
|
fields = model.fields_get() |
||||
|
contact_field = False |
||||
|
for n, f in fields.iteritems(): |
||||
|
if f['type'] == 'many2one' and f['relation'] == 'res.partner': |
||||
|
contact_field = n |
||||
|
break |
||||
|
if contact_field: |
||||
|
context.update({'default_%s' % contact_field: partner_id}) |
||||
|
return context |
||||
|
|
||||
|
@api.one |
||||
|
def read_close(self): |
||||
|
self.message_id.set_message_done() |
||||
|
self.message_id.child_ids.set_message_done() |
||||
|
return {'type': 'ir.actions.act_window_close'} |
||||
|
|
||||
|
|
||||
|
class MailMessage(models.Model): |
||||
|
_inherit = 'mail.message' |
||||
|
|
||||
|
is_moved = fields.Boolean('Is moved') |
||||
|
moved_from_res_id = fields.Integer('Related Document ID (Original)') |
||||
|
moved_from_model = fields.Char('Related Document Model (Original)') |
||||
|
moved_from_parent_id = fields.Many2one('mail.message', 'Parent Message (Original)', ondelete='set null') |
||||
|
moved_by_message_id = fields.Many2one('mail.message', 'Moved by message', ondelete='set null', help='Top message, that initate moving this message') |
||||
|
moved_by_user_id = fields.Many2one('res.users', 'Moved by user', ondelete='set null') |
||||
|
all_child_ids = fields.One2many('mail.message', string='All childs', compute='_get_all_childs', help='all childs, including subchilds') |
||||
|
|
||||
|
@api.one |
||||
|
def _get_all_childs(self, include_myself=True): |
||||
|
ids = [] |
||||
|
if include_myself: |
||||
|
ids.append(self.id) |
||||
|
while True: |
||||
|
new_ids = self.search([('parent_id', 'in', ids), ('id', 'not in', ids)]).ids |
||||
|
if new_ids: |
||||
|
ids = ids + new_ids |
||||
|
continue |
||||
|
break |
||||
|
moved_childs = self.search([('moved_by_message_id', '=', self.id)]).ids |
||||
|
self.all_child_ids = ids + moved_childs |
||||
|
|
||||
|
@api.multi |
||||
|
def move_followers(self, model, ids): |
||||
|
fol_obj = self.env['mail.followers'] |
||||
|
for message in self: |
||||
|
followers = fol_obj.sudo().search([('res_model', '=', message.model), |
||||
|
('res_id', '=', message.res_id)]) |
||||
|
for f in followers: |
||||
|
self.env[model].browse(ids).message_subscribe([f.partner_id.id], [s.id for s in f.subtype_ids]) |
||||
|
|
||||
|
@api.one |
||||
|
def move(self, parent_id, res_id, model, move_back, move_followers=False): |
||||
|
if parent_id == res_id: |
||||
|
return |
||||
|
vals = {} |
||||
|
if move_back: |
||||
|
# clear variables if we move everything back |
||||
|
vals['is_moved'] = False |
||||
|
vals['moved_by_user_id'] = None |
||||
|
vals['moved_by_message_id'] = None |
||||
|
|
||||
|
vals['moved_from_res_id'] = None |
||||
|
vals['moved_from_model'] = None |
||||
|
vals['moved_from_parent_id'] = None |
||||
|
else: |
||||
|
vals['parent_id'] = parent_id |
||||
|
vals['res_id'] = res_id |
||||
|
vals['model'] = model |
||||
|
|
||||
|
vals['is_moved'] = True |
||||
|
vals['moved_by_user_id'] = self.env.user.id |
||||
|
vals['moved_by_message_id'] = self.id |
||||
|
|
||||
|
# Update record_name in message |
||||
|
vals['record_name'] = self._get_record_name(vals) |
||||
|
|
||||
|
for r in self.all_child_ids: |
||||
|
r_vals = vals.copy() |
||||
|
if not r.is_moved: |
||||
|
# moved_from_* variables contain not last, but original |
||||
|
# reference |
||||
|
r_vals['moved_from_parent_id'] = r.parent_id.id |
||||
|
r_vals['moved_from_res_id'] = r.res_id |
||||
|
r_vals['moved_from_model'] = r.model |
||||
|
elif move_back: |
||||
|
r_vals['parent_id'] = r.moved_from_parent_id.id |
||||
|
r_vals['res_id'] = r.moved_from_res_id |
||||
|
r_vals['model'] = r.moved_from_model |
||||
|
# print 'update message', r, r_vals |
||||
|
if move_followers: |
||||
|
r.sudo().move_followers(r_vals.get('model'), r_vals.get('res_id')) |
||||
|
r.sudo().write(r_vals) |
||||
|
r.attachment_ids.sudo().write({ |
||||
|
'res_id': r_vals.get('res_id'), |
||||
|
'res_model': r_vals.get('model') |
||||
|
}) |
||||
|
|
||||
|
# Send notification |
||||
|
notification = { |
||||
|
'id': self.id, |
||||
|
'res_id': vals.get('res_id'), |
||||
|
'model': vals.get('model'), |
||||
|
'is_moved': vals['is_moved'], |
||||
|
'record_name': vals['record_name'] |
||||
|
} |
||||
|
self.env['bus.bus'].sendone((self._cr.dbname, 'mail_move_message'), notification) |
||||
|
|
||||
|
def name_get(self, cr, uid, ids, context=None): |
||||
|
if not (context or {}).get('extended_name'): |
||||
|
return super(MailMessage, self).name_get(cr, uid, ids, context=context) |
||||
|
if isinstance(ids, (list, tuple)) and not len(ids): |
||||
|
return [] |
||||
|
if isinstance(ids, (long, int)): |
||||
|
ids = [ids] |
||||
|
reads = self.read(cr, uid, ids, ['record_name', 'model', 'res_id'], context=context) |
||||
|
res = [] |
||||
|
for record in reads: |
||||
|
name = record['record_name'] or '' |
||||
|
extended_name = ' [%s] ID %s' % (record.get('model', 'UNDEF'), record.get('res_id', 'UNDEF')) |
||||
|
res.append((record['id'], name + extended_name)) |
||||
|
return res |
||||
|
|
||||
|
def _message_read_dict(self, cr, uid, message, parent_id=False, context=None): |
||||
|
res = super(MailMessage, self)._message_read_dict(cr, uid, message, parent_id, context) |
||||
|
res['is_moved'] = message.is_moved |
||||
|
return res |
||||
|
|
||||
|
@api.multi |
||||
|
def message_format(self): |
||||
|
message_values = super(MailMessage, self).message_format() |
||||
|
message_index = {message['id']: message for message in message_values} |
||||
|
for item in self: |
||||
|
msg = message_index.get(item.id) |
||||
|
if msg: |
||||
|
msg['is_moved'] = item.is_moved |
||||
|
return message_values |
||||
|
|
||||
|
|
||||
|
class MailMoveMessageConfiguration(models.TransientModel): |
||||
|
_name = 'mail_move_message.config.settings' |
||||
|
_inherit = 'res.config.settings' |
||||
|
|
||||
|
model_ids = fields.Many2many(comodel_name='ir.model', string='Models') |
||||
|
move_followers = fields.Boolean('Move Followers') |
||||
|
|
||||
|
@api.model |
||||
|
def get_default_move_message_configs(self, fields): |
||||
|
config_parameters = self.env['ir.config_parameter'] |
||||
|
model_obj = self.env['ir.model'] |
||||
|
model_names = config_parameters.get_param('mail_relocation_models') |
||||
|
if not model_names: |
||||
|
return {} |
||||
|
model_names = model_names.split(',') |
||||
|
model_ids = model_obj.search([('model', 'in', model_names)]) |
||||
|
return { |
||||
|
'model_ids': [m.id for m in model_ids], |
||||
|
'move_followers': config_parameters.get_param('mail_relocation_move_followers') |
||||
|
} |
||||
|
|
||||
|
@api.multi |
||||
|
def set_move_message_configs(self): |
||||
|
config_parameters = self.env['ir.config_parameter'] |
||||
|
model_names = '' |
||||
|
for record in self: |
||||
|
model_names = ','.join([m.model for m in record.model_ids]) |
||||
|
config_parameters.set_param('mail_relocation_models', model_names) |
||||
|
config_parameters.set_param('mail_relocation_move_followers', record.move_followers or '') |
||||
|
|
||||
|
|
||||
|
class ResPartner(models.Model): |
||||
|
_inherit = 'res.partner' |
||||
|
|
||||
|
@api.model |
||||
|
def create(self, vals): |
||||
|
res = super(ResPartner, self).create(vals) |
||||
|
if 'update_message_author' in self.env.context and 'email' in vals: |
||||
|
mail_message_obj = self.env['mail.message'] |
||||
|
# Escape special SQL characters in email_address to avoid invalid matches |
||||
|
email_address = (vals['email'].replace('\\', '\\\\').replace('%', '\\%').replace('_', '\\_')) |
||||
|
email_brackets = "<%s>" % email_address |
||||
|
messages = mail_message_obj.search([ |
||||
|
'|', |
||||
|
('email_from', '=ilike', email_address), |
||||
|
('email_from', 'ilike', email_brackets), |
||||
|
('author_id', '=', False) |
||||
|
]) |
||||
|
if messages: |
||||
|
messages.sudo().write({'author_id': res.id}) |
||||
|
return res |
@ -0,0 +1,122 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<openerp><data> |
||||
|
|
||||
|
<template id="assets_backend" name="custom bar assets" inherit_id="web.assets_backend"> |
||||
|
<xpath expr="." position="inside"> |
||||
|
<link rel="stylesheet" href="/mail_move_message/static/src/css/mail_move_message.css"/> |
||||
|
<script type="text/javascript" src="/mail_move_message/static/src/js/mail_move_message.js"></script> |
||||
|
</xpath> |
||||
|
</template> |
||||
|
|
||||
|
<record id="view_wizard" model="ir.ui.view"> |
||||
|
<field name="name">mail_move_message.wizard.view</field> |
||||
|
<field name="model">mail_move_message.wizard</field> |
||||
|
<field name="arch" type="xml"> |
||||
|
<form string="Move Message"> |
||||
|
|
||||
|
<field name="can_move" invisible="1"/> |
||||
|
<field name="message_is_moved" invisible="1"/> |
||||
|
<field name="message_name_from" invisible="1"/> |
||||
|
<field name="message_email_from" invisible="1"/> |
||||
|
<field name="message_to_read" invisible="1"/> |
||||
|
<field name="uid" invisible="1"/> |
||||
|
|
||||
|
<p attrs="{'invisible':[('can_move', '!=', False)]}">You cannot move this message. It was already moved with a message bellow. Open one and apply changes there.</p> |
||||
|
<group attrs="{'invisible':[('can_move', '!=', False)]}"> |
||||
|
<field name="message_moved_by_message_id" context="{'extended_name':1}"/> |
||||
|
<field name="message_moved_by_user_id"/> |
||||
|
<button name="open_moved_by_message_id" string="Open message" type="object" class="oe_highlight"/> |
||||
|
</group> |
||||
|
|
||||
|
<group attrs="{'invisible':[('can_move', '=', False)]}" colspan="2"> |
||||
|
<label for="model"/> |
||||
|
<div> |
||||
|
<field name="model" widget="polymorphic" polymorphic="res_id" class="oe_inline"/> |
||||
|
</div> |
||||
|
<label for="filter_by_partner"/> |
||||
|
<div> |
||||
|
<field name="filter_by_partner"/> |
||||
|
<field name="partner_id" style="width:50%"/> |
||||
|
<button string="Create Partner" attrs="{'invisible':[('partner_id','!=',False)]}" class="oe_highlight" special="quick_create" field="partner_id" context="{'force_email':True,'default_email':message_email_from,'default_name':message_name_from, 'update_message_author':True}"/> |
||||
|
</div> |
||||
|
<label for="res_id"/> |
||||
|
<div> |
||||
|
<field name="res_id" context="{'extended_name_with_contact':1}" widget="many2one" attrs="{'readonly': [('model','=',False)]}" style="width:50%"/> |
||||
|
<button string="Create new record" name="create_record" type="object" class="oe_highlight" attrs="{'invisible':['|',('model','=',False)]}" special="quick_create" field="res_id" use_for_mail_move_message="True"/> |
||||
|
</div> |
||||
|
<label for="move_back" attrs="{'invisible':[('message_is_moved','=',False)]}"/> |
||||
|
<div attrs="{'invisible':[('message_is_moved','=',False)]}"> |
||||
|
<field name="move_back"/> |
||||
|
</div> |
||||
|
<label for="move_followers"/> |
||||
|
<div> |
||||
|
<field name="move_followers"/> |
||||
|
</div> |
||||
|
</group> |
||||
|
<button name="move" string="Move" type="object" class="oe_highlight" attrs="{'invisible':[('can_move', '=', False)]}"/> |
||||
|
<button string="Close" class="" special="cancel" /> |
||||
|
<separator string="Message"/> |
||||
|
<group> |
||||
|
<field name="message_subject"/> |
||||
|
<field name="message_from"/> |
||||
|
<field name="message_id" invisible="1"/> |
||||
|
</group> |
||||
|
<div class="openerp mail_move_message"> |
||||
|
<div class="oe_mail"> |
||||
|
<div class="oe_msg"> |
||||
|
<div class="oe_msg_content"> |
||||
|
<div class="oe_msg_body"> |
||||
|
<!-- use built-in css for messages --> |
||||
|
<field name="message_body"/> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
<footer> |
||||
|
<button name="read_close" string="Mark as read and Close" type="object" class="oe_highlight"/> or |
||||
|
<button special="cancel" string="Close" class="oe_link"/> |
||||
|
|
||||
|
|
||||
|
<button name="delete" string="Delete message" type="object" class="oe_highlight oe_right" confirm="Do you really want to delete this message?" attrs="{'invisible':[('uid','!=',1)]}"/> |
||||
|
</footer> |
||||
|
</form> |
||||
|
</field> |
||||
|
</record> |
||||
|
|
||||
|
<!-- Relocation config wizard --> |
||||
|
<record id="view_mail_move_message_config_settings" model="ir.ui.view"> |
||||
|
<field name="name">relocation settings</field> |
||||
|
<field name="model">mail_move_message.config.settings</field> |
||||
|
<field name="arch" type="xml"> |
||||
|
<form string="Configure Mail Relocation" class="oe_form_configuration"> |
||||
|
<header> |
||||
|
<button string="Apply" type="object" name="execute" class="oe_highlight"/> |
||||
|
or |
||||
|
<button string="Cancel" type="object" name="cancel" class="oe_link"/> |
||||
|
</header> |
||||
|
<div name="general"> |
||||
|
<separator string="Models"/> |
||||
|
<field name="model_ids" widget="many2many_tags"/> |
||||
|
<separator string="Options"/> |
||||
|
<label for="move_followers"/> |
||||
|
<field name="move_followers"/> |
||||
|
</div> |
||||
|
</form> |
||||
|
</field> |
||||
|
</record> |
||||
|
|
||||
|
<record id="action_mail_move_message_config" model="ir.actions.act_window"> |
||||
|
<field name="name">Mail Relocation</field> |
||||
|
<field name="type">ir.actions.act_window</field> |
||||
|
<field name="res_model">mail_move_message.config.settings</field> |
||||
|
<field name="view_id" ref="view_mail_move_message_config_settings"/> |
||||
|
<field name="view_mode">form</field> |
||||
|
<field name="target">inline</field> |
||||
|
</record> |
||||
|
|
||||
|
<!-- Add menu entry in Settings/Email --> |
||||
|
<menuitem name="Mail Relocation" id="menu_mail_move_message" parent="base.menu_email" sequence="99" action="action_mail_move_message_config"/> |
||||
|
|
||||
|
</data> |
||||
|
</openerp> |
After Width: 1012 | Height: 546 | Size: 39 KiB |
After Width: 1012 | Height: 546 | Size: 40 KiB |
After Width: 1012 | Height: 546 | Size: 39 KiB |
After Width: 890 | Height: 473 | Size: 73 KiB |
After Width: 149 | Height: 149 | Size: 1.5 KiB |
After Width: 1054 | Height: 562 | Size: 57 KiB |