Merge PR #664 into 13.0

Signed-off-by hbrunn
pull/665/head
OCA-git-bot 2021-01-27 18:34:46 +00:00
commit 929ec8f264
110 changed files with 2658 additions and 1926 deletions

View File

@ -0,0 +1,13 @@
# Do NOT update manually; changes here will be overwritten by Copier
_commit: v1.1.3
_src_path: https://github.com/OCA/oca-addons-repo-template.git
dependency_installation_mode: OCA
generate_requirements_txt: true
include_wkhtmltopdf: false
odoo_version: 13.0
rebel_module_groups: []
repo_description: Addons concerning Odoo's social features and messaging in general.
repo_name: Social addons for Odoo
repo_slug: social
travis_apt_packages: []
travis_apt_sources: []

View File

@ -7,11 +7,11 @@ indent_style = space
insert_final_newline = true
trim_trailing_whitespace = true
[.eslintrc,*.{json,yml,yaml,rst,md}]
[*.{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]
[{*/static/{lib,src/lib}/**,*/static/description/index.html,*/readme/../README.rst}]
charset = unset
end_of_line = unset
indent_size = unset

291
.eslintrc
View File

@ -1,291 +0,0 @@
{
"globals": {
"$": false,
"_": false,
"fuzzy": false,
"jQuery": false,
"moment": false,
"odoo": false,
"openerp": false,
"self": false
},
"env": {
"browser": true
},
"rules": {
"no-alert": "warn",
"no-array-constructor": "warn",
"no-bitwise": "off",
"no-caller": "warn",
"no-case-declarations": "warn",
"no-catch-shadow": "warn",
"no-class-assign": "warn",
"no-cond-assign": "warn",
"no-confusing-arrow": "warn",
"no-console": "off",
"no-const-assign": "warn",
"no-constant-condition": "warn",
"no-continue": "off",
"no-control-regex": "warn",
"no-debugger": "warn",
"no-delete-var": "warn",
"no-div-regex": "warn",
"no-dupe-args": "warn",
"no-dupe-class-members": "warn",
"no-dupe-keys": "warn",
"no-duplicate-case": "warn",
"no-duplicate-imports": "warn",
"no-else-return": "warn",
"no-empty": "warn",
"no-empty-character-class": "warn",
"no-empty-function": "warn",
"no-empty-pattern": "warn",
"no-eq-null": "warn",
"no-eval": "warn",
"no-ex-assign": "warn",
"no-extend-native": "warn",
"no-extra-bind": "warn",
"no-extra-boolean-cast": "warn",
"no-extra-label": "warn",
"no-extra-parens": "warn",
"no-extra-semi": "warn",
"no-fallthrough": "warn",
"no-floating-decimal": "warn",
"no-func-assign": "warn",
"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-invalid-this": "off",
"no-irregular-whitespace": "warn",
"no-iterator": "warn",
"no-label-var": "warn",
"no-labels": "warn",
"no-lone-blocks": "warn",
"no-lonely-if": "warn",
"no-loop-func": "off",
"no-magic-numbers": "off",
"no-mixed-operators": "warn",
"no-mixed-requires": "warn",
"no-mixed-spaces-and-tabs": "warn",
"no-multi-spaces": "warn",
"no-multi-str": "warn",
"no-multiple-empty-lines": "warn",
"no-native-reassign": "warn",
"no-negated-condition": "warn",
"no-negated-in-lhs": "warn",
"no-nested-ternary": "off",
"no-new": "warn",
"no-new-func": "warn",
"no-new-object": "warn",
"no-new-require": "warn",
"no-new-symbol": "warn",
"no-new-wrappers": "warn",
"no-obj-calls": "warn",
"no-octal": "warn",
"no-octal-escape": "warn",
"no-param-reassign": "warn",
"no-path-concat": "warn",
"no-plusplus": "off",
"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": "warn",
"no-script-url": "warn",
"no-self-assign": "warn",
"no-self-compare": "warn",
"no-sequences": "warn",
"no-shadow": "warn",
"no-shadow-restricted-names": "warn",
"no-whitespace-before-property": "warn",
"no-spaced-func": "warn",
"no-sparse-arrays": "warn",
"no-sync": "warn",
"no-tabs": "warn",
"no-ternary": "off",
"no-trailing-spaces": "warn",
"no-this-before-super": "warn",
"no-throw-literal": "warn",
"no-undef": "warn",
"no-undef-init": "warn",
"no-undefined": "off",
"no-unexpected-multiline": "warn",
"no-underscore-dangle": "off",
"no-unmodified-loop-condition": "warn",
"no-unneeded-ternary": "warn",
"no-unreachable": "warn",
"no-unsafe-finally": "warn",
"no-unused-expressions": "warn",
"no-unused-labels": "warn",
"no-unused-vars": "warn",
"no-use-before-define": "warn",
"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-var": "off",
"no-warning-comments": "off",
"no-with": "warn",
"array-bracket-spacing": "off",
"array-callback-return": "warn",
"arrow-body-style": "warn",
"arrow-parens": "warn",
"arrow-spacing": "off",
"accessor-pairs": "warn",
"block-scoped-var": "off",
"block-spacing": ["warn", "always"],
"brace-style": "warn",
"callback-return": "warn",
"camelcase": "off",
"capitalized-comments": ["warn", "always", {
"ignoreConsecutiveComments": true,
"ignoreInlineComments": true
}],
"comma-dangle": ["warn", "always-multiline"],
"comma-spacing": ["warn", {
"before": false,
"after": true
}],
"comma-style": "warn",
"complexity": [
"warn",
15
],
"computed-property-spacing": "off",
"consistent-return": "off",
"consistent-this": "off",
"constructor-super": "warn",
"curly": "warn",
"default-case": "off",
"dot-location": ["warn", "property"],
"dot-notation": "warn",
"eol-last": "warn",
"eqeqeq": "warn",
"func-names": "off",
"func-style": "off",
"generator-star-spacing": "off",
"global-require": "warn",
"guard-for-in": "off",
"handle-callback-err": "warn",
"id-blacklist": "warn",
"id-length": "off",
"id-match": "warn",
"indent": "warn",
"init-declarations": "warn",
"jsx-quotes": "warn",
"key-spacing": "off",
"keyword-spacing": "warn",
"linebreak-style": [
"warn",
"unix"
],
"lines-around-comment": "warn",
"max-depth": "warn",
"max-len": ["warn", {
"code": 88,
"ignorePattern": "odoo\\.define\\(",
"tabWidth": 4
}],
"max-lines": "off",
"max-nested-callbacks": "warn",
"max-params": "off",
"max-statements": "off",
"max-statements-per-line": "warn",
"multiline-ternary": "off",
"new-cap": "off",
"new-parens": "warn",
"newline-after-var": "off",
"newline-before-return": "off",
"newline-per-chained-call": "off",
"object-curly-newline": ["warn", { "consistent": true }],
"object-curly-spacing": ["warn", "never"],
"object-property-newline": ["warn", {
"allowAllPropertiesOnSameLine": true
}],
"object-shorthand": "off",
"one-var": "off",
"one-var-declaration-per-line": "off",
"operator-assignment": "warn",
"operator-linebreak": "warn",
"padded-blocks": "off",
"prefer-arrow-callback": "off",
"prefer-const": "warn",
"prefer-reflect": "off",
"prefer-rest-params": "off",
"prefer-spread": "off",
"prefer-template": "off",
"quote-props": "off",
"quotes": "off",
"radix": "warn",
"require-yield": "warn",
"rest-spread-spacing": "off",
"semi": [
"warn",
"always"
],
"semi-spacing": "warn",
"sort-imports": "warn",
"sort-vars": "off",
"space-before-blocks": "warn",
"space-before-function-paren": "warn",
"space-in-parens": "off",
"space-infix-ops": "off",
"space-unary-ops": "off",
"spaced-comment": ["warn", "always"],
"strict": ["warn", "function"],
"template-curly-spacing": "off",
"unicode-bom": "warn",
"use-isnan": "warn",
"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",
"vars-on-top": "off",
"wrap-iife": "warn",
"wrap-regex": "warn",
"yield-star-spacing": "off",
"yoda": "warn"
},
"parserOptions": {
"ecmaVersion": 2017
}
}

180
.eslintrc.yml 100644
View File

@ -0,0 +1,180 @@
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
# Styling is handled by Prettier, so we only need to enable AST rules;
# see https://github.com/OCA/maintainer-quality-tools/pull/618#issuecomment-558576890
rules:
accessor-pairs: warn
array-callback-return: warn
callback-return: warn
capitalized-comments:
- warn
- always
- ignoreConsecutiveComments: true
ignoreInlineComments: true
complexity:
- warn
- 15
constructor-super: warn
dot-notation: warn
eqeqeq: warn
global-require: warn
handle-callback-err: warn
id-blacklist: warn
id-match: warn
init-declarations: error
max-depth: warn
max-nested-callbacks: warn
max-statements-per-line: warn
no-alert: warn
no-array-constructor: warn
no-caller: warn
no-case-declarations: warn
no-class-assign: warn
no-cond-assign: error
no-const-assign: error
no-constant-condition: warn
no-control-regex: warn
no-debugger: error
no-delete-var: warn
no-div-regex: warn
no-dupe-args: error
no-dupe-class-members: error
no-dupe-keys: error
no-duplicate-case: error
no-duplicate-imports: error
no-else-return: warn
no-empty-character-class: warn
no-empty-function: error
no-empty-pattern: error
no-empty: warn
no-eq-null: error
no-eval: error
no-ex-assign: error
no-extend-native: warn
no-extra-bind: warn
no-extra-boolean-cast: warn
no-extra-label: warn
no-fallthrough: warn
no-func-assign: error
no-global-assign: error
no-implicit-coercion:
- warn
- allow: ["~"]
no-implicit-globals: warn
no-implied-eval: warn
no-inline-comments: warn
no-inner-declarations: warn
no-invalid-regexp: warn
no-irregular-whitespace: warn
no-iterator: warn
no-label-var: warn
no-labels: warn
no-lone-blocks: warn
no-lonely-if: error
no-mixed-requires: error
no-multi-str: warn
no-native-reassign: error
no-negated-condition: warn
no-negated-in-lhs: error
no-new-func: warn
no-new-object: warn
no-new-require: warn
no-new-symbol: warn
no-new-wrappers: warn
no-new: warn
no-obj-calls: warn
no-octal-escape: warn
no-octal: warn
no-param-reassign: warn
no-path-concat: warn
no-process-env: warn
no-process-exit: warn
no-proto: warn
no-prototype-builtins: warn
no-redeclare: warn
no-regex-spaces: warn
no-restricted-globals: warn
no-restricted-imports: warn
no-restricted-modules: warn
no-restricted-syntax: warn
no-return-assign: error
no-script-url: warn
no-self-assign: warn
no-self-compare: warn
no-sequences: warn
no-shadow-restricted-names: warn
no-shadow: warn
no-sparse-arrays: warn
no-sync: warn
no-this-before-super: warn
no-throw-literal: warn
no-undef-init: warn
no-undef: error
no-unmodified-loop-condition: warn
no-unneeded-ternary: error
no-unreachable: error
no-unsafe-finally: error
no-unused-expressions: error
no-unused-labels: error
no-unused-vars: error
no-use-before-define: error
no-useless-call: warn
no-useless-computed-key: warn
no-useless-concat: warn
no-useless-constructor: warn
no-useless-escape: warn
no-useless-rename: warn
no-void: warn
no-with: warn
operator-assignment: [error, always]
prefer-const: warn
radix: warn
require-yield: warn
sort-imports: warn
spaced-comment: [error, always]
strict: [error, function]
use-isnan: error
valid-jsdoc:
- warn
- prefer:
arg: param
argument: param
augments: extends
constructor: class
exception: throws
func: function
method: function
prop: property
return: returns
virtual: abstract
yield: yields
preferType:
array: Array
bool: Boolean
boolean: Boolean
number: Number
object: Object
str: String
string: String
requireParamDescription: false
requireReturn: false
requireReturnDescription: false
requireReturnType: false
valid-typeof: warn
yoda: warn

View File

@ -0,0 +1,13 @@
name: pre-commit
on:
pull_request:
push:
jobs:
pre-commit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
- uses: pre-commit/action@v2.0.0

40
.gitignore vendored
View File

@ -1,6 +1,8 @@
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
/.venv
/.pytest_cache
# C extensions
*.so
@ -8,12 +10,11 @@ __pycache__/
# Distribution / packaging
.Python
env/
bin/
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
@ -22,12 +23,7 @@ var/
*.egg-info/
.installed.cfg
*.egg
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
*.eggs
# Installer logs
pip-log.txt
@ -37,11 +33,9 @@ pip-delete-this-directory.txt
htmlcov/
.tox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*,cover
# Translations
*.mo
@ -49,11 +43,33 @@ coverage.xml
# Pycharm
.idea
# Eclipse
.settings
# Visual Studio cache/options directory
.vs/
.vscode
# OSX Files
.DS_Store
# Django stuff:
*.log
# Mr Developer
.mr.developer.cfg
.project
.pydevproject
# Rope
.ropeproject
# Sphinx documentation
docs/_build/
# PyBuilder
target/
# Backup files
*~
*.swp
# OCA rules
!static/lib/

View File

@ -9,4 +9,4 @@ line_length=88
known_odoo=odoo
known_odoo_addons=odoo.addons
sections=FUTURE,STDLIB,THIRDPARTY,ODOO,ODOO_ADDONS,FIRSTPARTY,LOCALFOLDER
known_third_party=lxml,mock,psycopg2,requests,setuptools,werkzeug
default_section=THIRDPARTY

View File

@ -1,69 +1,128 @@
exclude: "^setup/|/static/lib/|/static/src/lib/"
exclude: |
(?x)
# NOT INSTALLABLE ADDONS
# END NOT INSTALLABLE ADDONS
# Files and folders generated by bots, to avoid loops
^setup/|/static/description/index\.html$|
# We don't want to mess with tool-generated files
.svg$|
# 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/psf/black
rev: 19.3b0
hooks:
- id: black
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v2.3.0
hooks:
- id: trailing-whitespace
# exclude autogenerated files
exclude: /README\.rst$|\.pot?$
- id: end-of-file-fixer
# exclude autogenerated files
exclude: /README\.rst$|\.pot?$
- id: debug-statements
- id: flake8
name: flake8 except __init__.py
exclude: /__init__\.py$
additional_dependencies: ["flake8-bugbear==19.8.0"]
- id: flake8
name: flake8 only __init__.py
args: ["--extend-ignore=F401"] # ignore unused imports in __init__.py
files: /__init__\.py$
additional_dependencies: ["flake8-bugbear==19.8.0"]
- 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/pre-commit/mirrors-pylint
rev: v2.3.1
hooks:
- id: pylint
name: pylint with optional checks
args: ["--rcfile=.pylintrc", "--exit-zero"]
verbose: true
additional_dependencies: ["pylint-odoo==3.5.0"]
- id: pylint
name: pylint with mandatory checks
args: ["--rcfile=.pylintrc-mandatory"]
additional_dependencies: ["pylint-odoo==3.5.0"]
- repo: https://github.com/asottile/pyupgrade
rev: v1.24.0
hooks:
- id: pyupgrade
- repo: https://github.com/asottile/seed-isort-config
rev: v1.9.3
hooks:
- id: seed-isort-config
- repo: https://github.com/pre-commit/mirrors-isort
rev: v4.3.21
hooks:
- id: isort
name: isort except __init__.py
exclude: /__init__\.py$
- repo: https://github.com/pre-commit/mirrors-eslint
rev: v6.5.1
hooks:
- id: eslint
verbose: true
- repo: local
hooks:
# These files are most likely copier diff rejection junks; if found,
# review them manually, fix the problem (if needed) and remove them
- id: forbidden-files
name: forbidden files
entry: found forbidden files; remove them
language: fail
files: "\\.rej$"
- repo: https://github.com/oca/maintainer-tools
rev: ab1d7f6
hooks:
# update the NOT INSTALLABLE ADDONS section above
- id: oca-update-pre-commit-excluded-addons
- id: oca-fix-manifest-website
args: ["https://github.com/OCA/social"]
- 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: 19.10b0
hooks:
- id: black
- repo: https://github.com/pre-commit/mirrors-prettier
rev: v1.19.1
hooks:
- id: prettier
name: prettier (with plugin-xml)
entry: prettier --write --list-different
additional_dependencies:
- "prettier@1.19.1"
- "@prettier/plugin-xml@0.7.2"
files: \.(css|htm|html|js|json|jsx|less|md|scss|toml|ts|xml|yaml|yml)$
- repo: https://github.com/pre-commit/mirrors-eslint
rev: v6.8.0
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$|\.pot?$
- id: end-of-file-fixer
# exclude autogenerated files
exclude: /README\.rst$|\.pot?$
- 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
# exclude files where underlines are not distinguishable from merge conflicts
exclude: /README\.rst$|^docs/.*\.rst$
- id: check-symlinks
- id: check-xml
- id: mixed-line-ending
args: ["--fix=lf"]
- repo: https://github.com/asottile/pyupgrade
rev: v1.26.2
hooks:
- id: pyupgrade
args: ["--keep-percent-format"]
- repo: https://github.com/pre-commit/mirrors-isort
rev: v4.3.21
hooks:
- id: isort
name: isort except __init__.py
exclude: /__init__\.py$
- repo: https://github.com/acsone/setuptools-odoo
rev: 2.5.2
hooks:
- id: setuptools-odoo-make-default
- repo: https://gitlab.com/pycqa/flake8
rev: 3.7.9
hooks:
- id: flake8
name: flake8 except __init__.py
exclude: /__init__\.py$
additional_dependencies: ["flake8-bugbear==19.8.0"]
- id: flake8
name: flake8 only __init__.py
args: ["--extend-ignore=F401"] # ignore unused imports in __init__.py
files: /__init__\.py$
additional_dependencies: ["flake8-bugbear==19.8.0"]
- repo: https://github.com/pre-commit/mirrors-pylint
rev: v2.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

8
.prettierrc.yml 100644
View File

@ -0,0 +1,8 @@
# Defaults for all prettier-supported languages.
# Prettier will complete this with settings from .editorconfig file.
bracketSpacing: false
printWidth: 88
proseWrap: always
semi: true
trailingComma: "es5"
xmlWhitespaceSensitivity: "strict"

View File

@ -46,8 +46,6 @@ enable=anomalous-backslash-in-string,
method-inverse,
method-required-super,
method-search,
missing-import-error,
missing-manifest-dependency,
openerp-exception-warning,
pointless-statement,
pointless-string-statement,
@ -73,6 +71,7 @@ enable=anomalous-backslash-in-string,
deprecated-module,
file-not-used,
invalid-commit,
missing-manifest-dependency,
missing-newline-extrafiles,
missing-readme,
no-utf8-coding-comment,
@ -82,6 +81,7 @@ enable=anomalous-backslash-in-string,
too-complex,
unnecessary-utf8-coding-comment
[REPORTS]
msg-template={path}:{line}: [{msg_id}({symbol}), {obj}] {msg}
output-format=colorized

View File

@ -39,8 +39,6 @@ enable=anomalous-backslash-in-string,
method-inverse,
method-required-super,
method-search,
missing-import-error,
missing-manifest-dependency,
openerp-exception-warning,
pointless-statement,
pointless-string-statement,

View File

@ -1,8 +1,8 @@
language: python
cache:
directories:
- $HOME/.cache/pip
- $HOME/.cache/pre-commit
- $HOME/.cache/pip
- $HOME/.cache/pre-commit
python:
- "3.6"
@ -11,33 +11,26 @@ addons:
postgresql: "9.6"
apt:
packages:
- expect-dev # provides unbuffer utility
- expect-dev # provides unbuffer utility
stages:
- linting
- test
jobs:
include:
- stage: linting
name: "pre-commit"
before_install:
install: pip install pre-commit
script: pre-commit run --all --show-diff-on-failure --verbose --color always
after_success:
- stage: test
env:
- TESTS="1" ODOO_REPO="odoo/odoo" MAKEPOT="1"
- TESTS=1 ODOO_REPO="odoo/odoo" MAKEPOT="1"
- stage: test
env:
- TESTS="1" ODOO_REPO="OCA/OCB"
- TESTS=1 ODOO_REPO="OCA/OCB"
env:
global:
- VERSION="13.0" TESTS="0" LINT_CHECK="0" MAKEPOT="0"
- VERSION="13.0" TESTS="0" LINT_CHECK="0" MAKEPOT="0"
install:
- git clone --depth=1 https://github.com/OCA/maintainer-quality-tools.git ${HOME}/maintainer-quality-tools
- git clone --depth=1 https://github.com/OCA/maintainer-quality-tools.git
${HOME}/maintainer-quality-tools
- export PATH=${HOME}/maintainer-quality-tools/travis:${PATH}
- travis_install_nightly

10
CONTRIBUTING.md 100644
View File

@ -0,0 +1,10 @@
# OCA Guidelines
Please follow the official guide from the
[OCA Guidelines page](https://odoo-community.org/page/contributing).
## Project Specific Guidelines
<!-- /!\ do not modify above this line -->
This project does not have specific coding guidelines.

10
LICENSE
View File

@ -1,7 +1,7 @@
GNU AFFERO GENERAL PUBLIC LICENSE
Version 3, 19 November 2007
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
@ -633,8 +633,8 @@ the "copyright" line and a pointer to where the full notice is found.
Copyright (C) <year> <name of author>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
@ -643,7 +643,7 @@ the "copyright" line and a pointer to where the full notice is found.
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
along with this program. If not, see <https://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
@ -658,4 +658,4 @@ specific requirements.
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU AGPL, see
<http://www.gnu.org/licenses/>.
<https://www.gnu.org/licenses/>.

View File

@ -1,11 +1,17 @@
[![Runbot Status](https://runbot.odoo-community.org/runbot/badge/flat/205/13.0.svg)](https://runbot.odoo-community.org/runbot/repo/github-com-oca-social-205)
[![Build Status](https://travis-ci.org/OCA/social.svg?branch=13.0)](https://travis-ci.org/OCA/social)
[![Coverage Status](https://coveralls.io/repos/OCA/social/badge.svg?branch=13.0)](https://coveralls.io/r/OCA/social?branch=13.0)
[![Build Status](https://travis-ci.com/OCA/social.svg?branch=13.0)](https://travis-ci.com/OCA/social)
[![codecov](https://codecov.io/gh/OCA/social/branch/13.0/graph/badge.svg)](https://codecov.io/gh/OCA/social)
[![Translation Status](https://translation.odoo-community.org/widgets/social-13-0/-/svg-badge.svg)](https://translation.odoo-community.org/engage/social-13-0/?utm_source=widget)
Social addons for Odoo
======================
<!-- /!\ do not modify above this line -->
Addons concerning Odoo's social ERP features and messaging in general
# Social addons for Odoo
Addons concerning Odoo's social features and messaging in general.
<!-- /!\ do not modify below this line -->
<!-- prettier-ignore-start -->
[//]: # (addons)
@ -44,7 +50,18 @@ addon | version | summary
[//]: # (end addons)
Translation Status
------------------
<!-- prettier-ignore-end -->
[![Translation status](https://translation.odoo-community.org/widgets/social-13-0/-/multi-auto.svg)](https://translation.odoo-community.org/engage/social-13-0/?utm_source=widget)
## Licenses
This repository is licensed under [AGPL-3.0](LICENSE).
However, each module can have a totally different license, as long as they adhere to OCA
policy. Consult each module's `__manifest__.py` file, which contains a `license` key
that explains its license.
----
OCA, or the [Odoo Community Association](http://odoo-community.org/), is a nonprofit
organization whose mission is to support the collaborative development of Odoo features
and promote its widespread use.

View File

@ -1,34 +1,44 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8" ?>
<odoo noupdate="1">
<record id="subject_gin_idx" model="trgm.index">
<field name="index_type">gin</field>
<field name="field_id"
search="[('model','=','mail.message'),('name','=','subject')]"/>
<field
name="field_id"
search="[('model','=','mail.message'),('name','=','subject')]"
/>
</record>
<record id="body_gin_idx" model="trgm.index">
<field name="index_type">gin</field>
<field name="field_id"
search="[('model','=','mail.message'),('name','=','body')]"/>
<field
name="field_id"
search="[('model','=','mail.message'),('name','=','body')]"
/>
</record>
<record id="record_name_gin_idx" model="trgm.index">
<field name="index_type">gin</field>
<field name="field_id"
search="[('model','=','mail.message'),('name','=','record_name')]"/>
<field
name="field_id"
search="[('model','=','mail.message'),('name','=','record_name')]"
/>
</record>
<record id="email_from_gin_idx" model="trgm.index">
<field name="index_type">gin</field>
<field name="field_id"
search="[('model','=','mail.message'),('name','=','email_from')]"/>
<field
name="field_id"
search="[('model','=','mail.message'),('name','=','email_from')]"
/>
</record>
<record id="reply_to_gin_idx" model="trgm.index">
<field name="index_type">gin</field>
<field name="field_id"
search="[('model','=','mail.message'),('name','=','reply_to')]"/>
<field
name="field_id"
search="[('model','=','mail.message'),('name','=','reply_to')]"
/>
</record>
</odoo>

View File

@ -1,9 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<template id="view_email_template_corporate_identity">
<body>
<html>
<img style="float: right" t-attf-src="data:image;base64,{{env.user.company_id.logo}}" />
<img
style="float: right"
t-attf-src="data:image;base64,{{env.user.company_id.logo}}"
/>
<!-- if some template calling us sets this variable,
we print a h1 tag /-->
<h1 t-if="email_heading"><t t-esc="email_heading" /></h1>

View File

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<record id="email_template_demo1" model="mail.template">
<field name="name">QWeb demo</field>

View File

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<record id="email_template_form" model="ir.ui.view">
<field name="model">mail.template</field>
@ -9,12 +9,21 @@
</field>
<field name="body_html" position="before">
<group attrs="{'invisible': [('body_type', '!=', 'qweb')]}">
<field name="body_view_id" attrs="{'required': [('body_type', '=', 'qweb')]}" />
<field name="body_view_arch" widget="ace" attrs="{'required': [('body_type', '=', 'qweb')], 'invisible': [('body_view_id', '=', False)]}" />
<field
name="body_view_id"
attrs="{'required': [('body_type', '=', 'qweb')]}"
/>
<field
name="body_view_arch"
widget="ace"
attrs="{'required': [('body_type', '=', 'qweb')], 'invisible': [('body_view_id', '=', False)]}"
/>
</group>
</field>
<field name="body_html" position="attributes">
<attribute name="attrs">{'invisible': [('body_type', '!=', 'jinja2')]}</attribute>
<attribute
name="attrs"
>{'invisible': [('body_type', '!=', 'jinja2')]}</attribute>
</field>
</field>
</record>

View File

@ -5,7 +5,7 @@
"summary": "Post unkonwn messages to an existing thread",
"version": "13.0.1.0.0",
"category": "Discuss",
"website": "https://www.github.com/social",
"website": "https://github.com/OCA/social",
"author": "Tecnativa, Odoo Community Association (OCA)",
"license": "AGPL-3",
"application": False,

View File

@ -1,23 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8" ?>
<!-- Copyright 2017 Tecnativa - Jairo Llopis <jairo.llopis@tecnativa.com>
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -->
<odoo>
<record id="demo_sink" model="mail.channel">
<field name="name">mailsink</field>
<field name="email_send" eval="True"/>
<field name="email_send" eval="True" />
<field name="description">Unbounded email sink</field>
<field name="alias_contact">everyone</field>
<field name="public">private</field>
<field name="group_ids" eval="[(4, ref('base.group_user'))]"/>
<field name="group_ids" eval="[(4, ref('base.group_user'))]" />
</record>
<record id="demo_server" model="fetchmail.server">
<field name="name">Demo server</field>
<field name="server_type">pop</field>
<field name="server">pop3.example.com</field>
<field name="default_thread_id" eval="'mail.channel,%d' % ref('demo_sink')"/>
<field name="default_thread_id" eval="'mail.channel,%d' % ref('demo_sink')" />
<!-- <field name="default_thread_id">mail.channel,%(demo_sink)d</field> -->
</record>

View File

@ -1,16 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8" ?>
<!-- Copyright 2017 Tecnativa - Jairo Llopis <jairo.llopis@tecnativa.com>
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -->
<odoo>
<record id="view_email_server_form" model="ir.ui.view">
<field name="name">Add default thread</field>
<field name="model">fetchmail.server</field>
<field name="inherit_id" ref="fetchmail.view_email_server_form"/>
<field name="inherit_id" ref="fetchmail.view_email_server_form" />
<field name="arch" type="xml">
<xpath expr="//field[@name='object_id']" position="after">
<field name="default_thread_id"/>
<field name="default_thread_id" />
</xpath>
</field>
</record>

View File

@ -1,14 +1,13 @@
/* Copyright 2018 David Juaneda
* License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). */
odoo.define('mail.Chatter.activity', function (require) {
odoo.define("mail.Chatter.activity", function(require) {
"use strict";
var chatter = require('mail.Chatter');
var chatter = require("mail.Chatter");
chatter.include({
events: _.extend({}, chatter.prototype.events, {
'click .o_chatter_button_list_activity': '_onListActivity',
"click .o_chatter_button_list_activity": "_onListActivity",
}),
/**
@ -16,18 +15,17 @@ odoo.define('mail.Chatter.activity', function (require) {
*
* @private
*/
_onListActivity: function () {
_onListActivity: function() {
this._rpc({
model: this.record.model,
method: 'redirect_to_activities',
method: "redirect_to_activities",
args: [[]],
kwargs: {
'id':this.record.res_id,
'model':this.record.model,
id: this.record.res_id,
model: this.record.model,
},
context: this.record.getContext(),
}).then($.proxy(this, "do_action"));
},
});
});

View File

@ -1,11 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<?xml version="1.0" encoding="UTF-8" ?>
<templates id="template" xml:space="preserve">
<t t-extend="mail.chatter.Buttons">
<t t-jquery="button.o_chatter_button_schedule_activity" t-operation="after">
<button t-if="scheduleActivityButton" class="btn btn-link o_chatter_button_list_activity"
title="See activities list" type="button" disabled="disabled">
<i class="fa fa-list"/> Activities
<button
t-if="scheduleActivityButton"
class="btn btn-link o_chatter_button_list_activity"
title="See activities list"
type="button"
disabled="disabled"
>
<i class="fa fa-list" /> Activities
</button>
</t>
</t>

View File

@ -12,38 +12,57 @@
<field name="arch" type="xml">
<form string="Activity Form" create="false" edit="false" delete="false">
<sheet string="Activity">
<button name="open_origin" type="object" class="centre oe_link" nolabel="1">
<h1><field name="res_name"/></h1>
<button
name="open_origin"
type="object"
class="centre oe_link"
nolabel="1"
>
<h1><field name="res_name" /></h1>
</button>
<field name="activity_category" invisible="1" />
<field name="res_model" invisible="1"/>
<field name="res_model_id" invisible="1"/>
<field name="res_id" invisible="1"/>
<field name="res_model" invisible="1" />
<field name="res_model_id" invisible="1" />
<field name="res_id" invisible="1" />
<group>
<group>
<field name="activity_type_id" required="1" options="{'no_create': True, 'no_open': True}"/>
<field name="res_model_id_name"/>
<field name="calendar_event_id" invisible="1"/>
<field name="create_date" invisible="1"/>
<field
name="activity_type_id"
required="1"
options="{'no_create': True, 'no_open': True}"
/>
<field name="res_model_id_name" />
<field name="calendar_event_id" invisible="1" />
<field name="create_date" invisible="1" />
</group>
<group>
<field name="date_deadline"
attrs="{'invisible': [('activity_category', '=', 'meeting')]}"/>
<field name="calendar_event_id_start" string="Start meeting"
attrs="{'invisible': [('calendar_event_id','=', False)]}"/>
<field name="duration" widget="float_time"
attrs="{'invisible': ['|',('duration', '=', False),
('calendar_event_id','=', False)]}"/>
<field name="user_id" options="{'no_open': True}"/>
<field
name="date_deadline"
attrs="{'invisible': [('activity_category', '=', 'meeting')]}"
/>
<field
name="calendar_event_id_start"
string="Start meeting"
attrs="{'invisible': [('calendar_event_id','=', False)]}"
/>
<field
name="duration"
widget="float_time"
attrs="{'invisible': ['|',('duration', '=', False),
('calendar_event_id','=', False)]}"
/>
<field name="user_id" options="{'no_open': True}" />
</group>
</group>
<group attrs="{'invisible': ['|',('calendar_event_id','=', False),('calendar_event_id_partner_ids','=', False)]}">
<field name="calendar_event_id_partner_ids" mode="kanban"/>
<group
attrs="{'invisible': ['|',('calendar_event_id','=', False),('calendar_event_id_partner_ids','=', False)]}"
>
<field name="calendar_event_id_partner_ids" mode="kanban" />
</group>
<group>
<field name="summary" placeholder="e.g. Discuss proposal"/>
<field name="note" placeholder="Log a note..."/>
<field name="summary" placeholder="e.g. Discuss proposal" />
<field name="note" placeholder="Log a note..." />
</group>
</sheet>
</form>
@ -55,13 +74,19 @@
<record id="mail_activity_view_tree" model="ir.ui.view">
<field name="name">mail.activity.boards.view.tree</field>
<field name="model">mail.activity</field>
<field name="inherit_id" ref="mail.mail_activity_view_tree"/>
<field name="inherit_id" ref="mail.mail_activity_view_tree" />
<field name="arch" type="xml">
<xpath expr="//tree" position="attributes">
<attribute name="default_order"/>
<attribute name="decoration-danger">(date_deadline &lt; current_date)</attribute>
<attribute name="decoration-warning">(date_deadline == current_date)</attribute>
<attribute name="decoration-success">(date_deadline &gt; current_date)</attribute>
<attribute name="default_order" />
<attribute
name="decoration-danger"
>(date_deadline &lt; current_date)</attribute>
<attribute
name="decoration-warning"
>(date_deadline == current_date)</attribute>
<attribute
name="decoration-success"
>(date_deadline &gt; current_date)</attribute>
</xpath>
</field>
@ -72,66 +97,103 @@
<record id="mail_activity_view_kanban" model="ir.ui.view">
<field name="name">mail.activity.boards.view.kanban</field>
<field name="model">mail.activity</field>
<field name="priority" eval="10"/>
<field name="priority" eval="10" />
<field name="arch" type="xml">
<kanban default_group_by="activity_type_id" class="_kanban_small_column o_opportunity_kanban" create="0" _order="date_deadline"
group_create="false" group_delete="false" group_edit="false">
<field name="user_id"/>
<field name="res_id"/>
<field name="res_name"/>
<field name="res_model"/>
<field name="summary"/>
<field name="date_deadline"/>
<field name="state"/>
<field name="icon"/>
<field name="activity_type_id"/>
<field name="activity_category"/>
<kanban
default_group_by="activity_type_id"
class="_kanban_small_column o_opportunity_kanban"
create="0"
_order="date_deadline"
group_create="false"
group_delete="false"
group_edit="false"
>
<field name="user_id" />
<field name="res_id" />
<field name="res_name" />
<field name="res_model" />
<field name="summary" />
<field name="date_deadline" />
<field name="state" />
<field name="icon" />
<field name="activity_type_id" />
<field name="activity_category" />
<templates>
<t t-name="kanban-box">
<div t-attf-class="oe_kanban_content oe_kanban_global_click">
<div class="oe_kanban_content">
<div>
<strong class="o_kanban_record_subtitle">
<span t-attf-class="fa #{record.icon.raw_value}" />
<field name="summary"/>
<span
t-attf-class="fa #{record.icon.raw_value}"
/>
<field name="summary" />
</strong>
</div>
<div>
<strong class="o_kanban_record_title"><field name="res_name"/></strong>
<strong class="o_kanban_record_title"><field
name="res_name"
/></strong>
</div>
<div class="o_kanban_record_bottom">
<div class="oe_kanban_bottom_left">
<t t-set="act_date" t-value="new Date(record.date_deadline.raw_value)"/>
<t
t-set="act_date"
t-value="new Date(record.date_deadline.raw_value)"
/>
<t t-if="act_date &lt; (new Date())">
<span t-attf-class="text-danger"><i class="fa fa-clock-o"/></span>
<t t-if="record.activity_category.raw_value!='meeting'">
<span t-attf-class="text-danger"><i
class="fa fa-clock-o"
/></span>
<t
t-if="record.activity_category.raw_value!='meeting'"
>
<span t-attf-class="text-danger">
<field name="date_deadline" t-options='{"widget": "date"}'/>
<field
name="date_deadline"
t-options='{"widget": "date"}'
/>
</span>
</t>
<t t-else="">
<span t-attf-class="text-danger">
<field name="calendar_event_id_start" t-options='{"widget": "date"}'/>
<field
name="calendar_event_id_start"
t-options='{"widget": "date"}'
/>
</span>
</t>
</t>
<t t-else="">
<span><i class="fa fa-clock-o"/></span>
<t t-if="record.activity_category.raw_value!='meeting'">
<span><i class="fa fa-clock-o" /></span>
<t
t-if="record.activity_category.raw_value!='meeting'"
>
<span>
<field name="date_deadline" t-options='{"widget": "date"}'/>
<field
name="date_deadline"
t-options='{"widget": "date"}'
/>
</span>
</t>
<t t-else="">
<field name="calendar_event_id_start" t-options='{"widget": "date"}'/>
<field
name="calendar_event_id_start"
t-options='{"widget": "date"}'
/>
</t>
</t>
</div>
<div class="oe_kanban_bottom_right">
<img t-att-src="kanban_image('res.users', 'image_small', record.user_id.raw_value)"
t-att-title="record.user_id.value"
t-att-alt="record.user_id.value" width="24" height="24" class="oe_kanban_avatar"/>
<img
t-att-src="kanban_image('res.users', 'image_small', record.user_id.raw_value)"
t-att-title="record.user_id.value"
t-att-alt="record.user_id.value"
width="24"
height="24"
class="oe_kanban_avatar"
/>
</div>
</div>
</div>
@ -147,28 +209,42 @@
<record id="mail_activity_view_search" model="ir.ui.view">
<field name="name">mail.activity.boards.view.search</field>
<field name="model">mail.activity</field>
<field name="inherit_id" ref="mail.mail_activity_view_search"/>
<field name="priority" eval="2"/>
<field name="inherit_id" ref="mail.mail_activity_view_search" />
<field name="priority" eval="2" />
<field name="mode">primary</field>
<field name="arch" type="xml">
<xpath expr='//field[@name="res_model_id"]' position='before'>
<field name="user_id"/>
<field name="res_name" string="Origin"/>
<field name="user_id" />
<field name="res_name" string="Origin" />
</xpath>
<xpath expr='//filter[@name="activities_overdue"]' position='before'>
<filter string="Act. next month" name="activities_month"
domain="[('date_deadline', '&lt;', (context_today()+datetime.timedelta(days=30)).strftime('%Y-%m-%d'))]"
help="Show activities scheduled for next month."/>
<filter string="Act. next 6 months" name="activities_6_month"
domain="[('date_deadline', '&lt;', (context_today()+datetime.timedelta(days=180)).strftime('%Y-%m-%d'))]"
help="Show activities scheduled for next 6 months."/>
<separator/>
<filter
string="Act. next month"
name="activities_month"
domain="[('date_deadline', '&lt;', (context_today()+datetime.timedelta(days=30)).strftime('%Y-%m-%d'))]"
help="Show activities scheduled for next month."
/>
<filter
string="Act. next 6 months"
name="activities_6_month"
domain="[('date_deadline', '&lt;', (context_today()+datetime.timedelta(days=180)).strftime('%Y-%m-%d'))]"
help="Show activities scheduled for next 6 months."
/>
<separator />
</xpath>
<xpath expr='//search/group' position='inside'>
<filter string="User" name='assigned_user' context="{'group_by':'user_id'}"/>
<filter string="Origin" name='origin' context="{'group_by': 'res_model_id'}"/>
<filter
string="User"
name='assigned_user'
context="{'group_by':'user_id'}"
/>
<filter
string="Origin"
name='origin'
context="{'group_by': 'res_model_id'}"
/>
</xpath>
</field>
@ -184,15 +260,17 @@
<field name="view_mode">kanban,form</field>
<field name="domain">[]</field>
<field name="context">{}</field>
<field name="view_ids"
eval="[(5, 0, 0),
<field
name="view_ids"
eval="[(5, 0, 0),
(0, 0, {'view_mode': 'kanban', 'view_id': ref('mail_activity_view_kanban')}),
(0, 0, {'view_mode': 'tree', 'view_id': ref('mail_activity_view_tree')}),
(0, 0, {'view_mode': 'form', 'view_id': ref('mail_activity_view_form_board')}),
(0, 0, {'view_mode': 'calendar'}),
(0, 0, {'view_mode': 'pivot'}),
(0, 0, {'view_mode': 'graph'})]"/>
<field name="search_view_id" ref="mail_activity_view_search"/>
(0, 0, {'view_mode': 'graph'})]"
/>
<field name="search_view_id" ref="mail_activity_view_search" />
</record>
@ -200,10 +278,11 @@
Menus
-->
<menuitem
id="board_menu_activities"
name="Activities"
parent="base.menu_board_root"
action="open_boards_activities"
sequence="1"/>
id="board_menu_activities"
name="Activities"
parent="base.menu_board_root"
action="open_boards_activities"
sequence="1"
/>
</odoo>

View File

@ -1,8 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<template id="assets_backend" name="mail_activity_board assets" inherit_id="web.assets_backend">
<template
id="assets_backend"
name="mail_activity_board assets"
inherit_id="web.assets_backend"
>
<xpath expr="." position="inside">
<script type="text/javascript" src="/mail_activity_board/static/src/js/override_chatter.js"/>
<script
type="text/javascript"
src="/mail_activity_board/static/src/js/override_chatter.js"
/>
</xpath>
</template>
</odoo>

View File

@ -6,6 +6,7 @@
"author": "ForgeFlow, Odoo Community Association (OCA)",
"license": "LGPL-3",
"category": "Discuss",
"website": "https://github.com/OCA/social",
"depends": ["mail"],
"data": ["views/templates.xml", "views/mail_activity_views.xml"],
"pre_init_hook": "pre_init_hook",

View File

@ -1,87 +1,86 @@
// Copyright 2018-20 ForgeFlow <http://www.forgeflow.com>
// License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html).
odoo.define('mail.Activity.done', function (require) {
odoo.define("mail.Activity.done", function(require) {
"use strict";
var mailUtils = require('mail.utils');
var core = require('web.core');
var time = require('web.time');
var mail_activity = require('mail.Activity');
var mailUtils = require("mail.utils");
var core = require("web.core");
var time = require("web.time");
var mail_activity = require("mail.Activity");
var QWeb = core.qweb;
var _t = core._t;
// We are forced here to override the method, as there is no possibility
// to inherit it.
var setDelayLabel = function (activities) {
var today = moment().startOf('day');
_.each(activities, function (activity) {
var to_display = '';
var deadline = moment(activity.date_deadline).startOf('day');
var setDelayLabel = function(activities) {
var today = moment().startOf("day");
_.each(activities, function(activity) {
var to_display = "";
var deadline = moment(activity.date_deadline).startOf("day");
// On next line, true means no rounding
var diff = deadline.diff(today, 'days', true);
var diff = deadline.diff(today, "days", true);
if (diff === 0) {
to_display = _t('Today');
} else {
to_display = _t("Today");
} else if (diff < 0) {
// This block is for overdue
if (diff < 0) { // eslint-disable-line no-lonely-if
if (diff === -1) {
to_display = _t('Yesterday');
} else {
to_display = _.str.sprintf(
_t('%d days overdue'), Math.abs(diff)
);
}
// This block is for due
// eslint-disable-line no-lonely-if
if (diff === -1) {
to_display = _t("Yesterday");
} else {
if (diff === 1) { // eslint-disable-line no-lonely-if
to_display = _t('Tomorrow');
} else {
to_display = _.str.sprintf(
_t('Due in %d days'), Math.abs(diff)
);
}
to_display = _.str.sprintf(_t("%d days overdue"), Math.abs(diff));
}
// This block is for due
} else if (diff === 1) {
// eslint-disable-line no-lonely-if
to_display = _t("Tomorrow");
} else {
to_display = _.str.sprintf(_t("Due in %d days"), Math.abs(diff));
}
activity.label_delay = to_display;
});
// We do not want to show the activities that have been completed.
var open_activities = _.filter(activities, function (activity) {
var open_activities = _.filter(activities, function(activity) {
return activity.done !== true;
});
return open_activities;
};
mail_activity.include({
/**
* @override
* @private
*/
_render: function () {
_.each(this._activities, function (activity) {
_render: function() {
_.each(this._activities, function(activity) {
var note = mailUtils.parseAndTransform(
activity.note || '', mailUtils.inline);
var is_blank = (/^\s*$/).test(note);
activity.note || "",
mailUtils.inline
);
var is_blank = /^\s*$/.test(note);
if (is_blank) {
activity.note = '';
activity.note = "";
} else {
activity.note = mailUtils.parseAndTransform(
activity.note, mailUtils.addLink);
activity.note,
mailUtils.addLink
);
}
});
var activities = setDelayLabel(this._activities);
if (activities.length) {
var nbActivities = _.countBy(activities, 'state');
this.$el.html(QWeb.render('mail.activity_items', {
activities: activities,
nbPlannedActivities: nbActivities.planned,
nbTodayActivities: nbActivities.today,
nbOverdueActivities: nbActivities.overdue,
dateFormat: time.getLangDateFormat(),
datetimeFormat: time.getLangDatetimeFormat(),
}));
var nbActivities = _.countBy(activities, "state");
this.$el.html(
QWeb.render("mail.activity_items", {
activities: activities,
nbPlannedActivities: nbActivities.planned,
nbTodayActivities: nbActivities.today,
nbOverdueActivities: nbActivities.overdue,
dateFormat: time.getLangDateFormat(),
datetimeFormat: time.getLangDatetimeFormat(),
})
);
} else {
this.$el.empty();
}

View File

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<!--
Copyright 2018-20 ForgeFlow <http://www.forgeflow.com>
@ -7,15 +7,17 @@
<record id="mail_activity_view_form_popup" model="ir.ui.view">
<field name="name">mail.activity.view.form.popup</field>
<field name="model">mail.activity</field>
<field name="inherit_id" ref="mail.mail_activity_view_form_popup"/>
<field name="inherit_id" ref="mail.mail_activity_view_form_popup" />
<field name="priority">20</field>
<field name="arch" type="xml">
<field name="activity_type_id" position="after">
<field name="state"/>
<field name="date_done"/>
<field name="state" />
<field name="date_done" />
</field>
<button name="action_done" position="attributes">
<attribute name="attrs">{'invisible':[('state', '=', 'done')]}</attribute>
<attribute
name="attrs"
>{'invisible':[('state', '=', 'done')]}</attribute>
</button>
</field>
</record>
@ -23,22 +25,31 @@
<record id="mail_activity_view_search" model="ir.ui.view">
<field name="name">mail.activity.view.search</field>
<field name="model">mail.activity</field>
<field name="inherit_id" ref="mail.mail_activity_view_search"/>
<field name="inherit_id" ref="mail.mail_activity_view_search" />
<field name="arch" type="xml">
<field name="res_model_id" position="after">
<field name="done"/>
<field name="active"/>
<filter string="Completed Activities" name="activities_completed"
domain="[('active', '=', False), ('state', '=', 'done')]"/>
<field name="done" />
<field name="active" />
<filter
string="Completed Activities"
name="activities_completed"
domain="[('active', '=', False), ('state', '=', 'done')]"
/>
</field>
<filter name="activities_overdue" position="attributes">
<attribute name="domain">[('date_deadline', '&lt;', context_today().strftime('%Y-%m-%d'))]</attribute>
<attribute
name="domain"
>[('date_deadline', '&lt;', context_today().strftime('%Y-%m-%d'))]</attribute>
</filter>
<filter name="activities_today" position="attributes">
<attribute name="domain">[('date_deadline', '=', context_today().strftime('%Y-%m-%d'))]</attribute>
<attribute
name="domain"
>[('date_deadline', '=', context_today().strftime('%Y-%m-%d'))]</attribute>
</filter>
<filter name="activities_upcoming_all" position="attributes">
<attribute name="domain">[('date_deadline', '&gt;', context_today().strftime('%Y-%m-%d'))]</attribute>
<attribute
name="domain"
>[('date_deadline', '&gt;', context_today().strftime('%Y-%m-%d'))]</attribute>
</filter>
</field>
</record>
@ -46,22 +57,34 @@
<record id="res_partner_view_search_inherit_mail" model="ir.ui.view">
<field name="name">res.partner.view.search.inherit.mail</field>
<field name="model">res.partner</field>
<field name="inherit_id" ref="mail.res_partner_view_search_inherit_mail"/>
<field name="inherit_id" ref="mail.res_partner_view_search_inherit_mail" />
<field name="arch" type="xml">
<filter name="activities_overdue" position="before">
<filter string="Open Activities" name="activities_open"
domain="[('activity_ids.active', '=', True)]"/>
<filter string="Completed Activities" name="activities_completed"
domain="[('activity_ids.active', '=', False), ('activity_ids.state', '=', 'done')]"/>
<filter
string="Open Activities"
name="activities_open"
domain="[('activity_ids.active', '=', True)]"
/>
<filter
string="Completed Activities"
name="activities_completed"
domain="[('activity_ids.active', '=', False), ('activity_ids.state', '=', 'done')]"
/>
</filter>
<filter name="activities_overdue" position="attributes">
<attribute name="domain">[('activity_ids.date_deadline', '&lt;', context_today().strftime('%Y-%m-%d'))]</attribute>
<attribute
name="domain"
>[('activity_ids.date_deadline', '&lt;', context_today().strftime('%Y-%m-%d'))]</attribute>
</filter>
<filter name="activities_today" position="attributes">
<attribute name="domain">[('activity_ids.date_deadline', '=', context_today().strftime('%Y-%m-%d'))]</attribute>
<attribute
name="domain"
>[('activity_ids.date_deadline', '=', context_today().strftime('%Y-%m-%d'))]</attribute>
</filter>
<filter name="activities_upcoming_all" position="attributes">
<attribute name="domain">[('activity_ids.date_deadline', '&gt;', context_today().strftime('%Y-%m-%d'))]</attribute>
<attribute
name="domain"
>[('activity_ids.date_deadline', '&gt;', context_today().strftime('%Y-%m-%d'))]</attribute>
</filter>
</field>
</record>
@ -69,16 +92,20 @@
<record id="mail_activity_view_tree" model="ir.ui.view">
<field name="name">mail.activity.view.tree</field>
<field name="model">mail.activity</field>
<field name="inherit_id" ref="mail.mail_activity_view_tree"/>
<field name="inherit_id" ref="mail.mail_activity_view_tree" />
<field name="arch" type="xml">
<field name="date_deadline" position="after">
<field name="state"/>
<field name="date_done"/>
<field name="state" />
<field name="date_done" />
</field>
<tree position="attributes">
<attribute name="decoration-muted">state == 'done'</attribute>
<attribute name="decoration-danger">date_deadline &lt; current_date and state != 'done'</attribute>
<attribute name="decoration-success">date_deadline == current_date and state != 'done'</attribute>
<attribute
name="decoration-danger"
>date_deadline &lt; current_date and state != 'done'</attribute>
<attribute
name="decoration-success"
>date_deadline == current_date and state != 'done'</attribute>
</tree>
</field>
</record>
@ -86,12 +113,12 @@
<record id="mail_activity_view_calendar" model="ir.ui.view">
<field name="name">mail.activity.view.calendar</field>
<field name="model">mail.activity</field>
<field name="inherit_id" ref="mail.mail_activity_view_calendar"/>
<field name="priority" eval="2"/>
<field name="inherit_id" ref="mail.mail_activity_view_calendar" />
<field name="priority" eval="2" />
<field name="arch" type="xml">
<field name="summary" position="after">
<field name="state"/>
<field name="date_done"/>
<field name="state" />
<field name="date_done" />
</field>
</field>
</record>

View File

@ -1,12 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<!--
Copyright 2018-20 ForgeFlow <http://www.forgeflow.com>
License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html).
-->
<template id="assets_backend" name="mail_activity_done assets" inherit_id="web.assets_backend">
<template
id="assets_backend"
name="mail_activity_done assets"
inherit_id="web.assets_backend"
>
<xpath expr="." position="inside">
<script type="text/javascript" src="/mail_activity_done/static/src/js/mail_activity.js"/>
<script
type="text/javascript"
src="/mail_activity_done/static/src/js/mail_activity.js"
/>
</xpath>
</template>
</odoo>

View File

@ -8,11 +8,14 @@
<record id="mail_activity_view_form_board" model="ir.ui.view">
<field name="name">mail.activity.boards.view.form</field>
<field name="model">mail.activity</field>
<field name="inherit_id" ref="mail_activity_board.mail_activity_view_form_board"/>
<field
name="inherit_id"
ref="mail_activity_board.mail_activity_view_form_board"
/>
<field name="arch" type="xml">
<field name="activity_type_id" position="before">
<field name="partner_id" readonly="True"/>
<field name="commercial_partner_id"/>
<field name="partner_id" readonly="True" />
<field name="commercial_partner_id" />
</field>
</field>
</record>
@ -22,11 +25,11 @@
<record id="mail_activity_view_tree" model="ir.ui.view">
<field name="name">mail.activity.boards.view.tree</field>
<field name="model">mail.activity</field>
<field name="inherit_id" ref="mail_activity_board.mail_activity_view_tree"/>
<field name="inherit_id" ref="mail_activity_board.mail_activity_view_tree" />
<field name="arch" type="xml">
<field name="activity_type_id" position="before">
<field name="partner_id" readonly="True"/>
<field name="commercial_partner_id"/>
<field name="partner_id" readonly="True" />
<field name="commercial_partner_id" />
</field>
</field>
</record>
@ -36,10 +39,10 @@
<record id="mail_activity_view_kanban" model="ir.ui.view">
<field name="name">mail.activity.boards.view.kanban</field>
<field name="model">mail.activity</field>
<field name="inherit_id" ref="mail_activity_board.mail_activity_view_kanban"/>
<field name="inherit_id" ref="mail_activity_board.mail_activity_view_kanban" />
<field name="arch" type="xml">
<strong class="o_kanban_record_title" position="inside">
<field name="partner_id"/>
<field name="partner_id" />
</strong>
</field>
</record>
@ -49,11 +52,11 @@
<record id="mail_activity_view_search" model="ir.ui.view">
<field name="name">mail.activity.boards.view.search</field>
<field name="model">mail.activity</field>
<field name="inherit_id" ref="mail_activity_board.mail_activity_view_search"/>
<field name="inherit_id" ref="mail_activity_board.mail_activity_view_search" />
<field name="arch" type="xml">
<xpath expr='//field[@name="res_model_id"]' position='before'>
<field name="partner_id"/>
<field name="commercial_partner_id"/>
<field name="partner_id" />
<field name="commercial_partner_id" />
</xpath>
</field>
</record>

View File

@ -1,15 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8" ?>
<odoo noupdate="1">
<record id="mail_activity_rule_my_team" model="ir.rule">
<field name="name">mail.activity: user: my team</field>
<field name="model_id" ref="mail.model_mail_activity"/>
<field name="domain_force">["|", ('team_id', 'in', user.activity_team_ids.ids), "&amp;", ('team_id', '=', False), ('user_id', '=', user.id)]</field>
<field name="groups" eval="[(4, ref('base.group_user'))]"/>
<field name="perm_create" eval="True"/>
<field name="perm_read" eval="False"/>
<field name="perm_write" eval="True"/>
<field name="perm_unlink" eval="True"/>
<field name="model_id" ref="mail.model_mail_activity" />
<field
name="domain_force"
>["|", ('team_id', 'in', user.activity_team_ids.ids), "&amp;", ('team_id', '=', False), ('user_id', '=', user.id)]</field>
<field name="groups" eval="[(4, ref('base.group_user'))]" />
<field name="perm_create" eval="True" />
<field name="perm_read" eval="False" />
<field name="perm_write" eval="True" />
<field name="perm_unlink" eval="True" />
</record>
</odoo>

View File

@ -11,32 +11,46 @@
<field name="arch" type="xml">
<form string="Activity Team">
<header>
<field name="count_missing_activities" invisible="1"/>
<button name="assign_team_to_unassigned_activities" string="Assign to missing activities"
type="object" class="oe_highlight"
groups="base.group_no_one"
attrs="{'invisible': [('count_missing_activities', '=', 0)]}"/>
<field name="count_missing_activities" invisible="1" />
<button
name="assign_team_to_unassigned_activities"
string="Assign to missing activities"
type="object"
class="oe_highlight"
groups="base.group_no_one"
attrs="{'invisible': [('count_missing_activities', '=', 0)]}"
/>
</header>
<sheet string="Activity Team">
<div class="oe_button_box" name="button_box">
<button name="toggle_active" type="object"
class="oe_stat_button" icon="fa-archive">
<field name="active" widget="boolean_button"
options='{"terminology": "archive"}'/></button>
<button
name="toggle_active"
type="object"
class="oe_stat_button"
icon="fa-archive"
>
<field
name="active"
widget="boolean_button"
options='{"terminology": "archive"}'
/></button>
</div>
<group>
<group name="base">
<field name="name"/>
<field name="user_id"/>
<field name="name" />
<field name="user_id" />
</group>
<group name="models">
<field name="res_model_ids" widget="many2many_tags"
options="{'no_create': True, 'no_open': True}"/>
<field
name="res_model_ids"
widget="many2many_tags"
options="{'no_create': True, 'no_open': True}"
/>
</group>
</group>
<notebook>
<page name="members" string="Members">
<field name="member_ids" nolabel="1"/>
<field name="member_ids" nolabel="1" />
</page>
</notebook>
</sheet>
@ -51,8 +65,8 @@
<field name="model">mail.activity.team</field>
<field name="arch" type="xml">
<tree>
<field name="name"/>
<field name="res_model_ids" widget="many2many_tags"/>
<field name="name" />
<field name="res_model_ids" widget="many2many_tags" />
</tree>
</field>
</record>
@ -64,9 +78,9 @@
<field name="model">mail.activity.team</field>
<field name="arch" type="xml">
<search>
<field name="name"/>
<field name="res_model_ids"/>
<field name="member_ids"/>
<field name="name" />
<field name="res_model_ids" />
<field name="member_ids" />
</search>
</field>
</record>
@ -81,7 +95,7 @@
<field name="view_mode">tree,form</field>
<field name="domain">[]</field>
<field name="context">{}</field>
<field name="search_view_id" ref="mail_activity_team_view_search"/>
<field name="search_view_id" ref="mail_activity_team_view_search" />
</record>
@ -89,9 +103,10 @@
Menus
-->
<menuitem
id="menu_mail_activity_team"
name="Activity Teams"
parent="base.menu_email"
action="mail_activity_team_action"/>
id="menu_mail_activity_team"
name="Activity Teams"
parent="base.menu_email"
action="mail_activity_team_action"
/>
</odoo>

View File

@ -1,13 +1,13 @@
<?xml version="1.0"?>
<?xml version="1.0" ?>
<odoo>
<record id="mail_activity_view_form_popup" model="ir.ui.view">
<field name="name">mail.activity.view.form.popup</field>
<field name="model">mail.activity</field>
<field name="inherit_id" ref="mail.mail_activity_view_form_popup"/>
<field name="inherit_id" ref="mail.mail_activity_view_form_popup" />
<field name="arch" type="xml">
<field name="user_id" position="after">
<field name="team_id" options="{'no_create': True, 'no_open': True}"/>
<field name="team_id" options="{'no_create': True, 'no_open': True}" />
</field>
</field>
</record>
@ -15,10 +15,10 @@
<record id="mail_activity_view_tree" model="ir.ui.view">
<field name="name">mail.activity.view.tree</field>
<field name="model">mail.activity</field>
<field name="inherit_id" ref="mail.mail_activity_view_tree"/>
<field name="inherit_id" ref="mail.mail_activity_view_tree" />
<field name="arch" type="xml">
<field name="date_deadline" position="after">
<field name="team_id"/>
<field name="team_id" />
</field>
</field>
</record>
@ -26,10 +26,13 @@
<record id="mail_activity_view_form_board" model="ir.ui.view">
<field name="name">mail.activity.view.form</field>
<field name="model">mail.activity</field>
<field name="inherit_id" ref="mail_activity_board.mail_activity_view_form_board"/>
<field
name="inherit_id"
ref="mail_activity_board.mail_activity_view_form_board"
/>
<field name="arch" type="xml">
<field name="user_id" position="after">
<field name="team_id"/>
<field name="team_id" />
</field>
</field>
</record>
@ -37,15 +40,15 @@
<record id="mail_activity_view_kanban" model="ir.ui.view">
<field name="name">mail.activity.boards.view.kanban</field>
<field name="model">mail.activity</field>
<field name="inherit_id" ref="mail_activity_board.mail_activity_view_kanban"/>
<field name="inherit_id" ref="mail_activity_board.mail_activity_view_kanban" />
<field name="arch" type="xml">
<field name="user_id" position="after">
<field name="team_id"/>
<field name="team_id" />
</field>
<xpath expr="//div[hasclass('oe_kanban_content')]" position="inside">
<br/>
<br />
<div>
Team: <field name="team_id"/>
Team: <field name="team_id" />
</div>
</xpath>
</field>
@ -55,17 +58,21 @@
<record id="mail_activity_view_search" model="ir.ui.view">
<field name="name">mail.activity.boards.view.search</field>
<field name="model">mail.activity</field>
<field name="inherit_id" ref="mail_activity_board.mail_activity_view_search"/>
<field name="priority" eval="2"/>
<field name="inherit_id" ref="mail_activity_board.mail_activity_view_search" />
<field name="priority" eval="2" />
<field name="arch" type="xml">
<xpath expr='//field[@name="user_id"]' position='before'>
<field name="team_id"/>
<field name="team_id" />
</xpath>
<xpath expr='//filter[@name="activities_6_month"]' position='after'>
<filter name="my_team_activities" string="My Team Activities" domain="[('team_id.member_ids', '=', uid)]"/>
<filter
name="my_team_activities"
string="My Team Activities"
domain="[('team_id.member_ids', '=', uid)]"
/>
</xpath>
<group position='inside'>
<filter name='team' string="Team" context="{'group_by': 'team_id'}"/>
<filter name='team' string="Team" context="{'group_by': 'team_id'}" />
</group>
</field>

View File

@ -1,14 +1,14 @@
<?xml version="1.0"?>
<?xml version="1.0" ?>
<odoo>
<!-- Update user form !-->
<record id="view_users_form_activity_teams" model="ir.ui.view">
<field name="name">res.users.form.activity.team</field>
<field name="model">res.users</field>
<field name="inherit_id" ref="base.view_users_form"/>
<field name="inherit_id" ref="base.view_users_form" />
<field name="arch" type="xml">
<data>
<field name="signature" position="before">
<field name="activity_team_ids" widget="many2many_tags"/>
<field name="activity_team_ids" widget="many2many_tags" />
</field>
</data>
</field>

View File

@ -1,15 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<record model="ir.ui.view" id="email_compose_message_wizard_inherit_form">
<field name="name">mail.compose.message.form (mail_attach_existing_attachment)</field>
<field
name="name"
>mail.compose.message.form (mail_attach_existing_attachment)</field>
<field name="model">mail.compose.message</field>
<field name="inherit_id" ref="mail.email_compose_message_wizard_form"/>
<field name="inherit_id" ref="mail.email_compose_message_wizard_form" />
<field name="arch" type="xml">
<xpath expr="//field[@name='attachment_ids']" position="after">
<field name="can_attach_attachment" invisible="1"/>
<field name="can_attach_attachment" invisible="1" />
<div attrs="{'invisible': [('can_attach_attachment', '=', False)]}">
<br />
<field name="object_attachment_ids" widget="many2many_checkboxes" domain="[('res_model', '=', model), ('res_id', '=', res_id)]" />
<field
name="object_attachment_ids"
widget="many2many_checkboxes"
domain="[('res_model', '=', model), ('res_id', '=', res_id)]"
/>
</div>
</xpath>
</field>

View File

@ -9,7 +9,7 @@
"summary": "Remove Odoo branding in sent emails",
"version": "13.0.2.0.1",
"category": "Social Network",
"website": "https://github.com/OCA/social/",
"website": "https://github.com/OCA/social",
"author": "Tecnativa, Eficent, Onestein, Odoo Community Association (OCA)",
"license": "AGPL-3",
"installable": True,

View File

@ -6,6 +6,7 @@
"author": "Therp BV,Odoo Community Association (OCA)",
"license": "AGPL-3",
"category": "Discuss",
"website": "https://github.com/OCA/social",
"summary": "Attach emails to Odoo by dragging them from your desktop",
"depends": ["mail", "web_drop_target"],
"external_dependencies": {"python": ["extract_msg"]},

View File

@ -1,27 +1,27 @@
// -*- coding: utf-8 -*-
/* global base64js:false, Uint8Array:false */
// Copyright 2018 Therp BV <https://therp.nl>
// License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
odoo.define('mail_drop_target', function (require) {
odoo.define("mail_drop_target", function(require) {
"use strict";
var Chatter = require('mail.Chatter');
var web_drop_target = require('web_drop_target');
var Chatter = require("mail.Chatter");
var web_drop_target = require("web_drop_target");
Chatter.include(web_drop_target.DropTargetMixin);
Chatter.include({
_drop_allowed_types: ['message/rfc822'],
_drop_allowed_types: ["message/rfc822"],
_get_record_id: function() {
return this.record.res_id;
},
_handle_drop_items: function (drop_items, e) {
_handle_drop_items: function(drop_items) {
var self = this;
_.each(drop_items, function(item, e) {
return self._handle_file_drop_proxy(item, e);
});
},
_handle_file_drop_proxy: function (item, e) {
_handle_file_drop_proxy: function(item, e) {
var self = this;
var file = item;
if (!file || !(file instanceof Blob)) {
@ -31,20 +31,18 @@ odoo.define('mail_drop_target', function (require) {
reader.onloadend = self.proxy(
_.partial(self._handle_file_drop, file, reader, e)
);
reader.onerror = self.proxy('_file_reader_error_handler');
reader.onerror = self.proxy("_file_reader_error_handler");
reader.readAsArrayBuffer(file);
},
_handle_file_drop: function (drop_file, reader, e) {
_handle_file_drop: function(drop_file, reader) {
var self = this,
mail_processor = '',
data = '';
if (drop_file.name.endsWith('.msg')) {
mail_processor = 'message_process_msg';
data = base64js.fromByteArray(
new Uint8Array(reader.result)
);
mail_processor = "",
data = "";
if (drop_file.name.endsWith(".msg")) {
mail_processor = "message_process_msg";
data = base64js.fromByteArray(new Uint8Array(reader.result));
} else {
mail_processor = 'message_drop';
mail_processor = "message_drop";
var reader_array = new Uint8Array(reader.result);
data = "";
for (var i = 0; i < reader_array.length; i++) {
@ -54,14 +52,14 @@ odoo.define('mail_drop_target', function (require) {
// TODO: read some config parameter if this should set
// some of the context keys to suppress mail.thread's behavior
return this._rpc({
model:'mail.thread',
method:mail_processor,
model: "mail.thread",
method: mail_processor,
args: [this.record.model, data],
kwargs: {
thread_id: this.record.data.id,
},
}).then(function() {
self.trigger_up('reload', {});
self.trigger_up("reload", {});
});
},
});

View File

@ -1,18 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<data>
<record id="res_config_settings_view_form" model="ir.ui.view">
<field name="name">res.config.settings.view.form.inherit.mail</field>
<field name="model">res.config.settings</field>
<field name="inherit_id" ref="mail.res_config_settings_view_form"/>
<field name="inherit_id" ref="mail.res_config_settings_view_form" />
<field name="arch" type="xml">
<div id="emails" position="inside">
<div class="col-xs-12 col-md-6 o_setting_box">
<div class="o_setting_left_pane">
<field name="disable_notify_mail_drop_target"/>
<field name="disable_notify_mail_drop_target" />
</div>
<div class="o_setting_right_pane">
<label string="Disable Mail Drag&amp;Drop Notification" for="disable_notify_mail_drop_target"/>
<label
string="Disable Mail Drag&amp;Drop Notification"
for="disable_notify_mail_drop_target"
/>
<div class="text-muted">
When a user drops an email into an existing thread
the followers of the thread will not be notified.

View File

@ -1,10 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<?xml version="1.0" encoding="UTF-8" ?>
<openerp>
<data>
<template id="assets_backend" name="mail_drop_target assets" inherit_id="web.assets_backend">
<template
id="assets_backend"
name="mail_drop_target assets"
inherit_id="web.assets_backend"
>
<xpath expr="." position="inside">
<script type="text/javascript" src="/mail_drop_target/static/src/js/mail_drop_target.js"></script>
<link rel="stylesheet" href="/mail_drop_target/static/src/css/mail_drop_target.css"/>
<script
type="text/javascript"
src="/mail_drop_target/static/src/js/mail_drop_target.js"
/>
<link
rel="stylesheet"
href="/mail_drop_target/static/src/css/mail_drop_target.css"
/>
</xpath>
</template>
</data>

View File

@ -1,32 +1,33 @@
/* Copyright 2014-2015 Grupo ESOC <http://www.grupoesoc.es>
* License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
*/
odoo.define('mail_full_expand.expand', function (require) {
odoo.define("mail_full_expand.expand", function(require) {
"use strict";
var ThreadWidget = require('mail.widget.Thread');
var ThreadWidget = require("mail.widget.Thread");
ThreadWidget.include({
events: _.defaults({
"click .o_full_expand": "_onClickMessageFullExpand",
}, ThreadWidget.prototype.events),
events: _.defaults(
{
"click .o_full_expand": "_onClickMessageFullExpand",
},
ThreadWidget.prototype.events
),
_onClickMessageFullExpand: function (event) {
_onClickMessageFullExpand: function(event) {
// Get the action data and execute it to open the full view
var do_action = this.do_action,
msg_id = $(event.currentTarget).data('message-id');
msg_id = $(event.currentTarget).data("message-id");
this._rpc({
route: "/web/action/load",
params: {
action_id: "mail_full_expand.mail_message_action",
},
})
.then(function (action) {
action.res_id = msg_id;
do_action(action);
});
}).then(function(action) {
action.res_id = msg_id;
do_action(action);
});
},
});
});

View File

@ -1,10 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<?xml version="1.0" encoding="UTF-8" ?>
<!-- Copyright 2014-2015 Grupo ESOC <http://www.grupoesoc.es>License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -->
<template>
<t t-extend="mail.widget.Thread.Message">
<t t-jquery=".o_mail_info" t-operation="append">
<span class="o_thread_full_spand_icon">
<i class="fa o_full_expand fa-arrows-alt" t-att-data-message-id="message.getID()" title="Fully Expand"/>
<i
class="fa o_full_expand fa-arrows-alt"
t-att-data-message-id="message.getID()"
title="Fully Expand"
/>
</span>
</t>
</t>

View File

@ -1,10 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8" ?>
<!-- Copyright 2014-2015 Grupo ESOC <http://www.grupoesoc.es>License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -->
<odoo>
<template id="assets_backend" inherit_id="web.assets_backend" name="mail_full_expand assets">
<template
id="assets_backend"
inherit_id="web.assets_backend"
name="mail_full_expand assets"
>
<xpath expr="." position="inside">
<link href="/mail_full_expand/static/src/scss/mail_full_expand.scss" rel="stylesheet" type="text/scss"/>
<script src="/mail_full_expand/static/src/js/mail_full_expand.js" type="text/javascript"/>
<link
href="/mail_full_expand/static/src/scss/mail_full_expand.scss"
rel="stylesheet"
type="text/scss"
/>
<script
src="/mail_full_expand/static/src/js/mail_full_expand.js"
type="text/javascript"
/>
</xpath>
</template>
</odoo>

View File

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8" ?>
<!-- Copyright 2014-2015 Grupo ESOC <http://www.grupoesoc.es>License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -->
<odoo>
<record id="mail_message_view_form" model="ir.ui.view">
@ -8,18 +8,18 @@
<field name="arch" type="xml">
<form string="Message">
<group>
<field name="author_id" readonly="1"/>
<field name="email_from" readonly="1"/>
<field name="date" readonly="1"/>
<field name="partner_ids" readonly="1" widget="many2many_tags"/>
<field name="author_id" readonly="1" />
<field name="email_from" readonly="1" />
<field name="date" readonly="1" />
<field name="partner_ids" readonly="1" widget="many2many_tags" />
</group>
<h1>
<field name="subject" readonly="1"/>
<field name="subject" readonly="1" />
</h1>
<field name="body" readonly="1"/>
<field name="attachment_ids" readonly="1" widget="many2many_binary"/>
<field name="body" readonly="1" />
<field name="attachment_ids" readonly="1" widget="many2many_binary" />
<footer>
<button class="btn-secondary" special="cancel" string="Close"/>
<button class="btn-secondary" special="cancel" string="Close" />
</footer>
</form>
</field>
@ -28,7 +28,7 @@
<field name="name">Read Full Email</field>
<field name="res_model">mail.message</field>
<field name="type">ir.actions.act_window</field>
<field name="view_id" ref="mail_message_view_form"/>
<field name="view_id" ref="mail_message_view_form" />
<field name="view_mode">form</field>
<field name="target">new</field>
</record>

View File

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8" ?>
<odoo noupdate="0">
<record id="email_template_demo" model="mail.template">
<field name="name">Inline styles demo</field>

View File

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8" ?>
<odoo noupdate="0">
<template id="demo_email_hello" name="demo email hello">
<html>
@ -48,7 +48,10 @@
<tbody>
<tr>
<td class="left">
<img id="main_logo" t-attf-src="data:image;base64,{{env.user.company_id.logo}}" />
<img
id="main_logo"
t-attf-src="data:image;base64,{{env.user.company_id.logo}}"
/>
</td>
<td class="right">
<span class="date_today" t-esc="time.strftime('%%d %%B %%Y')" />
@ -62,18 +65,23 @@
<p>Hello <span t-field="object.name" />.</p>
</div>
<div id="content">
<p>This e-mail styles are inline rendered although its template defines styles as embedded CSS!</p>
<p
>This e-mail styles are inline rendered although its template defines styles as embedded CSS!</p>
</div>
</main>
<footer id="main_footer">
<div class="company_info">
<p class="website"><a href="https://www.example.com">www.example.com</a></p>
<p class="website"><a
href="https://www.example.com"
>www.example.com</a></p>
<div class="address">
<div t-field="env.user.company_id.partner_id"
t-options='{
<div
t-field="env.user.company_id.partner_id"
t-options='{
"widget": "contact",
"fields": ["name", "address", "phone", "mobile", "email"]
}'/>
}'
/>
</div>
</div>
</footer>

View File

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<template id="email_templates_list">
@ -17,7 +17,8 @@
<li>Copy the link</li>
<li>
Paste into the address bar and change the record ID (the last value in the URL).
<br />If you don't know the ID of the record, just browse to the record via odoo interface and check its ID in the address bar `#id=XXX`.
<br
/>If you don't know the ID of the record, just browse to the record via odoo interface and check its ID in the address bar `#id=XXX`.
</li>
<li>Load it</li>
</ol>
@ -31,10 +32,16 @@
<strong>XMLID:</strong> <span class="xid" t-esc="xid" /><br />
</t>
<strong>Link:</strong>
<a class="preview" t-attf-href="/email-preview/#{model}/#{xid or tmpl.id}/1">
<a
class="preview"
t-attf-href="/email-preview/#{model}/#{xid or tmpl.id}/1"
>
Preview
</a><br />
<a class="preview" t-attf-href="/web#id=#{tmpl.id}&amp;view_type=form&amp;model=mail.template">
<a
class="preview"
t-attf-href="/web#id=#{tmpl.id}&amp;view_type=form&amp;model=mail.template"
>
View in backend
</a>
</li>

View File

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<record model="ir.ui.view" id="email_template_preview_form">
<field name="model">email_template.preview</field>
@ -6,7 +6,7 @@
<field name="arch" type="xml">
<footer position="before">
<hr />
<label for="layout_preview_url"/>
<label for="layout_preview_url" />
<field name="layout_preview_url" widget="url" />
</footer>
</field>

View File

@ -6,7 +6,7 @@
Choose if you want to automatically add new recipients as followers
on mail.compose.message""",
"author": "ACSONE SA/NV," "Odoo Community Association (OCA)",
"website": "http://acsone.eu",
"website": "https://github.com/OCA/social",
"category": "Social Network",
"version": "13.0.1.0.0",
"license": "AGPL-3",

View File

@ -1,12 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<record model="ir.ui.view" id="email_compose_message_wizard_inherit_form">
<field name="name">mail.compose.message.form (mail_optional_autofollow)</field>
<field name="model">mail.compose.message</field>
<field name="inherit_id" ref="mail.email_compose_message_wizard_form"/>
<field name="inherit_id" ref="mail.email_compose_message_wizard_form" />
<field name="arch" type="xml">
<xpath expr="//div[field[@name='partner_ids']]" position="after">
<field name="autofollow_recipients" attrs="{'invisible': [('composition_mode', '=', 'mass_mail')]}"/>
<field
name="autofollow_recipients"
attrs="{'invisible': [('composition_mode', '=', 'mass_mail')]}"
/>
</xpath>
</field>
</record>

View File

@ -1,15 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<record id="email_compose_message_wizard_form" model="ir.ui.view">
<field name="model">mail.compose.message</field>
<field name="inherit_id" ref="mail.email_compose_message_wizard_form"/>
<field name="inherit_id" ref="mail.email_compose_message_wizard_form" />
<field name="arch" type="xml">
<span name="document_followers_text" position="before">
<field name="notify_followers" attrs="{'invisible': [('composition_mode', '=', 'mass_mail')]}"/>
<field
name="notify_followers"
attrs="{'invisible': [('composition_mode', '=', 'mass_mail')]}"
/>
</span>
<span name="document_followers_text" position="after">
<span name="no_followers_text" attrs="{'invisible': [('notify_followers', '=', True)]}" style="color: red;"> - Warning : Followers will not be notified but they can access the notification directly from the document (if they are allowed to)</span>
<span
name="no_followers_text"
attrs="{'invisible': [('notify_followers', '=', True)]}"
style="color: red;"
> - Warning : Followers will not be notified but they can access the notification directly from the document (if they are allowed to)</span>
</span>
</field>
</record>

View File

@ -1,10 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8" ?>
<!--
Copyright 2017 LasLabs Inc.
License LGPL-3 or later (http://www.gnu.org/licenses/lgpl.html).
-->
<odoo>
<record id="ir_mail_server_form" model="ir.ui.view">
@ -13,8 +11,8 @@
<field name="inherit_id" ref="base.ir_mail_server_form" />
<field name="arch" type="xml">
<xpath expr="//field[@name='smtp_pass']" position="after">
<field name="domain_whitelist"/>
<field name="smtp_from" widget="email"/>
<field name="domain_whitelist" />
<field name="smtp_from" widget="email" />
</xpath>
</field>
</record>

View File

@ -3,27 +3,24 @@
License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).
**********************************************************************************/
odoo.define("audio_file.preview", function (require) {
odoo.define("audio_file.preview", function(require) {
"use strict";
var preview = require("mail_preview_base.preview");
var DocumentViewer = preview.DocumentViewer;
DocumentViewer.include({
_checkAttachment: function (attachment) {
if (
attachment.type !== 'url' && attachment.mimetype.match("audio")
) {
attachment.type = 'audio';
_checkAttachment: function(attachment) {
if (attachment.type !== "url" && attachment.mimetype.match("audio")) {
attachment.type = "audio";
attachment.source_url = this._getImageUrl(attachment);
return true;
}
return this._super.apply(this, arguments);
},
_hasPreview: function (type) {
_hasPreview: function(type) {
var result = this._super.apply(this, arguments);
return result || type === 'audio';
return result || type === "audio";
},
});
});

View File

@ -1,14 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<?xml version="1.0" encoding="UTF-8" ?>
<templates xml:space="preserve">
<t t-extend="DocumentViewer.Content">
<t t-jquery=".fa-video-camera" t-operation="after">
<i class="fa fa-volume-up mr8"
t-if="widget.activeAttachment.type == 'audio'"
role="img" aria-label="Audio" title="Audio"/>
<i
class="fa fa-volume-up mr8"
t-if="widget.activeAttachment.type == 'audio'"
role="img"
aria-label="Audio"
title="Audio"
/>
</t>
<t t-jquery=".o_viewer_video" t-operation="after">
<audio t-if="widget.activeAttachment.type == 'audio'" class="o_viewer_audio" controls="controls">
<source t-attf-src="#{widget.activeAttachment.source_url}" t-att-data-type="widget.activeAttachment.mimetype"/>
<audio
t-if="widget.activeAttachment.type == 'audio'"
class="o_viewer_audio"
controls="controls"
>
<source
t-attf-src="#{widget.activeAttachment.source_url}"
t-att-data-type="widget.activeAttachment.mimetype"
/>
</audio>
</t>
</t>

View File

@ -8,7 +8,10 @@
<odoo>
<template id="assets_backend" name="audio_assets" inherit_id="web.assets_backend">
<xpath expr="//script[last()]" position="after">
<script type="text/javascript" src="/mail_preview_audio/static/src/js/preview.js" />
<script
type="text/javascript"
src="/mail_preview_audio/static/src/js/preview.js"
/>
</xpath>
</template>
</odoo>

View File

@ -3,7 +3,7 @@
License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).
**********************************************************************************/
odoo.define("mail_preview_base.preview", function (require) {
odoo.define("mail_preview_base.preview", function(require) {
"use strict";
var DocumentViewer = require("mail.DocumentViewer");
@ -12,12 +12,13 @@ odoo.define("mail_preview_base.preview", function (require) {
var AttachmentBox = require("mail.AttachmentBox");
DocumentViewer.include({
init: function (parent, attachments) {
init: function(parent, attachments) {
this._super.apply(this, arguments);
var self = this;
_.forEach(this.attachment, function (attachment) {
if (attachment.mimetype === 'application/pdf' ||
attachment.type === 'text'
_.forEach(this.attachment, function(attachment) {
if (
attachment.mimetype === "application/pdf" ||
attachment.type === "text"
) {
attachment.source_url = self._getContentUrl(attachment);
} else {
@ -25,7 +26,7 @@ odoo.define("mail_preview_base.preview", function (require) {
}
});
this.attachment = this.attachment.concat(
_.filter(attachments, function (attachment) {
_.filter(attachments, function(attachment) {
return self._checkAttachment(attachment);
})
);
@ -35,41 +36,48 @@ odoo.define("mail_preview_base.preview", function (require) {
This function is a hook, it will allow to define new kind of
records
*/
_checkAttachment: function () {
_checkAttachment: function() {
return false;
},
_getContentUrl: function (attachment) {
return "/web/content/" + attachment.id +"?filename=" +
window.encodeURIComponent(attachment.name);
_getContentUrl: function(attachment) {
return (
"/web/content/" +
attachment.id +
"?filename=" +
window.encodeURIComponent(attachment.name)
);
},
_getImageUrl: function (attachment) {
_getImageUrl: function(attachment) {
return "/web/image/" + attachment.id;
},
_hasPreview: function (type, attachment) {
return type === 'image' ||
type === 'video' ||
attachment.mimetype === 'application/pdf';
_hasPreview: function(type, attachment) {
return (
type === "image" ||
type === "video" ||
attachment.mimetype === "application/pdf"
);
},
});
AttachmentBox.include({
init: function (parent, record, attachments) {
_.each(attachments, function (attachment) {
init: function(parent, record, attachments) {
_.each(attachments, function(attachment) {
attachment.has_preview = DocumentViewer.prototype._hasPreview(
attachment.mimetype && attachment.mimetype.split('/').shift(),
attachment);
attachment.mimetype && attachment.mimetype.split("/").shift(),
attachment
);
});
this._super.apply(this, arguments);
},
});
var FieldPreviewViewer = DocumentViewer.extend({
init: function (parent, attachments, activeAttachmentID, model, field) {
init: function(parent, attachments, activeAttachmentID, model, field) {
this.modelName = model;
this.fieldName = field;
this._super.apply(this, arguments);
},
_onDownload: function (e) {
_onDownload: function(e) {
e.preventDefault();
window.location =
"/web/content/" +
@ -82,19 +90,27 @@ odoo.define("mail_preview_base.preview", function (require) {
"datas" +
"?download=true";
},
_getContentUrl: function (attachment) {
return "/web/content/" +
this.modelName + '/' +
attachment.id + '/' +
_getContentUrl: function(attachment) {
return (
"/web/content/" +
this.modelName +
"/" +
attachment.id +
"/" +
this.fieldName +
"?filename=" +
window.encodeURIComponent(attachment.name);
window.encodeURIComponent(attachment.name)
);
},
_getImageUrl: function (attachment) {
return "/web/image/" +
this.modelName + '/' +
attachment.id + '/' +
this.fieldName;
_getImageUrl: function(attachment) {
return (
"/web/image/" +
this.modelName +
"/" +
attachment.id +
"/" +
this.fieldName
);
},
});
@ -102,7 +118,7 @@ odoo.define("mail_preview_base.preview", function (require) {
events: _.extend({}, basic_fields.FieldBinaryFile.prototype.events, {
"click .preview_file": "_previewFile",
}),
_previewFile: function (event) {
_previewFile: function(event) {
event.stopPropagation();
event.preventDefault();
var attachmentViewer = new FieldPreviewViewer(
@ -114,7 +130,7 @@ odoo.define("mail_preview_base.preview", function (require) {
);
attachmentViewer.appendTo($("body"));
},
_renderReadonly: function () {
_renderReadonly: function() {
this._super.apply(this, arguments);
if (this.value) {
this.attachment = {
@ -125,9 +141,7 @@ odoo.define("mail_preview_base.preview", function (require) {
};
var mimetype = this.recordData.res_mimetype;
var type = mimetype.split("/").shift();
if (
DocumentViewer.prototype._hasPreview(type, this.attachment)
) {
if (DocumentViewer.prototype._hasPreview(type, this.attachment)) {
this.$el.prepend(
$("<span/>").addClass("fa fa-search preview_file")
);
@ -143,5 +157,4 @@ odoo.define("mail_preview_base.preview", function (require) {
FieldPreviewBinary: FieldPreviewBinary,
DocumentViewer: DocumentViewer,
};
});

View File

@ -1,17 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<?xml version="1.0" encoding="UTF-8" ?>
<templates xml:space="preserve">
<t t-extend="DocumentViewer.Content">
<t t-jquery=".o_viewer_img" t-operation="attributes">
<attribute name="t-attf-src">#{widget.activeAttachment.source_url}?unique=1&amp;signature=#{widget.activeAttachment.checksum}</attribute>
<attribute
name="t-attf-src"
>#{widget.activeAttachment.source_url}?unique=1&amp;signature=#{widget.activeAttachment.checksum}</attribute>
</t>
<t t-jquery=".o_viewer_pdf" t-operation="attributes">
<attribute name="t-attf-src">/web/static/lib/pdfjs/web/viewer.html?file=#{widget.activeAttachment.source_url}</attribute>
<attribute
name="t-attf-src"
>/web/static/lib/pdfjs/web/viewer.html?file=#{widget.activeAttachment.source_url}</attribute>
</t>
<t t-jquery=".o_viewer_video > source" t-operation="attributes">
<attribute name="t-attf-src">#{widget.activeAttachment.source_url}</attribute>
<attribute
name="t-attf-src"
>#{widget.activeAttachment.source_url}</attribute>
</t>
<t t-jquery=".o_viewer_text:first" t-operation="attributes">
<attribute name="t-attf-src">#{widget.activeAttachment.source_url}</attribute>
<attribute
name="t-attf-src"
>#{widget.activeAttachment.source_url}</attribute>
</t>
</t>
@ -22,7 +30,7 @@
defined on the preview.
TODO: Set the has_preview on a function...
-->
<t t-set="has_preview" t-value="has_preview || attachment.has_preview"/>
<t t-set="has_preview" t-value="has_preview || attachment.has_preview" />
</t>
</t>
</templates>

View File

@ -6,12 +6,22 @@
-->
<odoo>
<template id="assets_backend" name="mail_preview_base_assets" inherit_id="web.assets_backend">
<template
id="assets_backend"
name="mail_preview_base_assets"
inherit_id="web.assets_backend"
>
<xpath expr="//script[last()]" position="after">
<script type="text/javascript" src="/mail_preview_base/static/src/js/preview.js" />
<script
type="text/javascript"
src="/mail_preview_base/static/src/js/preview.js"
/>
</xpath>
<xpath expr="//link[last()]" position="after">
<link rel="stylesheet" href="/mail_preview_base/static/src/scss/preview.scss" />
<link
rel="stylesheet"
href="/mail_preview_base/static/src/scss/preview.scss"
/>
</xpath>
</template>
</odoo>

View File

@ -8,6 +8,7 @@
"author": "Therp BV,Creu Blanca,Odoo Community Association (OCA)",
"license": "AGPL-3",
"category": "Social Network",
"website": "https://github.com/OCA/social",
"summary": "Define a domain from which followers can be selected",
"depends": ["mail"],
"data": ["data/ir_config_parameter.xml", "data/ir_actions.xml"],

View File

@ -1,7 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<act_window id="action_setup" res_model="ir.config_parameter"
name="Configure the restriction on followers" view_mode="form" />
<act_window
id="action_setup"
res_model="ir.config_parameter"
name="Configure the restriction on followers"
view_mode="form"
/>
<record id="action_setup" model="ir.actions.act_window">
<field name="res_id" ref="parameter_domain" />
</record>

View File

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<?xml version="1.0" encoding="UTF-8" ?>
<odoo noupdate="1">
<record id="parameter_domain" model="ir.config_parameter">
<field name="key">mail_restrict_follower_selection.domain</field>

View File

@ -9,7 +9,7 @@
"summary": "Email tracking system for all mails sent",
"version": "13.0.1.0.6",
"category": "Social Network",
"website": "http://github.com/OCA/social",
"website": "https://github.com/OCA/social",
"author": ("Tecnativa, " "Odoo Community Association (OCA)"),
"license": "AGPL-3",
"application": False,

View File

@ -1,8 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8" ?>
<!-- Copyright 2016 Antonio Espinosa - <antonio.espinosa@tecnativa.com>
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -->
<odoo>
<record forcecreate="True" id="decimal_tracking_timestamp" model="decimal.precision">
<record
forcecreate="True"
id="decimal_tracking_timestamp"
model="decimal.precision"
>
<field name="name">MailTracking Timestamp</field>
<field name="digits">6</field>
</record>

View File

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<data noupdate="0">
@ -8,7 +8,9 @@
<field name="res_id" ref="base.partner_demo" />
<field name="message_type">comment</field>
<field name="subtype_id" ref="mail.mt_comment" />
<field name="email_cc">acc@testmail.com,wood.corner26@example.com,toni.rhodes11@example.com</field>
<field
name="email_cc"
>acc@testmail.com,wood.corner26@example.com,toni.rhodes11@example.com</field>
<field name="mail_tracking_needs_action">1</field>
<field name="body"><![CDATA[<p>This is a message with CC</p>]]></field>
<field name="email_from">wood.corner26@example.com</field>
@ -24,7 +26,7 @@
<field name="recipient">demo@yourcompany.example.com</field>
<field name="sender">wood.corner26@example.com</field>
<field name="state">sent</field>
<field name="time" eval="DateTime.today().strftime('%Y-%m-%d %H:%M')"/>
<field name="time" eval="DateTime.today().strftime('%Y-%m-%d %H:%M')" />
</record>
<!-- Failed Message A -->
@ -48,7 +50,7 @@
<field name="recipient">demo@yourcompany.example.com</field>
<field name="sender">wood.corner26@example.com</field>
<field name="state">error</field>
<field name="time" eval="DateTime.today().strftime('%Y-%m-%d %H:%M')"/>
<field name="time" eval="DateTime.today().strftime('%Y-%m-%d %H:%M')" />
</record>
<!-- Failed Message B -->
@ -72,7 +74,7 @@
<field name="recipient">demo@yourcompany.example.com</field>
<field name="sender">jackson.group82@example.com</field>
<field name="state">error</field>
<field name="time" eval="DateTime.today().strftime('%Y-%m-%d %H:%M')"/>
<field name="time" eval="DateTime.today().strftime('%Y-%m-%d %H:%M')" />
</record>
<!-- Failed Message C -->
@ -96,7 +98,7 @@
<field name="recipient">demo@yourcompany.example.com</field>
<field name="sender">admin@example.com</field>
<field name="state">error</field>
<field name="time" eval="DateTime.today().strftime('%Y-%m-%d %H:%M')"/>
<field name="time" eval="DateTime.today().strftime('%Y-%m-%d %H:%M')" />
</record>
</data>

View File

@ -1,16 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8" ?>
<!-- Copyright 2016 Antonio Espinosa - <antonio.espinosa@tecnativa.com>
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -->
<odoo>
<record model="ir.rule" id="mail_tracking_email_portal_public_rule">
<field name="name">mail_tracking_email: portal/public: read access on my email trackings</field>
<field name="model_id" ref="model_mail_tracking_email"/>
<field
name="name"
>mail_tracking_email: portal/public: read access on my email trackings</field>
<field name="model_id" ref="model_mail_tracking_email" />
<field name="domain_force">[('partner_id', '=', user.partner_id.id)]</field>
<field name="groups" eval="[(4, ref('base.group_portal')), (4, ref('base.group_public'))]"/>
<field name="perm_create" eval="False"/>
<field name="perm_unlink" eval="False"/>
<field name="perm_write" eval="False"/>
<field
name="groups"
eval="[(4, ref('base.group_portal')), (4, ref('base.group_public'))]"
/>
<field name="perm_create" eval="False" />
<field name="perm_unlink" eval="False" />
<field name="perm_write" eval="False" />
</record>
</odoo>

View File

@ -31,7 +31,7 @@
}
i.fa-caret-down:before {
content: '\f0da';
content: "\f0da";
}
}
}
@ -52,7 +52,7 @@
}
.o_thread_typing_notification_free_space {
flex-grow: 1,
flex-grow: 1;
}
.o_thread_typing_notification_bar {
@ -60,7 +60,7 @@
background-color: rgba($white, 0.75);
padding: 5px;
text-align: center;
color: gray('600');
color: gray("600");
&.o_thread_order_asc {
@include o-position-sticky($bottom: 0px);
@ -83,7 +83,7 @@
margin-top: 0px;
margin-bottom: 15px;
}
border-bottom: 1px solid gray('400');
border-bottom: 1px solid gray("400");
text-align: center;
.o_thread_date {
@ -116,8 +116,8 @@
margin-bottom: 0px;
&.o_mail_not_discussion {
background-color: rgba(gray('300'), 0.5);
border-bottom: 1px solid gray('400');
background-color: rgba(gray("300"), 0.5);
border-bottom: 1px solid gray("400");
}
.o_thread_message_sidebar {
@ -155,7 +155,8 @@
}
}
&:hover, &.o_thread_selected_message {
&:hover,
&.o_thread_selected_message {
.o_thread_message_side_date {
opacity: $o-mail-thread-side-date-opacity;
}
@ -182,8 +183,6 @@
text-align: justify;
}
.o_mail_subject {
font-style: italic;
}
@ -193,7 +192,8 @@
color: gray;
}
[summary~=o_mail_notification] { // name conflicts with channel notifications, but is odoo notification buttons to hide in chatter if present
[summary~="o_mail_notification"] {
// name conflicts with channel notifications, but is odoo notification buttons to hide in chatter if present
display: none;
}
@ -224,7 +224,10 @@
}
}
.o_thread_message_star, .o_thread_message_needaction, .o_thread_message_reply, .o_thread_message_email {
.o_thread_message_star,
.o_thread_message_needaction,
.o_thread_message_reply,
.o_thread_message_email {
padding: 4px;
}
@ -232,14 +235,16 @@
&.o_thread_message_email_ready {
color: grey;
}
&.o_thread_message_email_exception, &.o_thread_message_email_bounce {
&.o_thread_message_email_exception,
&.o_thread_message_email_bounce {
color: red;
opacity: 1;
cursor: pointer;
}
}
.o_attachments_list, .o_attachments_previews {
.o_attachments_list,
.o_attachments_previews {
&:last-child {
margin-bottom: $grid-gutter-width;
}
@ -299,7 +304,7 @@
.o_activity_info {
vertical-align: baseline;
padding: 4px 6px;
background: theme-color('light');
background: theme-color("light");
border-radius: 2px 2px 0 0;
@include o-hover-opacity(1, 1);

View File

@ -1,6 +1,6 @@
/* Copyright 2019 Alexandre Díaz
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). */
odoo.define('mail_tracking.FailedMessageDiscuss', function (require) {
odoo.define("mail_tracking.FailedMessageDiscuss", function(require) {
"use strict";
// To be considered:
@ -9,45 +9,40 @@ odoo.define('mail_tracking.FailedMessageDiscuss', function (require) {
// - A mailbox is a type of thread that is displayed on top of
// the discuss menu, has a counter, etc...
var MailManagerNotif = require('mail.Manager.Notification');
var AbstractMessage = require('mail.model.AbstractMessage');
var Message = require('mail.model.Message');
var Discuss = require('mail.Discuss');
var MailManager = require('mail.Manager');
var Mailbox = require('mail.model.Mailbox');
var core = require('web.core');
var session = require('web.session');
var MailManagerNotif = require("mail.Manager.Notification");
var AbstractMessage = require("mail.model.AbstractMessage");
var Message = require("mail.model.Message");
var Discuss = require("mail.Discuss");
var MailManager = require("mail.Manager");
var Mailbox = require("mail.model.Mailbox");
var core = require("web.core");
var session = require("web.session");
var QWeb = core.qweb;
var _t = core._t;
/* The states to consider a message as failed message */
var FAILED_STATES = [
'error', 'rejected', 'spam', 'bounced', 'soft-bounced',
];
var FAILED_STATES = ["error", "rejected", "spam", "bounced", "soft-bounced"];
AbstractMessage.include({
/**
* Abstract declaration to know if a message is included in the
* failed mailbox. By default it should be false.
*
* @returns {Boolean}
*/
isFailed: function () {
isFailed: function() {
return false;
},
});
Message.include({
/**
* Overrides to store information from server
*
* @override
*/
init: function (parent, data) {
init: function(parent, data) {
this._isFailedMessage = data.is_failed_message;
return this._super.apply(this, arguments);
},
@ -58,8 +53,8 @@ odoo.define('mail_tracking.FailedMessageDiscuss', function (require) {
*
* @override
*/
isFailed: function () {
return _.contains(this._threadIDs, 'mailbox_failed');
isFailed: function() {
return _.contains(this._threadIDs, "mailbox_failed");
},
/**
@ -67,11 +62,11 @@ odoo.define('mail_tracking.FailedMessageDiscuss', function (require) {
*
* @param {Boolean} failed
*/
setFailed: function (failed) {
setFailed: function(failed) {
if (failed) {
this._addThread('mailbox_failed');
this._addThread("mailbox_failed");
} else {
this.removeThread('mailbox_failed');
this.removeThread("mailbox_failed");
}
},
@ -80,21 +75,20 @@ odoo.define('mail_tracking.FailedMessageDiscuss', function (require) {
*
* @override
*/
_processMailboxes: function () {
_processMailboxes: function() {
this.setFailed(this._isFailedMessage);
return this._super.apply(this, arguments);
},
});
MailManagerNotif.include({
/**
* Overrides to handle changes in the 'mail_tracking_needs_action' flag
*
* @override
*/
_handlePartnerNotification: function (data) {
if (data.type === 'toggle_tracking_status') {
_handlePartnerNotification: function(data) {
if (data.type === "toggle_tracking_status") {
this._handleChangeTrackingNeedsActionNotification(data);
} else {
// Workaround to avoid call '_handlePartnerChannelNotification'
@ -113,24 +107,23 @@ odoo.define('mail_tracking.FailedMessageDiscuss', function (require) {
* @private
* @param {Object} data
*/
_handleChangeTrackingNeedsActionNotification: function (data) {
_handleChangeTrackingNeedsActionNotification: function(data) {
var self = this;
var failed = this.getMailbox('failed');
_.each(data.message_ids, function (messageID) {
var message = _.find(self._messages, function (msg) {
var failed = this.getMailbox("failed");
_.each(data.message_ids, function(messageID) {
var message = _.find(self._messages, function(msg) {
return msg.getID() === messageID;
});
if (message) {
message.setFailed(data.needs_actions);
if (message.isFailed() === false) {
self._removeMessageFromThread(
'mailbox_failed', message);
self._removeMessageFromThread("mailbox_failed", message);
} else {
self._addMessageToThreads(message, []);
var channelFailed = self.getMailbox('failed');
var channelFailed = self.getMailbox("failed");
channelFailed.invalidateCaches();
}
self._mailBus.trigger('update_message', message, data.type);
self._mailBus.trigger("update_message", message, data.type);
}
});
@ -143,14 +136,14 @@ odoo.define('mail_tracking.FailedMessageDiscuss', function (require) {
}
// Trigger event to refresh threads
this._mailBus.trigger('update_failed', failed.getMailboxCounter());
this._mailBus.trigger("update_failed", failed.getMailboxCounter());
},
});
Discuss.include({
events: _.extend({}, Discuss.prototype.events, {
'click .o_failed_message_retry': '_onRetryFailedMessage',
'click .o_failed_message_reviewed': '_onMarkFailedMessageReviewed',
"click .o_failed_message_retry": "_onRetryFailedMessage",
"click .o_failed_message_reviewed": "_onMarkFailedMessageReviewed",
}),
/**
@ -159,8 +152,8 @@ odoo.define('mail_tracking.FailedMessageDiscuss', function (require) {
* @private
* @returns {Object}
*/
_sidebarQWebParams: function () {
var failed = this.call('mail_service', 'getMailbox', 'failed');
_sidebarQWebParams: function() {
var failed = this.call("mail_service", "getMailbox", "failed");
return {
activeThreadID: this._thread ? this._thread.getID() : undefined,
failedCounter: failed.getMailboxCounter(),
@ -173,14 +166,16 @@ odoo.define('mail_tracking.FailedMessageDiscuss', function (require) {
*
* @override
*/
_renderSidebar: function () {
_renderSidebar: function() {
var $sidebar = this._super.apply(this, arguments);
// Because Odoo implementation isn't designed to be inherited
// properly, we inject 'failed' button using jQuery.
var $failed_item = $(QWeb.render('mail_tracking.SidebarFailed',
this._sidebarQWebParams()));
var $failed_item = $(
QWeb.render("mail_tracking.SidebarFailed", this._sidebarQWebParams())
);
$failed_item.insertAfter(
$sidebar.find(".o_mail_discuss_title_main").filter(":last"));
$sidebar.find(".o_mail_discuss_title_main").filter(":last")
);
return $sidebar;
},
@ -190,11 +185,11 @@ odoo.define('mail_tracking.FailedMessageDiscuss', function (require) {
*
* @override
*/
_renderSidebarMailboxes: function () {
_renderSidebarMailboxes: function() {
this._super.apply(this, arguments);
this.$('.o_mail_discuss_sidebar_mailboxes').append(
QWeb.render('mail_tracking.SidebarFailed',
this._sidebarQWebParams()));
this.$(".o_mail_discuss_sidebar_mailboxes").append(
QWeb.render("mail_tracking.SidebarFailed", this._sidebarQWebParams())
);
},
/**
@ -202,12 +197,15 @@ odoo.define('mail_tracking.FailedMessageDiscuss', function (require) {
*
* @override
*/
_renderButtons: function () {
_renderButtons: function() {
this._super.apply(this, arguments);
this.$btn_set_all_reviewed = this.$buttons.find(
'.o_mail_discuss_button_set_all_reviewed');
this.$btn_set_all_reviewed
.on('click', $.proxy(this, "_onSetAllAsReviewedClicked"));
".o_mail_discuss_button_set_all_reviewed"
);
this.$btn_set_all_reviewed.on(
"click",
$.proxy(this, "_onSetAllAsReviewedClicked")
);
},
/**
@ -217,11 +215,11 @@ odoo.define('mail_tracking.FailedMessageDiscuss', function (require) {
*
* @override
*/
_updateControlPanelButtons: function (thread) {
this.$btn_set_all_reviewed
.toggleClass(
'd-none d-md-none',
thread.getID() !== 'mailbox_failed');
_updateControlPanelButtons: function(thread) {
this.$btn_set_all_reviewed.toggleClass(
"d-none d-md-none",
thread.getID() !== "mailbox_failed"
);
return this._super.apply(this, arguments);
},
@ -233,18 +231,16 @@ odoo.define('mail_tracking.FailedMessageDiscuss', function (require) {
*
* @override
*/
_updateButtonStatus: function (disabled, type) {
if (this._thread.getID() === 'mailbox_failed') {
this.$btn_set_all_reviewed
.toggleClass('disabled', disabled);
_updateButtonStatus: function(disabled, type) {
if (this._thread.getID() === "mailbox_failed") {
this.$btn_set_all_reviewed.toggleClass("disabled", disabled);
// Display Rainbowman when all failed messages are reviewed
// through 'TOGGLE TRACKING STATUS' or marking last failed
// message as reviewed
if (disabled && type === 'toggle_tracking_status') {
this.trigger_up('show_effect', {
message: _t(
"Congratulations, your failed mailbox is empty"),
type: 'rainbow_man',
if (disabled && type === "toggle_tracking_status") {
this.trigger_up("show_effect", {
message: _t("Congratulations, your failed mailbox is empty"),
type: "rainbow_man",
});
}
}
@ -255,20 +251,18 @@ odoo.define('mail_tracking.FailedMessageDiscuss', function (require) {
*
* @override
*/
_onMessageUpdated: function (message, type) {
_onMessageUpdated: function(message, type) {
var self = this;
var currentThreadID = this._thread.getID();
if (currentThreadID === 'mailbox_failed' && !message.isFailed()) {
this._thread.fetchMessages(this.domain)
.then(function () {
var options = self._getThreadRenderingOptions();
self._threadWidget.removeMessageAndRender(
message.getID(), self._thread, options)
.then(function () {
self._updateButtonStatus(
!self._thread.hasMessages(), type);
});
});
if (currentThreadID === "mailbox_failed" && !message.isFailed()) {
this._thread.fetchMessages(this.domain).then(function() {
var options = self._getThreadRenderingOptions();
self._threadWidget
.removeMessageAndRender(message.getID(), self._thread, options)
.then(function() {
self._updateButtonStatus(!self._thread.hasMessages(), type);
});
});
} else {
// Workaround to avoid calling '_fetchAndRenderThread' and
// refetching thread messages because these messages are
@ -283,9 +277,9 @@ odoo.define('mail_tracking.FailedMessageDiscuss', function (require) {
*
* @override
*/
_getThreadRenderingOptions: function () {
_getThreadRenderingOptions: function() {
var values = this._super.apply(this, arguments);
if (this._thread.getID() === 'mailbox_failed') {
if (this._thread.getID() === "mailbox_failed") {
values.displayEmailIcons = true;
values.displayReplyIcons = false;
values.displayRetryButton = true;
@ -299,10 +293,13 @@ odoo.define('mail_tracking.FailedMessageDiscuss', function (require) {
*
* @override
*/
_startListening: function () {
_startListening: function() {
this._super.apply(this, arguments);
this.call('mail_service', 'getMailBus')
.on('update_failed', this, this._throttledUpdateThreads);
this.call("mail_service", "getMailBus").on(
"update_failed",
this,
this._throttledUpdateThreads
);
},
// Handlers
@ -312,10 +309,10 @@ odoo.define('mail_tracking.FailedMessageDiscuss', function (require) {
* @private
* @param {Event} event
*/
_onRetryFailedMessage: function (event) {
_onRetryFailedMessage: function(event) {
event.preventDefault();
var messageID = $(event.currentTarget).data('message-id');
this.do_action('mail.mail_resend_message_action', {
var messageID = $(event.currentTarget).data("message-id");
this.do_action("mail.mail_resend_message_action", {
additional_context: {
mail_message_to_resend: messageID,
},
@ -329,12 +326,12 @@ odoo.define('mail_tracking.FailedMessageDiscuss', function (require) {
* @param {Event} event
* @returns {Promise}
*/
_onMarkFailedMessageReviewed: function (event) {
_onMarkFailedMessageReviewed: function(event) {
event.preventDefault();
var messageID = $(event.currentTarget).data('message-id');
var messageID = $(event.currentTarget).data("message-id");
return this._rpc({
model: 'mail.message',
method: 'set_need_action_done',
model: "mail.message",
method: "set_need_action_done",
args: [[messageID]],
context: this.getSession().user_context,
});
@ -345,22 +342,21 @@ odoo.define('mail_tracking.FailedMessageDiscuss', function (require) {
*
* @private
*/
_onSetAllAsReviewedClicked: function () {
_onSetAllAsReviewedClicked: function() {
this._thread.setAllMessagesAsReviewed();
},
});
MailManager.include({
/**
* Add the 'failed' mailbox
*
* @override
*/
_updateMailboxesFromServer: function (data) {
_updateMailboxesFromServer: function(data) {
this._super.apply(this, arguments);
this._addMailbox({
id: 'failed',
id: "failed",
name: _t("Failed"),
mailboxCounter: data.failed_counter || 0,
});
@ -368,20 +364,19 @@ odoo.define('mail_tracking.FailedMessageDiscuss', function (require) {
});
Mailbox.include({
/**
* Overrides to add domain for 'failed' mailbox thread
*
* @override
*/
_getThreadDomain: function () {
if (this._id === 'mailbox_failed') {
_getThreadDomain: function() {
if (this._id === "mailbox_failed") {
return [
['mail_tracking_ids.state', 'in', FAILED_STATES],
['mail_tracking_needs_action', '=', true],
'|',
['partner_ids', 'in', [session.partner_id]],
['author_id', '=', session.partner_id],
["mail_tracking_ids.state", "in", FAILED_STATES],
["mail_tracking_needs_action", "=", true],
"|",
["partner_ids", "in", [session.partner_id]],
["author_id", "=", session.partner_id],
];
}
// Workaround to avoid throw 'Missing domain' exception. Call _super
@ -397,15 +392,14 @@ odoo.define('mail_tracking.FailedMessageDiscuss', function (require) {
* @returns {$.Promise} resolved when all messages have been marked as
* reviewed on the server
*/
setAllMessagesAsReviewed: function () {
if (this._id === 'mailbox_failed' && this.getMailboxCounter() > 0) {
setAllMessagesAsReviewed: function() {
if (this._id === "mailbox_failed" && this.getMailboxCounter() > 0) {
return this._rpc({
model: 'mail.message',
method: 'set_all_as_reviewed',
model: "mail.message",
method: "set_all_as_reviewed",
});
}
return $.when();
},
});
});

View File

@ -1,21 +1,20 @@
/* Copyright 2019 Alexandre Díaz
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). */
odoo.define('mail_tracking.FailedMessageThread', function (require) {
odoo.define("mail_tracking.FailedMessageThread", function(require) {
"use strict";
var AbstractField = require('web.AbstractField');
var BasicModel = require('web.BasicModel');
var BasicView = require('web.BasicView');
var Chatter = require('mail.Chatter');
var MailThread = require('mail.widget.Thread');
var utils = require('mail.utils');
var core = require('web.core');
var field_registry = require('web.field_registry');
var time = require('web.time');
var AbstractField = require("web.AbstractField");
var BasicModel = require("web.BasicModel");
var BasicView = require("web.BasicView");
var Chatter = require("mail.Chatter");
var MailThread = require("mail.widget.Thread");
var utils = require("mail.utils");
var core = require("web.core");
var field_registry = require("web.field_registry");
var time = require("web.time");
var QWeb = core.qweb;
/**
* Helper method to fetch failed messages
*
@ -24,28 +23,29 @@ odoo.define('mail_tracking.FailedMessageThread', function (require) {
* @param {Array} ids
* @returns {Array}
*/
function _readMessages (widget, ids) {
function _readMessages(widget, ids) {
if (!ids.length) {
return $.when([]);
}
var context = widget.record && widget.record.getContext();
return widget._rpc({
model: 'mail.message',
method: 'get_failed_messages',
args: [ids],
context: context || widget.getSession().user_context,
}).then(function (messages) {
// Convert date to moment
_.each(messages, function (msg) {
msg.date = moment(time.auto_str_to_date(msg.date));
msg.hour = utils.timeFromNow(msg.date);
return widget
._rpc({
model: "mail.message",
method: "get_failed_messages",
args: [ids],
context: context || widget.getSession().user_context,
})
.then(function(messages) {
// Convert date to moment
_.each(messages, function(msg) {
msg.date = moment(time.auto_str_to_date(msg.date));
msg.hour = utils.timeFromNow(msg.date);
});
return messages;
});
return messages;
});
}
BasicModel.include({
/**
* Fetch data for the 'mail_failed_message' field widget in form views.
*
@ -54,27 +54,29 @@ odoo.define('mail_tracking.FailedMessageThread', function (require) {
* @param {String} fieldName
* @returns {Array}
*/
_fetchSpecialFailedMessages: function (record, fieldName) {
var localID = record._changes && fieldName in record._changes
? record._changes[fieldName] : record.data[fieldName];
_fetchSpecialFailedMessages: function(record, fieldName) {
var localID =
record._changes && fieldName in record._changes
? record._changes[fieldName]
: record.data[fieldName];
return _readMessages(this, this.localData[localID].res_ids);
},
});
var FailedMessage = AbstractField.extend({
className: 'o_mail_failed_message',
className: "o_mail_failed_message",
events: {
'click .o_failed_message_retry': '_onRetryFailedMessage',
'click .o_failed_message_reviewed': '_onMarkFailedMessageReviewed',
"click .o_failed_message_retry": "_onRetryFailedMessage",
"click .o_failed_message_reviewed": "_onMarkFailedMessageReviewed",
},
specialData: '_fetchSpecialFailedMessages',
specialData: "_fetchSpecialFailedMessages",
/**
* Overrides to reference failed messages in a easy way
*
* @override
*/
init: function () {
init: function() {
this._super.apply(this, arguments);
this.failed_messages = this.record.specialData[this.name] || [];
},
@ -84,10 +86,9 @@ odoo.define('mail_tracking.FailedMessageThread', function (require) {
*
* @override
*/
start: function () {
start: function() {
this._super.apply(this, arguments);
this.call(
'bus_service', 'onNotification', this, this._onNotification);
this.call("bus_service", "onNotification", this, this._onNotification);
},
/**
@ -96,7 +97,7 @@ odoo.define('mail_tracking.FailedMessageThread', function (require) {
* @private
* @returns {Object}
*/
_failedItemsQWebParams: function () {
_failedItemsQWebParams: function() {
return {
failed_messages: this.failed_messages,
nbFailedMessages: this.failed_messages.length,
@ -108,11 +109,14 @@ odoo.define('mail_tracking.FailedMessageThread', function (require) {
/**
* @private
*/
_render: function () {
_render: function() {
if (this.failed_messages.length) {
this.$el.html(QWeb.render(
'mail_tracking.failed_message_items',
this._failedItemsQWebParams()));
this.$el.html(
QWeb.render(
"mail_tracking.failed_message_items",
this._failedItemsQWebParams()
)
);
} else {
this.$el.empty();
}
@ -124,7 +128,7 @@ odoo.define('mail_tracking.FailedMessageThread', function (require) {
* @private
* @param {Object} record
*/
_reset: function (record) {
_reset: function(record) {
this._super.apply(this, arguments);
this.failed_messages = this.record.specialData[this.name] || [];
this.res_id = record.res_id;
@ -136,8 +140,8 @@ odoo.define('mail_tracking.FailedMessageThread', function (require) {
* @private
* @param {Array} fieldsToReload
*/
_reload: function (fieldsToReload) {
this.trigger_up('reload_mail_fields', fieldsToReload);
_reload: function(fieldsToReload) {
this.trigger_up("reload_mail_fields", fieldsToReload);
},
/**
@ -147,10 +151,10 @@ odoo.define('mail_tracking.FailedMessageThread', function (require) {
* @param {Int} id
* @returns {Promise}
*/
_markFailedMessageReviewed: function (id) {
_markFailedMessageReviewed: function(id) {
return this._rpc({
model: 'mail.message',
method: 'set_need_action_done',
model: "mail.message",
method: "set_need_action_done",
args: [[id]],
context: this.record.getContext(),
});
@ -165,13 +169,13 @@ odoo.define('mail_tracking.FailedMessageThread', function (require) {
* @private
* @param {Array} notifs
*/
_onNotification: function (notifs) {
_onNotification: function(notifs) {
var self = this;
_.each(notifs, function (notif) {
_.each(notifs, function(notif) {
var model = notif[0][1];
if (model === 'res.partner') {
if (model === "res.partner") {
var data = notif[1];
if (data.type === 'toggle_tracking_status') {
if (data.type === "toggle_tracking_status") {
// Reload 'mail_failed_message' widget
self._reload({failed_message: true});
}
@ -186,10 +190,10 @@ odoo.define('mail_tracking.FailedMessageThread', function (require) {
* @private
* @param {Event} event
*/
_onRetryFailedMessage: function (event) {
_onRetryFailedMessage: function(event) {
event.preventDefault();
var messageID = $(event.currentTarget).data('message-id');
this.do_action('mail.mail_resend_message_action', {
var messageID = $(event.currentTarget).data("message-id");
this.do_action("mail.mail_resend_message_action", {
additional_context: {
mail_message_to_resend: messageID,
},
@ -202,26 +206,26 @@ odoo.define('mail_tracking.FailedMessageThread', function (require) {
* @private
* @param {Event} event
*/
_onMarkFailedMessageReviewed: function (event) {
_onMarkFailedMessageReviewed: function(event) {
event.preventDefault();
var messageID = $(event.currentTarget).data('message-id');
var messageID = $(event.currentTarget).data("message-id");
this._markFailedMessageReviewed(messageID).then(
$.proxy(this, "_reload", {failed_message: true}));
$.proxy(this, "_reload", {failed_message: true})
);
},
});
field_registry.add('mail_failed_message', FailedMessage);
field_registry.add("mail_failed_message", FailedMessage);
var mailWidgets = ['mail_failed_message'];
var mailWidgets = ["mail_failed_message"];
BasicView.include({
/**
* Overrides to add 'mail_failed_message' widget as "mail widget" used
* in Chatter.
*
* @override
*/
init: function () {
init: function() {
this._super.apply(this, arguments);
var fieldsInfo = this.fieldsInfo[this.viewType];
for (var fieldName in fieldsInfo) {
@ -245,18 +249,21 @@ odoo.define('mail_tracking.FailedMessageThread', function (require) {
});
Chatter.include({
/**
* Overrides to initialize 'mail_failed_message' widget.
*
* @override
*/
init: function (parent, record, mailFields, options) {
init: function(parent, record, mailFields, options) {
this._super.apply(this, arguments);
// Initialize mail_failed_message widget
if (mailFields.mail_failed_message) {
this.fields.failed_message = new FailedMessage(
this, mailFields.mail_failed_message, record, options);
this,
mailFields.mail_failed_message,
record,
options
);
}
},
@ -266,12 +273,13 @@ odoo.define('mail_tracking.FailedMessageThread', function (require) {
* @private
* @returns {Promise}
*/
_render: function () {
_render: function() {
var self = this;
return this._super.apply(this, arguments).then(function () {
return this._super.apply(this, arguments).then(function() {
if (self.fields.failed_message) {
self.fields.failed_message.$el.insertBefore(
self.$el.find('.o_mail_thread'));
self.$el.find(".o_mail_thread")
);
}
});
},
@ -281,9 +289,9 @@ odoo.define('mail_tracking.FailedMessageThread', function (require) {
*
* @override
*/
_onReloadMailFields: function (event) {
_onReloadMailFields: function(event) {
if (this.fields.failed_message && event.data.failed_message) {
this.trigger_up('reload', {
this.trigger_up("reload", {
fieldNames: [this.fields.failed_message.name],
keepChanges: true,
});
@ -296,13 +304,12 @@ odoo.define('mail_tracking.FailedMessageThread', function (require) {
});
MailThread.include({
/**
* Show 'retry' & 'Set as reviewed' buttons in the Chatter
*
* @override
*/
init: function () {
init: function() {
this._super.apply(this, arguments);
this._enabledOptions.displayRetryButton = true;
this._enabledOptions.displayReviewedButton = true;
@ -312,5 +319,4 @@ odoo.define('mail_tracking.FailedMessageThread', function (require) {
});
return FailedMessage;
});

View File

@ -2,25 +2,24 @@
Copyright 2018 David Vidal - <david.vidal@tecnativa.com>
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). */
odoo.define('mail_tracking.partner_tracking', function (require) {
odoo.define("mail_tracking.partner_tracking", function(require) {
"use strict";
var core = require('web.core');
var ActionManager = require('web.ActionManager');
var AbstractMessage = require('mail.model.AbstractMessage');
var Message = require('mail.model.Message');
var ThreadWidget = require('mail.widget.Thread');
var core = require("web.core");
var ActionManager = require("web.ActionManager");
var AbstractMessage = require("mail.model.AbstractMessage");
var Message = require("mail.model.Message");
var ThreadWidget = require("mail.widget.Thread");
var _t = core._t;
AbstractMessage.include({
/**
* Messages do not have any PartnerTrackings.
*
* @returns {Boolean}
*/
hasPartnerTrackings: function () {
hasPartnerTrackings: function() {
return false;
},
@ -29,13 +28,13 @@ odoo.define('mail_tracking.partner_tracking', function (require) {
*
* @returns {Boolean}
*/
hasEmailCc: function () {
hasEmailCc: function() {
return false;
},
});
Message.include({
init: function (parent, data) {
init: function(parent, data) {
this._super.apply(this, arguments);
this._partnerTrackings = data.partner_trackings || [];
this._emailCc = data.email_cc || [];
@ -48,7 +47,7 @@ odoo.define('mail_tracking.partner_tracking', function (require) {
* @override
* @returns {Boolean}
*/
hasPartnerTrackings: function () {
hasPartnerTrackings: function() {
return _.some(this._partnerTrackings);
},
@ -57,7 +56,7 @@ odoo.define('mail_tracking.partner_tracking', function (require) {
*
* @returns {Boolean}
*/
hasEmailCc: function () {
hasEmailCc: function() {
return _.some(this._emailCc);
},
@ -68,7 +67,7 @@ odoo.define('mail_tracking.partner_tracking', function (require) {
* @override
* @returns {Object[]}
*/
getPartnerTrackings: function () {
getPartnerTrackings: function() {
if (!this.hasPartnerTrackings()) {
return [];
}
@ -81,7 +80,7 @@ odoo.define('mail_tracking.partner_tracking', function (require) {
*
* @returns {Array}
*/
getEmailCc: function () {
getEmailCc: function() {
if (!this.hasEmailCc()) {
return [];
}
@ -95,19 +94,19 @@ odoo.define('mail_tracking.partner_tracking', function (require) {
* @param {String} email
* @returns {Boolean}
*/
isEmailCc: function (email) {
isEmailCc: function(email) {
if (!this.hasEmailCc()) {
return false;
}
return _.some(this._emailCc, function (item) {
return _.some(this._emailCc, function(item) {
return item[0] === email;
});
},
toggleTrackingStatus: function () {
toggleTrackingStatus: function() {
return this._rpc({
model: 'mail.message',
method: 'toggle_tracking_status',
model: "mail.message",
method: "toggle_tracking_status",
args: [[this.id]],
});
},
@ -115,53 +114,52 @@ odoo.define('mail_tracking.partner_tracking', function (require) {
ThreadWidget.include({
events: _.extend(ThreadWidget.prototype.events, {
'click .o_mail_action_tracking_partner':
'on_tracking_partner_click',
'click .o_mail_action_tracking_status': 'on_tracking_status_click',
"click .o_mail_action_tracking_partner": "on_tracking_partner_click",
"click .o_mail_action_tracking_status": "on_tracking_status_click",
}),
on_tracking_partner_click: function (event) {
var partner_id = this.$el.find(event.currentTarget).data('partner');
on_tracking_partner_click: function(event) {
var partner_id = this.$el.find(event.currentTarget).data("partner");
var state = {
'model': 'res.partner',
'id': partner_id,
'title': _t("Tracking partner"),
model: "res.partner",
id: partner_id,
title: _t("Tracking partner"),
};
event.preventDefault();
this.action_manager.do_push_state(state);
var action = {
type:'ir.actions.act_window',
view_type: 'form',
view_mode: 'form',
res_model: 'res.partner',
views: [[false, 'form']],
target: 'current',
type: "ir.actions.act_window",
view_type: "form",
view_mode: "form",
res_model: "res.partner",
views: [[false, "form"]],
target: "current",
res_id: partner_id,
};
this.do_action(action);
},
on_tracking_status_click: function (event) {
var tracking_email_id = $(event.currentTarget).data('tracking');
on_tracking_status_click: function(event) {
var tracking_email_id = $(event.currentTarget).data("tracking");
var state = {
'model': 'mail.tracking.email',
'id': tracking_email_id,
'title': _t("Message tracking"),
model: "mail.tracking.email",
id: tracking_email_id,
title: _t("Message tracking"),
};
event.preventDefault();
this.action_manager.do_push_state(state);
var action = {
type:'ir.actions.act_window',
view_type: 'form',
view_mode: 'form',
res_model: 'mail.tracking.email',
views: [[false, 'form']],
target: 'new',
type: "ir.actions.act_window",
view_type: "form",
view_mode: "form",
res_model: "mail.tracking.email",
views: [[false, "form"]],
target: "new",
res_id: tracking_email_id,
};
this.do_action(action);
},
init: function () {
init: function() {
this._super.apply(this, arguments);
this.action_manager = this.findAncestor(function (ancestor) {
this.action_manager = this.findAncestor(function(ancestor) {
return ancestor instanceof ActionManager;
});
},

View File

@ -1,13 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<?xml version="1.0" encoding="UTF-8" ?>
<templates>
<t t-extend="mail.widget.Thread.Message">
<t t-jquery="span[t-attf-class=o_thread_icons]" t-operation="append">
<a t-if="message.isFailed() &amp;&amp; options.displayRetryButton" class="btn btn-link o_thread_icon btn-default text-muted btn-sm o_failed_message_reviewed o_activity_link mr8" t-att-data-message-id="message.getID()">
<i class="fa fa-check"/> Set as Reviewed
<a
t-if="message.isFailed() &amp;&amp; options.displayRetryButton"
class="btn btn-link o_thread_icon btn-default text-muted btn-sm o_failed_message_reviewed o_activity_link mr8"
t-att-data-message-id="message.getID()"
>
<i class="fa fa-check" /> Set as Reviewed
</a>
<a t-if="message.isFailed() &amp;&amp; options.displayReviewedButton" class="btn btn-link o_thread_icon btn-default text-muted btn-sm o_failed_message_retry" t-att-data-message-id="message.getID()">
<i class="fa fa-retweet"/> Retry
<a
t-if="message.isFailed() &amp;&amp; options.displayReviewedButton"
class="btn btn-link o_thread_icon btn-default text-muted btn-sm o_failed_message_retry"
t-att-data-message-id="message.getID()"
>
<i class="fa fa-retweet" /> Retry
</a>
</t>
</t>

View File

@ -1,25 +1,31 @@
<?xml version="1.0" encoding="UTF-8"?>
<?xml version="1.0" encoding="UTF-8" ?>
<templates>
<t t-name="mail_tracking.SidebarFailedCounter">
<span t-attf-class="o_mail_sidebar_failed badge badge-pill #{(counter ? '' : 'd-none')}">
<t t-esc="counter"/>
<span
t-attf-class="o_mail_sidebar_failed badge badge-pill #{(counter ? '' : 'd-none')}"
>
<t t-esc="counter" />
</span>
</t>
<t t-name="mail_tracking.SidebarFailed">
<div t-attf-class="o_mail_discuss_title_main o_mail_discuss_item #{(activeThreadID === 'mailbox_failed') ? 'o_active': ''}"
data-thread-id="mailbox_failed">
<span class="o_thread_name"><i class="fa fa-exclamation mr8"/>Failed</span>
<t t-set="counter" t-value="failedCounter"/>
<t t-call="mail_tracking.SidebarFailedCounter"/>
<div
t-attf-class="o_mail_discuss_title_main o_mail_discuss_item #{(activeThreadID === 'mailbox_failed') ? 'o_active': ''}"
data-thread-id="mailbox_failed"
>
<span class="o_thread_name"><i class="fa fa-exclamation mr8" />Failed</span>
<t t-set="counter" t-value="failedCounter" />
<t t-call="mail_tracking.SidebarFailedCounter" />
</div>
</t>
<t t-extend="mail.widget.Thread.Empty">
<t t-jquery="t:last-child" t-operation="after">
<t t-if="thread.getID() === 'mailbox_failed'">
<div class="o_thread_title">Congratulations, you don't have any failed messages</div>
<div
class="o_thread_title"
>Congratulations, you don't have any failed messages</div>
<div>Failed messages appear here.</div>
</t>
</t>
@ -27,7 +33,11 @@
<t t-extend="mail.discuss.ControlButtons">
<t t-jquery="div" t-operation="append">
<button type="button" class="btn btn-secondary o_mail_discuss_button_set_all_reviewed d-none d-md-none d-md-inline-block" title="Mark all as reviewed">Set all as reviewed</button>
<button
type="button"
class="btn btn-secondary o_mail_discuss_button_set_all_reviewed d-none d-md-none d-md-inline-block"
title="Mark all as reviewed"
>Set all as reviewed</button>
</t>
</t>

View File

@ -1,13 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<?xml version="1.0" encoding="UTF-8" ?>
<templates xml:space="preserve">
<t t-name="mail_tracking.failed_message_items">
<div class="o_thread_date_separator o_border_dashed collapsed" data-toggle="collapse" data-target="#o_chatter_failed_message">
<div
class="o_thread_date_separator o_border_dashed collapsed"
data-toggle="collapse"
data-target="#o_chatter_failed_message"
>
<a role="button" class="o_thread_date btn">
<i class="fa fa-fw fa-caret-down"/>
<i class="fa fa-fw fa-caret-down" />
Failed messages
<small class="o_chatter_failed_message_summary ml8">
<span class="badge rounded-circle badge-danger"><t t-esc="nbFailedMessages"/></span>
<span class="badge rounded-circle badge-danger"><t
t-esc="nbFailedMessages"
/></span>
</small>
</a>
</div>
@ -16,40 +22,60 @@
<div class="o_thread_message" style="margin-bottom: 10px">
<div class="o_thread_message_sidebar">
<div class="o_avatar_stack">
<img t-attf-src="/web/image/res.partner/#{message.author[0]}/image_small" class="o_thread_message_avatar rounded-circle mb8" t-att-title="message.author[1]" t-att-alt="message.author[1]"/>
<i t-att-class="'o_avatar_icon fa fa-exclamation bg-danger-full'"
title="Failed"/>
<img
t-attf-src="/web/image/res.partner/#{message.author[0]}/image_small"
class="o_thread_message_avatar rounded-circle mb8"
t-att-title="message.author[1]"
t-att-alt="message.author[1]"
/>
<i
t-att-class="'o_avatar_icon fa fa-exclamation bg-danger-full'"
title="Failed"
/>
</div>
</div>
<div class="o_thread_message_core">
<div class="o_mail_info text-muted">
<strong class="o_thread_author">
<t t-esc="message.author[1]"/>
<t t-esc="message.author[1]" />
</strong>
- <small class="o_mail_timestamp" t-att-title="message.date.format(date_format)"><t t-esc="message.hour"/></small>
- <small
class="o_mail_timestamp"
t-att-title="message.date.format(date_format)"
><t t-esc="message.hour" /></small>
<span t-attf-class="o_thread_icons">
<a href="#" class="btn btn-link btn-success o_thread_icon text-muted btn-sm o_failed_message_reviewed o_activity_link" t-att-data-message-id="message.id">
<i class="fa fa-check"/> Set as Reviewed
<a
href="#"
class="btn btn-link btn-success o_thread_icon text-muted btn-sm o_failed_message_reviewed o_activity_link"
t-att-data-message-id="message.id"
>
<i class="fa fa-check" /> Set as Reviewed
</a>
<a href="#" class="btn btn-link btn-default o_thread_icon text-muted btn-sm o_failed_message_retry" t-att-data-message-id="message.id">
<i class="fa fa-retweet"/> Retry
<a
href="#"
class="btn btn-link btn-default o_thread_icon text-muted btn-sm o_failed_message_retry"
t-att-data-message-id="message.id"
>
<i class="fa fa-retweet" /> Retry
</a>
</span>
<br/>
<br />
<strong class="text-danger">Failed Recipients:</strong>
<t t-foreach="message.failed_recipients" t-as="recipient">
<t t-if="!recipient_first">
-
</t>
<a class="o_mail_action_tracking_partner"
t-att-data-partner="recipient[0]"
t-attf-href="#model=res.partner&amp;id=#{recipient[0]}">
<t t-esc="recipient[1]"/>
<a
class="o_mail_action_tracking_partner"
t-att-data-partner="recipient[0]"
t-attf-href="#model=res.partner&amp;id=#{recipient[0]}"
>
<t t-esc="recipient[1]" />
</a>
</t>
</div>
<div class="o_thread_message_note small">
<t t-raw="message.body"/>
<t t-raw="message.body" />
</div>
</div>
</div>

View File

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<?xml version="1.0" encoding="UTF-8" ?>
<!-- Copyright 2016 Antonio Espinosa - <antonio.espinosa@tecnativa.com>
Copyright 2019 Alexandre Díaz
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -->
@ -7,54 +7,57 @@
<t t-name="mail.tracking.status">
<t t-if="tracking['isCc']">
<span class="mail_tracking_cc">
<i class="fa fa-cc"></i>
<i class="fa fa-cc" />
</span>
</t>
<t t-elif="!tracking['isCc'] &amp;&amp; !tracking['partner_id']">
<span class="mail_anon_recipient">
<i class="fa fa-low-vision"></i>
<i class="fa fa-low-vision" />
</span>
</t>
<t t-elif="tracking['status'] === 'unknown'">
<span class="mail_tracking_unknown">
<i class="fa fa-ban"></i>
<i class="fa fa-ban" />
</span>
</t>
<t t-elif="tracking['status'] === 'waiting'">
<span class="mail_tracking_waiting mail_tracking_pointer">
<i class="fa fa-clock-o"></i>
<i class="fa fa-clock-o" />
</span>
</t>
<t t-elif="tracking['status'] === 'error'">
<span class="mail_tracking_error mail_tracking_pointer">
<i t-if="tracking['error_type'] === 'no_recipient'" class="fa fa-user-times"></i>
<i t-else="" class="fa fa-remove"></i>
<i
t-if="tracking['error_type'] === 'no_recipient'"
class="fa fa-user-times"
/>
<i t-else="" class="fa fa-remove" />
</span>
</t>
<t t-elif="tracking['status'] === 'sent'">
<span class="mail_tracking_sent mail_tracking_pointer">
<i class="fa fa-check"></i>
<i class="fa fa-check" />
</span>
</t>
<t t-elif="tracking['status'] === 'delivered'">
<span class="fa-stack mail_tracking_delivered mail_tracking_pointer">
<i class="fa fa-check fa-stack-1x" style="margin-left:1px"></i>
<i class="fa fa-check fa-inverse fa-stack-1x" style="margin-left:-2px;"></i>
<i class="fa fa-check fa-stack-1x" style="margin-left:-3px"></i>
<i class="fa fa-check fa-stack-1x" style="margin-left:1px" />
<i class="fa fa-check fa-inverse fa-stack-1x" style="margin-left:-2px;" />
<i class="fa fa-check fa-stack-1x" style="margin-left:-3px" />
</span>
</t>
<t t-elif="tracking['status'] === 'opened'">
<span class="fa-stack mail_tracking_opened mail_tracking_pointer">
<i class="fa fa-check fa-stack-1x" style="margin-left:1px"></i>
<i class="fa fa-check fa-inverse fa-stack-1x" style="margin-left:-2px;"></i>
<i class="fa fa-check fa-stack-1x" style="margin-left:-3px"></i>
<i class="fa fa-check fa-stack-1x" style="margin-left:1px" />
<i class="fa fa-check fa-inverse fa-stack-1x" style="margin-left:-2px;" />
<i class="fa fa-check fa-stack-1x" style="margin-left:-3px" />
</span>
</t>
</t>
<t t-extend="mail.widget.Thread.Message">
<t t-jquery="p.o_mail_info" t-operation="after">
<t t-if="message.hasPartnerTrackings() || message.hasEmailCc()" >
<t t-if="message.hasPartnerTrackings() || message.hasEmailCc()">
<p class="o_mail_tracking">
<strong>To:</strong>
<t t-foreach="message.getPartnerTrackings()" t-as="tracking">
@ -62,21 +65,35 @@
-
</t>
<t t-if="tracking['partner_id']">
<a t-attf-class="o_mail_action_tracking_partner #{tracking['isCc'] ? 'o_mail_cc' : ''}"
t-att-data-partner="tracking['partner_id']"
t-attf-href="#model=res.partner&amp;id=#{tracking['partner_id']}">
<t t-esc="tracking['recipient']"/>
<a
t-attf-class="o_mail_action_tracking_partner #{tracking['isCc'] ? 'o_mail_cc' : ''}"
t-att-data-partner="tracking['partner_id']"
t-attf-href="#model=res.partner&amp;id=#{tracking['partner_id']}"
>
<t t-esc="tracking['recipient']" />
</a>
</t>
<t t-else="">
<span t-attf-class="#{tracking['isCc'] ? 'o_mail_cc' : ''}"><t t-esc="tracking['recipient']"/></span>
<span t-attf-class="#{tracking['isCc'] ? 'o_mail_cc' : ''}"><t
t-esc="tracking['recipient']"
/></span>
</t>
<t t-if="tracking['status'] === 'error' &amp;&amp; tracking['error_type'] === 'no_recipient'" t-set="title_status" t-value="tracking['error_description']" />
<t t-else="" t-set="title_status" t-value="tracking['status_human']" />
<span class="mail_tracking o_mail_action_tracking_status"
t-att-data-tracking="tracking['tracking_id']"
t-att-title="title_status">
<t t-call="mail.tracking.status"/>
<t
t-if="tracking['status'] === 'error' &amp;&amp; tracking['error_type'] === 'no_recipient'"
t-set="title_status"
t-value="tracking['error_description']"
/>
<t
t-else=""
t-set="title_status"
t-value="tracking['status_human']"
/>
<span
class="mail_tracking o_mail_action_tracking_status"
t-att-data-tracking="tracking['tracking_id']"
t-att-title="title_status"
>
<t t-call="mail.tracking.status" />
</span>
</t>
</p>

View File

@ -1,21 +1,33 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8" ?>
<!-- Copyright 2016 Antonio Espinosa - <antonio.espinosa@tecnativa.com>
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -->
<odoo>
<template id="assets_backend"
name="mail_tracking assets"
inherit_id="web.assets_backend">
<template
id="assets_backend"
name="mail_tracking assets"
inherit_id="web.assets_backend"
>
<xpath expr="." position="inside">
<link rel="stylesheet"
href="/mail_tracking/static/src/css/mail_tracking.scss"/>
<link rel="stylesheet"
href="/mail_tracking/static/src/css/failed_message.scss"/>
<script type="text/javascript"
src="/mail_tracking/static/src/js/mail_tracking.js"/>
<script type="text/javascript"
src="/mail_tracking/static/src/js/failed_message/discuss.js"/>
<script type="text/javascript"
src="/mail_tracking/static/src/js/failed_message/thread.js"/>
<link
rel="stylesheet"
href="/mail_tracking/static/src/css/mail_tracking.scss"
/>
<link
rel="stylesheet"
href="/mail_tracking/static/src/css/failed_message.scss"
/>
<script
type="text/javascript"
src="/mail_tracking/static/src/js/mail_tracking.js"
/>
<script
type="text/javascript"
src="/mail_tracking/static/src/js/failed_message/discuss.js"
/>
<script
type="text/javascript"
src="/mail_tracking/static/src/js/failed_message/thread.js"
/>
</xpath>
</template>
</odoo>

View File

@ -1,9 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<record model="ir.ui.view" id="view_message_form">
<field name="model">mail.message</field>
<field name="inherit_id" ref="mail.view_message_form"/>
<field name="inherit_id" ref="mail.view_message_form" />
<field name="arch" type="xml">
<field name="subtype_id" position="after">
<field name="mail_tracking_needs_action" />

View File

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8" ?>
<!-- Copyright 2016 Antonio Espinosa - <antonio.espinosa@tecnativa.com>
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -->
<odoo>
@ -9,50 +9,54 @@
<field name="arch" type="xml">
<form string="MailTracking event" create="false" edit="false" delete="false">
<header>
<field name="state" widget="statusbar"/>
<field name="state" widget="statusbar" />
</header>
<sheet>
<group>
<field name="name"/>
<field name="name" />
</group>
<group>
<group>
<field name="mail_message_id"/>
<field name="mail_id"/>
<field name="partner_id"/>
<field name="recipient"/>
<field name="sender"/>
<field name="mail_message_id" />
<field name="mail_id" />
<field name="partner_id" />
<field name="recipient" />
<field name="sender" />
</group>
<group>
<field name="timestamp"/>
<field name="time"/>
<field name="date"/>
<field name="timestamp" />
<field name="time" />
<field name="date" />
</group>
</group>
<group attrs="{'invisible': [('bounce_type', '=', False)]}">
<field name="bounce_type"/>
<field name="bounce_description"/>
<field name="bounce_type" />
<field name="bounce_description" />
</group>
<group attrs="{'invisible': [('error_type', '=', False)]}">
<field name="error_smtp_server"
attrs="{'invisible': [('error_smtp_server', '=', False)]}"/>
<field name="error_type"/>
<field name="error_description"/>
<field
name="error_smtp_server"
attrs="{'invisible': [('error_smtp_server', '=', False)]}"
/>
<field name="error_type" />
<field name="error_description" />
</group>
<label for="tracking_event_ids"/>
<label for="tracking_event_ids" />
<div>
<field name="tracking_event_ids">
<tree string="Tracking events"
decoration-muted="event_type == 'deferral'"
decoration-danger="event_type in ('hard_bounce', 'soft_bounce', 'spam', 'reject')"
decoration-info="event_type in ('unsub', 'click', 'open')">
<field name="time"/>
<field name="event_type"/>
<field name="ip"/>
<field name="url"/>
<field name="user_country_id" string="Country"/>
<field name="os_family" string="OS"/>
<field name="ua_family" string="User agent"/>
<tree
string="Tracking events"
decoration-muted="event_type == 'deferral'"
decoration-danger="event_type in ('hard_bounce', 'soft_bounce', 'spam', 'reject')"
decoration-info="event_type in ('unsub', 'click', 'open')"
>
<field name="time" />
<field name="event_type" />
<field name="ip" />
<field name="url" />
<field name="user_country_id" string="Country" />
<field name="os_family" string="OS" />
<field name="ua_family" string="User agent" />
</tree>
</field>
</div>
@ -65,17 +69,22 @@
<field name="name">mail.tracking.email.tree</field>
<field name="model">mail.tracking.email</field>
<field name="arch" type="xml">
<tree string="MailTracking emails" create="false" edit="false" delete="false"
decoration-muted="state in (False, 'deferred')"
decoration-success="state == 'opened'"
decoration-danger="state in ('rejected', 'spam', 'bounced', 'soft-bounced', 'error')"
decoration-info="state == 'unsub'">
<field name="time"/>
<field name="date" invisible="1"/>
<field name="name"/>
<field name="sender" string="Sender"/>
<field name="recipient" string="Recipient"/>
<field name="state"/>
<tree
string="MailTracking emails"
create="false"
edit="false"
delete="false"
decoration-muted="state in (False, 'deferred')"
decoration-success="state == 'opened'"
decoration-danger="state in ('rejected', 'spam', 'bounced', 'soft-bounced', 'error')"
decoration-info="state == 'unsub'"
>
<field name="time" />
<field name="date" invisible="1" />
<field name="name" />
<field name="sender" string="Sender" />
<field name="recipient" string="Recipient" />
<field name="state" />
</tree>
</field>
</record>
@ -85,25 +94,63 @@
<field name="model">mail.tracking.email</field>
<field name="arch" type="xml">
<search string="MailTracking email search">
<field name="display_name" string="Email"
filter_domain="['|', ('sender', 'ilike', self), ('recipient', 'ilike', self)]"/>
<field name="sender" string="Sender"/>
<field name="recipient" string="Recipient"/>
<field name="name" string="Subject"/>
<field name="time" string="Time"/>
<field name="date" string="Date"/>
<filter name="sent" string="Sent" domain="[('state', 'in', ('sent',))]"/>
<filter name="deferred" string="Deferred" domain="[('state', '=', 'deferred')]"/>
<filter name="delivered" string="Delivered" domain="[('state', 'in', ('delivered', 'opened'))]"/>
<filter name="unsub" string="Unsubscribed" domain="[('state', '=', 'unsub')]"/>
<filter name="exception" string="Failed"
domain="[('state', 'in', ('error', 'rejected', 'spam', 'bounced', 'soft-bounced'))]"/>
<separator/>
<field
name="display_name"
string="Email"
filter_domain="['|', ('sender', 'ilike', self), ('recipient', 'ilike', self)]"
/>
<field name="sender" string="Sender" />
<field name="recipient" string="Recipient" />
<field name="name" string="Subject" />
<field name="time" string="Time" />
<field name="date" string="Date" />
<filter name="sent" string="Sent" domain="[('state', 'in', ('sent',))]" />
<filter
name="deferred"
string="Deferred"
domain="[('state', '=', 'deferred')]"
/>
<filter
name="delivered"
string="Delivered"
domain="[('state', 'in', ('delivered', 'opened'))]"
/>
<filter
name="unsub"
string="Unsubscribed"
domain="[('state', '=', 'unsub')]"
/>
<filter
name="exception"
string="Failed"
domain="[('state', 'in', ('error', 'rejected', 'spam', 'bounced', 'soft-bounced'))]"
/>
<separator />
<group expand="0" string="Group By">
<filter string="State" name="group_by_state" domain="[]" context="{'group_by': 'state'}"/>
<filter string="Subject" name="group_by_subject" domain="[]" context="{'group_by': 'name'}"/>
<filter string="Sender" name="group_by_sender" domain="[]" context="{'group_by': 'sender'}"/>
<filter string="Month" name="group_by_month" domain="[]" context="{'group_by': 'date'}"/>
<filter
string="State"
name="group_by_state"
domain="[]"
context="{'group_by': 'state'}"
/>
<filter
string="Subject"
name="group_by_subject"
domain="[]"
context="{'group_by': 'name'}"
/>
<filter
string="Sender"
name="group_by_sender"
domain="[]"
context="{'group_by': 'sender'}"
/>
<filter
string="Month"
name="group_by_month"
domain="[]"
context="{'group_by': 'date'}"
/>
</group>
</search>
</field>
@ -114,12 +161,15 @@
<field name="name">MailTracking emails</field>
<field name="res_model">mail.tracking.email</field>
<field name="view_mode">tree,form</field>
<field name="search_view_id" ref="view_mail_tracking_email_search"/>
<field name="search_view_id" ref="view_mail_tracking_email_search" />
</record>
<!-- Add menu entry in Settings/Email -->
<menuitem name="Tracking emails" id="menu_mail_tracking_email"
parent="base.menu_email"
action="action_view_mail_tracking_email"/>
<menuitem
name="Tracking emails"
id="menu_mail_tracking_email"
parent="base.menu_email"
action="action_view_mail_tracking_email"
/>
</odoo>

View File

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8" ?>
<!-- Copyright 2016 Antonio Espinosa - <antonio.espinosa@tecnativa.com>
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -->
<odoo>
@ -11,40 +11,46 @@
<sheet>
<group>
<group>
<field name="tracking_email_id"/>
<field name="recipient"/>
<field name="event_type"/>
<field name="tracking_email_id" />
<field name="recipient" />
<field name="event_type" />
</group>
<group>
<field name="timestamp"/>
<field name="time"/>
<field name="date"/>
<field name="timestamp" />
<field name="time" />
<field name="date" />
</group>
</group>
<group attrs="{'invisible': [('event_type', 'not in', ('sent',))]}">
<field name="smtp_server"/>
<field name="smtp_server" />
</group>
<group attrs="{'invisible': [('event_type', 'not in', ('open', 'click'))]}">
<field name="url"/>
<group
attrs="{'invisible': [('event_type', 'not in', ('open', 'click'))]}"
>
<field name="url" />
</group>
<group attrs="{'invisible': [('event_type', 'not in', ('open', 'click'))]}">
<group
attrs="{'invisible': [('event_type', 'not in', ('open', 'click'))]}"
>
<group>
<field name="mobile"/>
<field name="ip"/>
<field name="user_country_id"/>
<field name="mobile" />
<field name="ip" />
<field name="user_country_id" />
</group>
<group>
<field name="user_agent"/>
<field name="ua_family"/>
<field name="ua_type"/>
<field name="os_family"/>
<field name="user_agent" />
<field name="ua_family" />
<field name="ua_type" />
<field name="os_family" />
</group>
</group>
<group string="Error"
attrs="{'invisible': [('error_type', '=', False)]}">
<field name="error_type"/>
<field name="error_description"/>
<field name="error_details"/>
<group
string="Error"
attrs="{'invisible': [('error_type', '=', False)]}"
>
<field name="error_type" />
<field name="error_description" />
<field name="error_details" />
</group>
</sheet>
</form>
@ -55,21 +61,29 @@
<field name="name">mail.tracking.event.tree</field>
<field name="model">mail.tracking.event</field>
<field name="arch" type="xml">
<tree string="MailTracking events" create="false" edit="false" delete="false"
decoration-muted="event_type == 'deferred'"
decoration-danger="event_type in ('hard_bounce', 'soft_bounce', 'spam', 'reject')"
decoration-info="event_type in ('unsub', 'click', 'open')">
<field name="time"/>
<field name="tracking_email_id"/>
<field name="recipient"/>
<field name="event_type"/>
<field name="error_details" invisible="not context.get('event_error_filter', False)"/>
<field name="date" invisible="1"/>
<field name="ip"/>
<field name="url"/>
<field name="user_country_id" string="Country"/>
<field name="os_family" string="OS"/>
<field name="ua_family" string="User agent"/>
<tree
string="MailTracking events"
create="false"
edit="false"
delete="false"
decoration-muted="event_type == 'deferred'"
decoration-danger="event_type in ('hard_bounce', 'soft_bounce', 'spam', 'reject')"
decoration-info="event_type in ('unsub', 'click', 'open')"
>
<field name="time" />
<field name="tracking_email_id" />
<field name="recipient" />
<field name="event_type" />
<field
name="error_details"
invisible="not context.get('event_error_filter', False)"
/>
<field name="date" invisible="1" />
<field name="ip" />
<field name="url" />
<field name="user_country_id" string="Country" />
<field name="os_family" string="OS" />
<field name="ua_family" string="User agent" />
</tree>
</field>
</record>
@ -79,32 +93,88 @@
<field name="model">mail.tracking.event</field>
<field name="arch" type="xml">
<search string="MailTracking event search">
<field name="tracking_email_id" string="Message"
filter_domain="[('tracking_email_id', 'ilike', self)]"/>
<field name="recipient" string="Recipient"/>
<field name="time" string="Time"/>
<field name="date" string="Date"/>
<field name="ip" string="IP"/>
<field name="url" string="URL"/>
<filter name="sent" string="Sent" domain="[('event_type', '=', 'sent')]"/>
<filter name="delivered" string="Delivered" domain="[('event_type', '=', 'delivered')]"/>
<filter name="click" string="Click" domain="[('event_type', '=', 'click')]"/>
<filter name="open" string="Open" domain="[('event_type', '=', 'open')]"/>
<filter name="unsub" string="Unsubscribe" domain="[('event_type', '=', 'unsub')]"/>
<filter name="bounce" string="Bounce"
domain="[('event_type', 'in', ('hard_bounce', 'soft_bounce'))]"/>
<filter name="exception" string="Failed"
<field
name="tracking_email_id"
string="Message"
filter_domain="[('tracking_email_id', 'ilike', self)]"
/>
<field name="recipient" string="Recipient" />
<field name="time" string="Time" />
<field name="date" string="Date" />
<field name="ip" string="IP" />
<field name="url" string="URL" />
<filter name="sent" string="Sent" domain="[('event_type', '=', 'sent')]" />
<filter
name="delivered"
string="Delivered"
domain="[('event_type', '=', 'delivered')]"
/>
<filter
name="click"
string="Click"
domain="[('event_type', '=', 'click')]"
/>
<filter name="open" string="Open" domain="[('event_type', '=', 'open')]" />
<filter
name="unsub"
string="Unsubscribe"
domain="[('event_type', '=', 'unsub')]"
/>
<filter
name="bounce"
string="Bounce"
domain="[('event_type', 'in', ('hard_bounce', 'soft_bounce'))]"
/>
<filter
name="exception"
string="Failed"
domain="[('event_type', 'in', ('reject', 'spam'))]"
context="{'event_error_filter': True}"/>
<separator/>
context="{'event_error_filter': True}"
/>
<separator />
<group expand="0" string="Group By">
<filter string="Type" name="group_by_type" domain="[]" context="{'group_by': 'event_type'}"/>
<filter string="Message" name="group_by_message" domain="[]" context="{'group_by': 'tracking_email_id'}"/>
<filter string="OS" name="group_by_os" domain="[('os_family', '!=', False)]" context="{'group_by': 'os_family'}"/>
<filter string="User agent" name="group_by_user_agent" domain="[('ua_family', '!=', False)]" context="{'group_by': 'ua_family'}"/>
<filter string="User agent type" name="group_by_user_agent_type" domain="[('ua_type', '!=', False)]" context="{'group_by': 'ua_type'}"/>
<filter string="Country" name="group_by_country" domain="[('user_country_id', '!=', False)]" context="{'group_by': 'user_country_id'}"/>
<filter string="Month" name="group_by_date" domain="[]" context="{'group_by': 'date'}"/>
<filter
string="Type"
name="group_by_type"
domain="[]"
context="{'group_by': 'event_type'}"
/>
<filter
string="Message"
name="group_by_message"
domain="[]"
context="{'group_by': 'tracking_email_id'}"
/>
<filter
string="OS"
name="group_by_os"
domain="[('os_family', '!=', False)]"
context="{'group_by': 'os_family'}"
/>
<filter
string="User agent"
name="group_by_user_agent"
domain="[('ua_family', '!=', False)]"
context="{'group_by': 'ua_family'}"
/>
<filter
string="User agent type"
name="group_by_user_agent_type"
domain="[('ua_type', '!=', False)]"
context="{'group_by': 'ua_type'}"
/>
<filter
string="Country"
name="group_by_country"
domain="[('user_country_id', '!=', False)]"
context="{'group_by': 'user_country_id'}"
/>
<filter
string="Month"
name="group_by_date"
domain="[]"
context="{'group_by': 'date'}"
/>
</group>
</search>
</field>
@ -114,13 +184,16 @@
<field name="name">MailTracking events</field>
<field name="res_model">mail.tracking.event</field>
<field name="view_mode">tree,form</field>
<field name="search_view_id" ref="view_mail_tracking_event_search"/>
<field name="search_view_id" ref="view_mail_tracking_event_search" />
</record>
<!-- Add menu entry in Settings/Email -->
<menuitem name="Tracking events" id="menu_mail_tracking_event"
parent="base.menu_email"
action="action_view_mail_tracking_event"/>
<menuitem
name="Tracking events"
id="menu_mail_tracking_event"
parent="base.menu_email"
action="action_view_mail_tracking_event"
/>
</odoo>

View File

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8" ?>
<!-- Copyright 2016 Antonio Espinosa - <antonio.espinosa@tecnativa.com>
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -->
<odoo>
@ -7,26 +7,32 @@
<field name="name">Partner Form with tracking emails</field>
<field name="model">res.partner</field>
<field name="priority">46</field>
<field name="inherit_id" ref="base.view_partner_form"/>
<field name="inherit_id" ref="base.view_partner_form" />
<field name="arch" type="xml">
<div name="button_box" position="inside">
<button name="%(mail_tracking.action_view_mail_tracking_email)d"
<button
name="%(mail_tracking.action_view_mail_tracking_email)d"
context="{'search_default_recipient': email,
'default_recipient': email}"
type="action"
class="oe_stat_button"
icon="fa-envelope-o"
attrs="{'invisible': [('email', '=', False)]}">
<field name="tracking_emails_count"
widget="statinfo"
string="Tracking emails"/>
attrs="{'invisible': [('email', '=', False)]}"
>
<field
name="tracking_emails_count"
widget="statinfo"
string="Tracking emails"
/>
</button>
</div>
<xpath expr="//field[@name='email']/.." position="after">
<field name="email_score" widget="progressbar"
attrs="{'invisible': [('email', '=', False)]}"/>
<field name="email_bounced"
attrs="{'invisible': [('email', '=', False)]}"/>
<field
name="email_score"
widget="progressbar"
attrs="{'invisible': [('email', '=', False)]}"
/>
<field name="email_bounced" attrs="{'invisible': [('email', '=', False)]}" />
</xpath>
</field>
</record>
@ -34,12 +40,15 @@
<record model="ir.ui.view" id="view_res_partner_filter">
<field name="name">Filter bounced partners</field>
<field name="model">res.partner</field>
<field name="inherit_id" ref="base.view_res_partner_filter"/>
<field name="inherit_id" ref="base.view_res_partner_filter" />
<field name="arch" type="xml">
<filter name="type_company" position="after">
<separator/>
<filter string="Email bounced" name="email_bounced"
domain="[('email', '!=' , False), ('email_bounced', '=', True)]"/>
<separator />
<filter
string="Email bounced"
name="email_bounced"
domain="[('email', '!=' , False), ('email_bounced', '=', True)]"
/>
</filter>
</field>
</record>

View File

@ -1,14 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<record id="mailgun_manual_check" model="ir.ui.view">
<field name="name">Manual Mailgun check</field>
<field name="model">mail.tracking.email</field>
<field name="inherit_id" ref="mail_tracking.view_mail_tracking_email_form"/>
<field name="inherit_id" ref="mail_tracking.view_mail_tracking_email_form" />
<field name="arch" type="xml">
<field name="state" position="before">
<button name="action_manual_check_mailgun"
type="object" string="Re-sync Mailgun"/>
<button
name="action_manual_check_mailgun"
type="object"
string="Re-sync Mailgun"
/>
</field>
</field>
</record>

View File

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8" ?>
<!-- Copyright 2016 Tecnativa - Pedro M. Baeza
Copyright 2016 Carlos Dauden - Tecnativa <carlos.dauden@tecnativa.com>
Copyright 2017 Tecnativa <vicent.cubellsn@tecnativa.com>
@ -8,18 +8,44 @@
<record id="view_partner_form_mailgun" model="ir.ui.view">
<field name="name">Partner Mailgun button</field>
<field name="model">res.partner</field>
<field name="inherit_id" ref="mail_tracking.view_partner_form"/>
<field name="inherit_id" ref="mail_tracking.view_partner_form" />
<field name="arch" type="xml">
<field name="email_bounced" position="after">
<label for="check_email_bounced" string="Mailgun"
attrs="{'invisible': [('email', '=', False)]}"/>
<div name="mailgun_buttons" attrs="{'invisible': [('email', '=', False)]}">
<button name="check_email_bounced" type="object" string="Check Mailgun" class="oe_link"/>
<button name="check_email_validity" type="object" string="Check email validity" class="oe_link"/>
<button name="force_set_bounced" type="object" string="Set Bounced" class="oe_link"
attrs="{'invisible': [('email_bounced', '=', True)]}"/>
<button name="force_unset_bounced" type="object" string="Unset Bounced" class="oe_link"
attrs="{'invisible': [('email_bounced', '=', False)]}"/>
<label
for="check_email_bounced"
string="Mailgun"
attrs="{'invisible': [('email', '=', False)]}"
/>
<div
name="mailgun_buttons"
attrs="{'invisible': [('email', '=', False)]}"
>
<button
name="check_email_bounced"
type="object"
string="Check Mailgun"
class="oe_link"
/>
<button
name="check_email_validity"
type="object"
string="Check email validity"
class="oe_link"
/>
<button
name="force_set_bounced"
type="object"
string="Set Bounced"
class="oe_link"
attrs="{'invisible': [('email_bounced', '=', True)]}"
/>
<button
name="force_unset_bounced"
type="object"
string="Unset Bounced"
class="oe_link"
attrs="{'invisible': [('email_bounced', '=', False)]}"
/>
</div>
</field>
</field>

View File

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8" ?>
<!-- Copyright 2016 Antonio Espinosa - <antonio.espinosa@tecnativa.com>
Copyright 2017 Vicent Cubells - <vicent.cubells@tecnativa.com>
Copyright 2017 David Vidal - <david.vidal@tecnativa.com>
@ -19,15 +19,27 @@
<field name="domain">[('mass_mailing_id', '!=', False)]</field>
</record>
<menuitem name="Mail tracking" id="mail_tracking_menu"
parent="mass_mailing.mass_mailing_menu_root" sequence="50"/>
<menuitem
name="Mail tracking"
id="mail_tracking_menu"
parent="mass_mailing.mass_mailing_menu_root"
sequence="50"
/>
<menuitem name="Emails" id="mail_tracking_email_menu"
parent="mail_tracking_menu" sequence="1"
action="action_view_mail_tracking_email"/>
<menuitem
name="Emails"
id="mail_tracking_email_menu"
parent="mail_tracking_menu"
sequence="1"
action="action_view_mail_tracking_email"
/>
<menuitem name="Events" id="mail_tracking_event_menu"
parent="mail_tracking_menu" sequence="2"
action="action_view_mail_tracking_event"/>
<menuitem
name="Events"
id="mail_tracking_event_menu"
parent="mail_tracking_menu"
sequence="2"
action="action_view_mail_tracking_event"
/>
</odoo>

View File

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8" ?>
<!-- Copyright 2016 Antonio Espinosa - <antonio.espinosa@tecnativa.com>
Copyright 2017 Vicent Cubells - <vicent.cubells@tecnativa.com>
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -->
@ -7,23 +7,26 @@
<record model="ir.ui.view" id="mailing_trace_view_form">
<field name="name">Add tracking email info</field>
<field name="model">mailing.trace</field>
<field name="inherit_id" ref="mass_mailing.mailing_trace_view_form"/>
<field name="inherit_id" ref="mass_mailing.mailing_trace_view_form" />
<field name="arch" type="xml">
<xpath expr="//form/sheet" position="inside">
<group>
<field name="mail_tracking_id" />
</group>
<label for="tracking_event_ids"/>
<label for="tracking_event_ids" />
<div>
<field name="tracking_event_ids">
<tree string="Tracking events" colors="grey:event_type in ('deferral');black:event_type in ('send');red:event_type in ('hard_bounce', 'soft_bounce', 'spam', 'reject');blue:event_type in ('unsub', 'click', 'open')">
<field name="time"/>
<field name="event_type"/>
<field name="ip"/>
<field name="url"/>
<field name="user_country_id" string="Country"/>
<field name="os_family" string="OS"/>
<field name="ua_family" string="User agent"/>
<tree
string="Tracking events"
colors="grey:event_type in ('deferral');black:event_type in ('send');red:event_type in ('hard_bounce', 'soft_bounce', 'spam', 'reject');blue:event_type in ('unsub', 'click', 'open')"
>
<field name="time" />
<field name="event_type" />
<field name="ip" />
<field name="url" />
<field name="user_country_id" string="Country" />
<field name="os_family" string="OS" />
<field name="ua_family" string="User agent" />
</tree>
</field>
</div>

View File

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8" ?>
<!-- Copyright 2016 Antonio Espinosa - <antonio.espinosa@tecnativa.com>
Copyright 2017 Vicent Cubells - <vicent.cubells@tecnativa.com>
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -->
@ -7,11 +7,11 @@
<record model="ir.ui.view" id="view_mail_tracking_email_form">
<field name="name">Add mass mailing and mail stadistics</field>
<field name="model">mail.tracking.email</field>
<field name="inherit_id" ref="mail_tracking.view_mail_tracking_email_form"/>
<field name="inherit_id" ref="mail_tracking.view_mail_tracking_email_form" />
<field name="arch" type="xml">
<field name="date" position="after">
<field name="mass_mailing_id"/>
<field name="mail_stats_id"/>
<field name="mass_mailing_id" />
<field name="mail_stats_id" />
</field>
</field>
</record>

View File

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8" ?>
<!-- Copyright 2016 Antonio Espinosa - <antonio.espinosa@tecnativa.com>
Copyright 2017 Vicent Cubells - <vicent.cubells@tecnativa.com>
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -->
@ -7,11 +7,11 @@
<record model="ir.ui.view" id="view_mail_mass_mailing_contact_tree">
<field name="name">Add email score and stars</field>
<field name="model">mailing.contact</field>
<field name="inherit_id" ref="mass_mailing.view_mail_mass_mailing_contact_tree"/>
<field name="inherit_id" ref="mass_mailing.view_mail_mass_mailing_contact_tree" />
<field name="arch" type="xml">
<field name="is_blacklisted" position="after">
<field name="email_bounced"/>
<field name="email_score" widget="progressbar"/>
<field name="email_bounced" />
<field name="email_score" widget="progressbar" />
</field>
</field>
</record>
@ -19,11 +19,14 @@
<record model="ir.ui.view" id="view_mail_mass_mailing_contact_search">
<field name="name">Filter bounced contacts</field>
<field name="model">mailing.contact</field>
<field name="inherit_id" ref="mass_mailing.view_mail_mass_mailing_contact_search"/>
<field name="inherit_id" ref="mass_mailing.view_mail_mass_mailing_contact_search" />
<field name="arch" type="xml">
<filter name="filter_not_email_bl" position="after">
<filter string="Email bounced" name="email_bounced"
domain="[('email_bounced', '=', True)]"/>
<filter
string="Email bounced"
name="email_bounced"
domain="[('email_bounced', '=', True)]"
/>
</filter>
</field>
</record>

View File

@ -1,39 +1,38 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8" ?>
<!-- © 2016 Jairo Llopis <jairo.llopis@tecnativa.com>
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -->
<data noupdate="1">
<record id="reason_not_interested"
model="mail.unsubscription.reason"
forcecreate="False">
<record
id="reason_not_interested"
model="mail.unsubscription.reason"
forcecreate="False"
>
<field name="name">I'm not interested</field>
<field name="sequence">10</field>
<field name="details_required" eval="False"/>
<field name="details_required" eval="False" />
</record>
<record id="reason_not_requested"
model="mail.unsubscription.reason"
forcecreate="False">
<record
id="reason_not_requested"
model="mail.unsubscription.reason"
forcecreate="False"
>
<field name="name">I did not request this</field>
<field name="sequence">20</field>
<field name="details_required" eval="False"/>
<field name="details_required" eval="False" />
</record>
<record id="reason_too_many"
model="mail.unsubscription.reason"
forcecreate="False">
<record id="reason_too_many" model="mail.unsubscription.reason" forcecreate="False">
<field name="name">I get too many emails</field>
<field name="sequence">30</field>
<field name="details_required" eval="False"/>
<field name="details_required" eval="False" />
</record>
<record id="reason_other"
model="mail.unsubscription.reason"
forcecreate="False">
<record id="reason_other" model="mail.unsubscription.reason" forcecreate="False">
<field name="name">Other reason</field>
<field name="sequence">100</field>
<field name="details_required" eval="True"/>
<field name="details_required" eval="True" />
</record>
</data>

View File

@ -1,25 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8" ?>
<!-- Copyright 2016 Jairo Llopis <jairo.llopis@tecnativa.com>
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -->
<odoo>
<template id="assets_frontend_demo"
inherit_id="web.assets_frontend">
<template id="assets_frontend_demo" inherit_id="web.assets_frontend">
<xpath expr=".">
<script type="text/javascript"
src="/mass_mailing_custom_unsubscribe/static/src/js/contact.tour.js"/>
<script type="text/javascript"
src="/mass_mailing_custom_unsubscribe/static/src/js/partner.tour.js"/>
<script
type="text/javascript"
src="/mass_mailing_custom_unsubscribe/static/src/js/contact.tour.js"
/>
<script
type="text/javascript"
src="/mass_mailing_custom_unsubscribe/static/src/js/partner.tour.js"
/>
</xpath>
</template>
<!--This is set here to make tours work-->
<template id="mass_mailing_custom_unsubscribe.layout"
inherit_id="mass_mailing.layout">
<template id="mass_mailing_custom_unsubscribe.layout" inherit_id="mass_mailing.layout">
<xpath expr="//t[@t-set='head']" position="inside">
<t t-call-assets="web_editor.assets_wysiwyg"/>
<t t-call-assets="web.assets_frontend"/>
<t t-call-assets="web_editor.assets_wysiwyg" />
<t t-call-assets="web.assets_frontend" />
</xpath>
</template>

View File

@ -1,102 +1,100 @@
/* Copyright 2016 Jairo Llopis <jairo.llopis@tecnativa.com>
* License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). */
odoo.define("mass_mailing_custom_unsubscribe.contact_tour",
function (require) {
"use strict";
var base = require("web_editor.base");
var tour = require("web_tour.tour");
odoo.define("mass_mailing_custom_unsubscribe.contact_tour", function(require) {
"use strict";
var base = require("web_editor.base");
var tour = require("web_tour.tour");
// Allow to know if an element is required
$.extend($.expr[':'], {
propRequired: function (element) {
return $(element).prop("required");
},
});
// Allow to know if an element is required
$.extend($.expr[":"], {
propRequired: function(element) {
return $(element).prop("required");
},
});
tour.register(
"mass_mailing_custom_unsubscribe_tour_contact",
tour.register(
"mass_mailing_custom_unsubscribe_tour_contact",
{
test: true,
wait_for: base.ready(),
},
[
{
test: true,
wait_for: base.ready(),
content: "Choose other reason",
trigger: ".radio:contains('Other reason') :radio:not(:checked)",
extra_trigger: "#reason_form #custom_div_feedback",
},
[
{
content: "Choose other reason",
trigger: ".radio:contains('Other reason') :radio:not(:checked)",
extra_trigger: "#reason_form #custom_div_feedback",
},
{
content: "Switch to not interested reason",
trigger:
".radio:contains(\"I'm not interested\") :radio:not(:checked)",
extra_trigger: "[name='details']:propRequired",
},
{
content: "Unsubscribe",
trigger: "#reason_form button:submit",
extra_trigger: "body:not(:has([name='details']:propRequired))",
},
{
content: "Successfully unsubscribed",
trigger: "body:not(:has(#reason_form)) #subscription_info " +
":contains('successfully unsubscribed from')",
},
{
content: "Unsubscription reasons are invisible",
trigger: "#unsubscribe_form:has(#custom_div_feedback:hidden)",
},
{
content: "List 2 is not cross unsubscriptable",
trigger: "body:not(:has(li:contains('test list 2')))",
},
{
content: "List 3 is not public",
trigger: "body:not(:has(li:contains('test list 3')))",
},
{
content: "Uncheck list 1",
trigger: "li:contains('test list 1') input:checked",
},
{
content: "Choose other reason",
trigger: ".radio:contains('Other reason') :radio",
extra_trigger: ".radio:contains('Other reason') " +
":radio:not(:checked)",
},
{
content: "Add details to reason",
trigger: "[name='details']:visible:propRequired",
run: "text I want to unsubscribe because I want. Period.",
extra_trigger: ".radio:contains('Other reason') :radio:checked",
},
{
content: "Update subscriptions 2nd time",
trigger: "#unsubscribe_form :submit",
},
{
content: "Successfully unsubscribed",
trigger:
"#subscription_info:contains('Your changes have been saved.')",
},
{
content: "Subscribe again to list 0",
// eslint-disable-next-line no-multi-str
trigger:"body:not(:has(#unsubscribe_form #custom_div_feedback\
{
content: "Switch to not interested reason",
trigger: '.radio:contains("I\'m not interested") :radio:not(:checked)',
extra_trigger: "[name='details']:propRequired",
},
{
content: "Unsubscribe",
trigger: "#reason_form button:submit",
extra_trigger: "body:not(:has([name='details']:propRequired))",
},
{
content: "Successfully unsubscribed",
trigger:
"body:not(:has(#reason_form)) #subscription_info " +
":contains('successfully unsubscribed from')",
},
{
content: "Unsubscription reasons are invisible",
trigger: "#unsubscribe_form:has(#custom_div_feedback:hidden)",
},
{
content: "List 2 is not cross unsubscriptable",
trigger: "body:not(:has(li:contains('test list 2')))",
},
{
content: "List 3 is not public",
trigger: "body:not(:has(li:contains('test list 3')))",
},
{
content: "Uncheck list 1",
trigger: "li:contains('test list 1') input:checked",
},
{
content: "Choose other reason",
trigger: ".radio:contains('Other reason') :radio",
extra_trigger:
".radio:contains('Other reason') " + ":radio:not(:checked)",
},
{
content: "Add details to reason",
trigger: "[name='details']:visible:propRequired",
run: "text I want to unsubscribe because I want. Period.",
extra_trigger: ".radio:contains('Other reason') :radio:checked",
},
{
content: "Update subscriptions 2nd time",
trigger: "#unsubscribe_form :submit",
},
{
content: "Successfully unsubscribed",
trigger: "#subscription_info:contains('Your changes have been saved.')",
},
{
content: "Subscribe again to list 0",
// eslint-disable-next-line no-multi-str
trigger:
"body:not(:has(#unsubscribe_form #custom_div_feedback\
:visible)):has(.alert-success) li:contains('test list 0') \
input:not(:checked)",
},
{
content: "Update subscriptions 3nd time",
// eslint-disable-next-line no-multi-str
trigger:"#unsubscribe_form:not(\
},
{
content: "Update subscriptions 3nd time",
// eslint-disable-next-line no-multi-str
trigger:
"#unsubscribe_form:not(\
:has(.js_unsubscription_reason:visible)) :submit",
},
{
content: "Successfully subscribed",
trigger:
"#subscription_info:contains('Your changes have been saved.')",
},
]
);
}
);
},
{
content: "Successfully subscribed",
trigger: "#subscription_info:contains('Your changes have been saved.')",
},
]
);
});

View File

@ -1,48 +1,48 @@
/* Copyright 2016 Jairo Llopis <jairo.llopis@tecnativa.com>
* License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). */
odoo.define("mass_mailing_custom_unsubscribe.partner_tour",
function (require) {
"use strict";
var base = require("web_editor.base");
var tour = require("web_tour.tour");
odoo.define("mass_mailing_custom_unsubscribe.partner_tour", function(require) {
"use strict";
var base = require("web_editor.base");
var tour = require("web_tour.tour");
// Allow to know if an element is required
$.extend($.expr[':'], {
propRequired: function (element) {
return $(element).prop("required");
},
});
// Allow to know if an element is required
$.extend($.expr[":"], {
propRequired: function(element) {
return $(element).prop("required");
},
});
tour.register(
"mass_mailing_custom_unsubscribe_tour_partner",
tour.register(
"mass_mailing_custom_unsubscribe_tour_partner",
{
tour: true,
wait_for: base.ready(),
},
[
{
tour: true,
wait_for: base.ready(),
content: "Choose other reason",
trigger: ".radio:contains('Other reason') :radio:not(:checked)",
extra_trigger: "#reason_form #custom_div_feedback",
},
[
{
content: "Choose other reason",
trigger: ".radio:contains('Other reason') :radio:not(:checked)",
extra_trigger: "#reason_form #custom_div_feedback",
},
{
content: "Switch to not interested reason",
// eslint-disable-next-line no-multi-str
trigger: ".radio:contains(\"I'm not interested\") \
:radio:not(:checked)",
extra_trigger: "[name='details']:propRequired",
},
{
content: "Unsubscribe",
trigger: "#reason_form button:submit",
extra_trigger: "body:not(:has([name='details']:propRequired))",
},
{
content: "Successfully unsubscribed",
trigger: "body:not(:has(#reason_form)) #subscription_info " +
":contains('successfully unsubscribed')",
},
]
);
}
);
{
content: "Switch to not interested reason",
// eslint-disable-next-line no-multi-str
trigger:
'.radio:contains("I\'m not interested") \
:radio:not(:checked)',
extra_trigger: "[name='details']:propRequired",
},
{
content: "Unsubscribe",
trigger: "#reason_form button:submit",
extra_trigger: "body:not(:has([name='details']:propRequired))",
},
{
content: "Successfully unsubscribed",
trigger:
"body:not(:has(#reason_form)) #subscription_info " +
":contains('successfully unsubscribed')",
},
]
);
});

View File

@ -5,30 +5,30 @@
* to extend it as it is currently designed. It is almost a copy+paste from
* upstream, to allow easier version/patch updates, so linter is disabled. */
/* eslint-disable */
odoo.define('mass_mailing_custom_unsubscribe.unsubscribe', function (require) {
'use strict';
odoo.define("mass_mailing_custom_unsubscribe.unsubscribe", function(require) {
"use strict";
var ajax = require('web.ajax');
var core = require('web.core');
require('web.dom_ready');
var ajax = require("web.ajax");
var core = require("web.core");
require("web.dom_ready");
var _t = core._t;
if (!$('.o_unsubscribe_form').length) {
if (!$(".o_unsubscribe_form").length) {
return $.Deferred().reject("DOM doesn't contain '.o_unsubscribe_form'");
}
var email = $("input[name='email']").val();
var mailing_id = parseInt($("input[name='mailing_id']").val(), 10);
var res_id = parseInt($("input[name='res_id']").val(), 10);
var token = (location.search.split('token' + '=')[1] || '').split('&')[0];
var token = (location.search.split("token" + "=")[1] || "").split("&")[0];
var $mailing_lists = $("input[name='contact_ids']");
var $reasons = $("#custom_div_feedback");
var $details = $("textarea[name='details']");
var $radio = $(":radio");
var $info_state = $("#info_state, #custom_div_feedback");
$radio.on('change click', function (e) {
$radio.on("change click", function(e) {
$details.prop(
"required",
$(e.target).is("[data-details-required]") && $(e.target).is(":visible")
@ -36,9 +36,9 @@ odoo.define('mass_mailing_custom_unsubscribe.unsubscribe', function (require) {
});
// Display reasons form only if there are unsubscriptions
var toggle_reasons = function () {
var toggle_reasons = function() {
// Find contacts that were checked and now are unchecked
var $disabled = $mailing_lists.filter(function () {
var $disabled = $mailing_lists.filter(function() {
var $this = $(this);
return !$this.prop("checked") && $this.attr("checked");
});
@ -47,11 +47,12 @@ odoo.define('mass_mailing_custom_unsubscribe.unsubscribe', function (require) {
var $radios = $reasons.find(":radio");
if ($reasons.is(":hidden")) {
// Uncheck chosen reason
$radios.prop("checked", false)
// Unrequire specifying a reason
.prop("required", false)
// Remove possible constraints for details
.trigger("change");
$radios
.prop("checked", false)
// Unrequire specifying a reason
.prop("required", false)
// Remove possible constraints for details
.trigger("change");
// Clear textarea
$details.val("");
} else {
@ -60,76 +61,87 @@ odoo.define('mass_mailing_custom_unsubscribe.unsubscribe', function (require) {
}
};
$mailing_lists.change(function (e) {
$mailing_lists.change(function(e) {
toggle_reasons();
$('#info_state').addClass('invisible');
$("#info_state").addClass("invisible");
});
if (email != '' && email != undefined) {
ajax.jsonRpc('/mailing/blacklist/check', 'call', {
'email': email,
'mailing_id': mailing_id,
'res_id': res_id,
'token': token
if (email != "" && email != undefined) {
ajax.jsonRpc("/mailing/blacklist/check", "call", {
email: email,
mailing_id: mailing_id,
res_id: res_id,
token: token,
})
.then(function (result) {
if (result == 'unauthorized') {
$('#button_add_blacklist').hide();
$('#button_remove_blacklist').hide();
}
else if (result == true) {
$('#button_remove_blacklist').show();
.then(function(result) {
if (result == "unauthorized") {
$("#button_add_blacklist").hide();
$("#button_remove_blacklist").hide();
} else if (result == true) {
$("#button_remove_blacklist").show();
toggle_opt_out_section(false);
}
else if (result == false) {
$('#button_add_blacklist').show();
} else if (result == false) {
$("#button_add_blacklist").show();
toggle_opt_out_section(true);
}
else {
$('#subscription_info').html(_t('An error occured. Please try again later or contact us.'));
$info_state.removeClass('alert-success').removeClass('alert-info').removeClass('alert-warning').addClass('alert-error');
} else {
$("#subscription_info").html(
_t("An error occured. Please try again later or contact us.")
);
$info_state
.removeClass("alert-success")
.removeClass("alert-info")
.removeClass("alert-warning")
.addClass("alert-error");
}
})
.guardedCatch(function () {
$('#subscription_info').html(_t('An error occured. Please try again later or contact us.'));
$info_state.removeClass('alert-success').removeClass('alert-info').removeClass('alert-warning').addClass('alert-error');
.guardedCatch(function() {
$("#subscription_info").html(
_t("An error occured. Please try again later or contact us.")
);
$info_state
.removeClass("alert-success")
.removeClass("alert-info")
.removeClass("alert-warning")
.addClass("alert-error");
});
}
else {
$('#div_blacklist').hide();
} else {
$("#div_blacklist").hide();
}
var unsubscribed_list = $("input[name='unsubscribed_list']").val();
if (unsubscribed_list){
$('#subscription_info').html(_.str.sprintf(
_t("You have been <strong>successfully unsubscribed from %s</strong>."),
unsubscribed_list
));
}
else{
$('#subscription_info').html(_t('You have been <strong>successfully unsubscribed</strong>.'));
if (unsubscribed_list) {
$("#subscription_info").html(
_.str.sprintf(
_t("You have been <strong>successfully unsubscribed from %s</strong>."),
unsubscribed_list
)
);
} else {
$("#subscription_info").html(
_t("You have been <strong>successfully unsubscribed</strong>.")
);
}
$('#unsubscribe_form').on('submit', function (e) {
$("#unsubscribe_form").on("submit", function(e) {
e.preventDefault();
var checked_ids = [];
$("input[type='checkbox']:checked").each(function (i) {
$("input[type='checkbox']:checked").each(function(i) {
checked_ids[i] = parseInt($(this).val(), 10);
});
var unchecked_ids = [];
$("input[type='checkbox']:not(:checked)").each(function (i) {
$("input[type='checkbox']:not(:checked)").each(function(i) {
unchecked_ids[i] = parseInt($(this).val(), 10);
});
var values = {
'opt_in_ids': checked_ids,
'opt_out_ids': unchecked_ids,
'email': email,
'mailing_id': mailing_id,
'res_id': res_id,
'token': token
opt_in_ids: checked_ids,
opt_out_ids: unchecked_ids,
email: email,
mailing_id: mailing_id,
res_id: res_id,
token: token,
};
// Only send reason and details if an unsubscription was found
if ($reasons.is(":visible")) {
@ -140,41 +152,53 @@ odoo.define('mass_mailing_custom_unsubscribe.unsubscribe', function (require) {
values.details = $details.val();
}
ajax.jsonRpc('/mail/mailing/unsubscribe', 'call', values)
.then(function (result) {
if (result == 'unauthorized') {
$('#info_state').removeClass('invisible');
$('#subscription_info').html(_t('You are not authorized to do this!'));
$info_state.removeClass('alert-success').removeClass('alert-info').removeClass('alert-error').addClass('alert-warning');
}
else if (result == true) {
$('#info_state').removeClass('invisible');
$('#subscription_info').html(_t('Your changes have been saved.'));
$info_state.removeClass('alert-info').addClass('alert-success');
ajax.jsonRpc("/mail/mailing/unsubscribe", "call", values)
.then(function(result) {
if (result == "unauthorized") {
$("#info_state").removeClass("invisible");
$("#subscription_info").html(
_t("You are not authorized to do this!")
);
$info_state
.removeClass("alert-success")
.removeClass("alert-info")
.removeClass("alert-error")
.addClass("alert-warning");
} else if (result == true) {
$("#info_state").removeClass("invisible");
$("#subscription_info").html(_t("Your changes have been saved."));
$info_state.removeClass("alert-info").addClass("alert-success");
// Store checked status, to enable further changes
$mailing_lists.each(function () {
$mailing_lists.each(function() {
var $this = $(this);
$this.attr("checked", $this.prop("checked"));
});
toggle_reasons();
}
else {
$('#info_state').removeClass('invisible');
$('#subscription_info').html(_t('An error occurred. Your changes have not been saved, try again later.'));
$info_state.removeClass('alert-info').addClass('alert-warning');
} else {
$("#info_state").removeClass("invisible");
$("#subscription_info").html(
_t(
"An error occurred. Your changes have not been saved, try again later."
)
);
$info_state.removeClass("alert-info").addClass("alert-warning");
}
})
.guardedCatch(function () {
$('#info_state').removeClass('invisible');
$('#subscription_info').html(_t('An error occurred. Your changes have not been saved, try again later.'));
$info_state.removeClass('alert-info').addClass('alert-warning');
.guardedCatch(function() {
$("#info_state").removeClass("invisible");
$("#subscription_info").html(
_t(
"An error occurred. Your changes have not been saved, try again later."
)
);
$info_state.removeClass("alert-info").addClass("alert-warning");
});
});
// ==================
// Blacklist
// ==================
$('#button_add_blacklist').click(function (e) {
$("#button_add_blacklist").click(function(e) {
e.preventDefault();
if ($reasons.is(":hidden")) {
@ -185,104 +209,161 @@ odoo.define('mass_mailing_custom_unsubscribe.unsubscribe', function (require) {
return;
}
ajax.jsonRpc('/mailing/blacklist/add', 'call', {
'email': email,
'mailing_id': mailing_id,
'res_id': res_id,
'token': token,
'reason_id': parseInt($reasons.find("[name='reason_id']:checked").val(), 10),
'details': $details.val(),
ajax.jsonRpc("/mailing/blacklist/add", "call", {
email: email,
mailing_id: mailing_id,
res_id: res_id,
token: token,
reason_id: parseInt($reasons.find("[name='reason_id']:checked").val(), 10),
details: $details.val(),
})
.then(function (result) {
if (result == 'unauthorized') {
$('#info_state').removeClass('invisible');
$('#subscription_info').html(_t('You are not authorized to do this!'));
$info_state.removeClass('alert-success').removeClass('alert-info').removeClass('alert-error').addClass('alert-warning');
}
else {
.then(function(result) {
if (result == "unauthorized") {
$("#info_state").removeClass("invisible");
$("#subscription_info").html(
_t("You are not authorized to do this!")
);
$info_state
.removeClass("alert-success")
.removeClass("alert-info")
.removeClass("alert-error")
.addClass("alert-warning");
} else {
if (result) {
$('#info_state').removeClass('invisible');
$('#subscription_info').html(_t('You have been successfully <strong>added to our blacklist</strong>. '
+ 'You will not be contacted anymore by our services.'));
$info_state.removeClass('alert-warning').removeClass('alert-info').removeClass('alert-error').addClass('alert-success');
$("#info_state").removeClass("invisible");
$("#subscription_info").html(
_t(
"You have been successfully <strong>added to our blacklist</strong>. " +
"You will not be contacted anymore by our services."
)
);
$info_state
.removeClass("alert-warning")
.removeClass("alert-info")
.removeClass("alert-error")
.addClass("alert-success");
toggle_opt_out_section(false);
// set mailing lists checkboxes to previous state
$mailing_lists.each(function () {
$mailing_lists.each(function() {
var $this = $(this);
$this.prop("checked", $(this)[0].hasAttribute("checked"));
});
// Hide reasons and reset reason fields
$reasons.toggleClass("d-none", true)
.find(":radio").prop("checked", false);
$reasons
.toggleClass("d-none", true)
.find(":radio")
.prop("checked", false);
$details.val("").prop("required", false);
} else {
$("#info_state").removeClass("invisible");
$("#subscription_info").html(
_t(
"An error occured. Please try again later or contact us."
)
);
$info_state
.removeClass("alert-success")
.removeClass("alert-info")
.removeClass("alert-warning")
.addClass("alert-error");
}
else {
$('#info_state').removeClass('invisible');
$('#subscription_info').html(_t('An error occured. Please try again later or contact us.'));
$info_state.removeClass('alert-success').removeClass('alert-info').removeClass('alert-warning').addClass('alert-error');
}
$('#button_add_blacklist').hide();
$('#button_remove_blacklist').show();
$('#unsubscribed_info').hide();
$("#button_add_blacklist").hide();
$("#button_remove_blacklist").show();
$("#unsubscribed_info").hide();
}
})
.guardedCatch(function () {
$('#subscription_info').html(_t('An error occured. Please try again later or contact us.'));
$info_state.removeClass('alert-success').removeClass('alert-info').removeClass('alert-warning').addClass('alert-error');
.guardedCatch(function() {
$("#subscription_info").html(
_t("An error occured. Please try again later or contact us.")
);
$info_state
.removeClass("alert-success")
.removeClass("alert-info")
.removeClass("alert-warning")
.addClass("alert-error");
});
});
$('#button_remove_blacklist').click(function (e) {
$("#button_remove_blacklist").click(function(e) {
e.preventDefault();
ajax.jsonRpc('/mailing/blacklist/remove', 'call', {
'email': email,
'mailing_id': mailing_id,
'res_id': res_id,
'token': token
ajax.jsonRpc("/mailing/blacklist/remove", "call", {
email: email,
mailing_id: mailing_id,
res_id: res_id,
token: token,
})
.then(function (result) {
if (result == 'unauthorized') {
$('#info_state').removeClass('invisible');
$('#subscription_info').html(_t('You are not authorized to do this!'));
$info_state.removeClass('alert-success').removeClass('alert-info').removeClass('alert-error').addClass('alert-warning');
}
else {
.then(function(result) {
if (result == "unauthorized") {
$("#info_state").removeClass("invisible");
$("#subscription_info").html(
_t("You are not authorized to do this!")
);
$info_state
.removeClass("alert-success")
.removeClass("alert-info")
.removeClass("alert-error")
.addClass("alert-warning");
} else {
if (result) {
$('#info_state').removeClass('invisible');
$('#subscription_info').html(_t("You have been successfully <strong>removed from our blacklist</strong>. "
+ "You are now able to be contacted by our services."));
$info_state.removeClass('alert-warning').removeClass('alert-info').removeClass('alert-error').addClass('alert-success');
$("#info_state").removeClass("invisible");
$("#subscription_info").html(
_t(
"You have been successfully <strong>removed from our blacklist</strong>. " +
"You are now able to be contacted by our services."
)
);
$info_state
.removeClass("alert-warning")
.removeClass("alert-info")
.removeClass("alert-error")
.addClass("alert-success");
toggle_opt_out_section(true);
} else {
$("#info_state").removeClass("invisible");
$("#subscription_info").html(
_t(
"An error occured. Please try again later or contact us."
)
);
$info_state
.removeClass("alert-success")
.removeClass("alert-info")
.removeClass("alert-warning")
.addClass("alert-error");
}
else {
$('#info_state').removeClass('invisible');
$('#subscription_info').html(_t('An error occured. Please try again later or contact us.'));
$info_state.removeClass('alert-success').removeClass('alert-info').removeClass('alert-warning').addClass('alert-error');
}
$('#button_add_blacklist').show();
$('#button_remove_blacklist').hide();
$('#unsubscribed_info').hide();
$("#button_add_blacklist").show();
$("#button_remove_blacklist").hide();
$("#unsubscribed_info").hide();
}
})
.guardedCatch(function () {
$('#info_state').removeClass('invisible');
$('#subscription_info').html(_t('An error occured. Please try again later or contact us.'));
$info_state.removeClass('alert-success').removeClass('alert-info').removeClass('alert-warning').addClass('alert-error');
.guardedCatch(function() {
$("#info_state").removeClass("invisible");
$("#subscription_info").html(
_t("An error occured. Please try again later or contact us.")
);
$info_state
.removeClass("alert-success")
.removeClass("alert-info")
.removeClass("alert-warning")
.addClass("alert-error");
});
});
});
function toggle_opt_out_section(value) {
var result = !value;
$("#div_opt_out").find('*').attr('disabled', result);
$("#button_add_blacklist").attr('disabled', false);
$("#button_remove_blacklist").attr('disabled', false);
$("#custom_div_feedback").find('*').attr('disabled', false);
$("#div_opt_out")
.find("*")
.attr("disabled", result);
$("#button_add_blacklist").attr("disabled", false);
$("#button_remove_blacklist").attr("disabled", false);
$("#custom_div_feedback")
.find("*")
.attr("disabled", false);
if (value) {
$('[name="button_subscription"]').addClass('clickable');
}
else {
$('[name="button_subscription"]').removeClass('clickable');
$('[name="button_subscription"]').addClass("clickable");
} else {
$('[name="button_subscription"]').removeClass("clickable");
}
}

View File

@ -1,24 +1,34 @@
<?xml version="1.0" encoding="utf-8" ?>
<!-- Copyright 2016 Jairo Llopis <jairo.llopis@tecnativa.com>
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -->
<odoo>
<template id="reason" name="UI for Providing Unsubscription Reasons">
<div id="custom_div_feedback" t-attf-class="alert alert-success mt-4 #{extra_class or ''}" role="status">
<p>We would appreciate if you provide feedback about why you updated<br/>your subscriptions
<div
id="custom_div_feedback"
t-attf-class="alert alert-success mt-4 #{extra_class or ''}"
role="status"
>
<p>We would appreciate if you provide feedback about why you updated<br
/>your subscriptions
</p>
<div class="col-md-12 mb16">
<t t-foreach="reasons" t-as="reason">
<div class="radio">
<label>
<input type="radio" name="reason_id" t-att-data-details-required="reason.details_required" t-att-value="reason.id" required="required"/>
<t t-esc="reason.display_name"/>
<input
type="radio"
name="reason_id"
t-att-data-details-required="reason.details_required"
t-att-value="reason.id"
required="required"
/>
<t t-esc="reason.display_name" />
</label>
</div>
</t>
</div>
<textarea class="form-control" name="details" cols="60" rows="3"></textarea>
<textarea class="form-control" name="details" cols="60" rows="3" />
</div>
</template>
@ -26,12 +36,25 @@
<t t-call="mass_mailing.layout">
<div class="container o_unsubscribe_form">
<div class="row">
<form id="reason_form" t-attf-action="/mail/mailing/#{mailing_id}/unsubscribe?token=#{token}" method="post" class="col-lg-6 offset-lg-3 mt-4">
<input type="hidden" name="csrf_token" t-att-value="request.csrf_token()"/>
<input type="hidden" name="email" t-att-value="email"/>
<input type="hidden" name="mailing_id" t-att-value="mailing_id"/>
<input type="hidden" name="res_id" t-att-value="res_id"/>
<t t-call="mass_mailing_custom_unsubscribe.reason"/>
<form
id="reason_form"
t-attf-action="/mail/mailing/#{mailing_id}/unsubscribe?token=#{token}"
method="post"
class="col-lg-6 offset-lg-3 mt-4"
>
<input
type="hidden"
name="csrf_token"
t-att-value="request.csrf_token()"
/>
<input type="hidden" name="email" t-att-value="email" />
<input
type="hidden"
name="mailing_id"
t-att-value="mailing_id"
/>
<input type="hidden" name="res_id" t-att-value="res_id" />
<t t-call="mass_mailing_custom_unsubscribe.reason" />
<div class="form-group mb16 mt16">
<button type="submit" class="btn btn-danger">
Unsubscribe now

View File

@ -1,31 +1,36 @@
<?xml version="1.0" encoding="utf-8" ?>
<!-- Copyright 2016 Jairo Llopis <jairo.llopis@tecnativa.com>
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -->
<odoo>
<template id="unsubscribe" inherit_id="mass_mailing.unsubscribe" name="Add Reasons to Mailing List Management Form">
<template
id="unsubscribe"
inherit_id="mass_mailing.unsubscribe"
name="Add Reasons to Mailing List Management Form"
>
<!-- Hide original feedback textarea to put another one after mailing lists checkboxes -->
<xpath expr="//div[@id='div_feedback']" position="attributes">
<attribute name="class" add="d-none" separator=" "/>
<attribute name="class" add="d-none" separator=" " />
</xpath>
<!-- Add reasons to mass mailing list manager -->
<xpath expr="//ul[hasclass('list-group')]" position="after">
<t t-call="mass_mailing_custom_unsubscribe.reason">
<t t-set="extra_class" t-value="'d-none'"/>
<t t-set="extra_class" t-value="'d-none'" />
</t>
</xpath>
</template>
<template id="unsubscribed"
inherit_id="mass_mailing.unsubscribed"
name="Add Reasons to Blacklist Management Form">
<template
id="unsubscribed"
inherit_id="mass_mailing.unsubscribed"
name="Add Reasons to Blacklist Management Form"
>
<!-- Add reasons to blacklist manager -->
<xpath expr="//div[@id='button_add_blacklist']" position="before">
<form id="unsubscribe_form">
<t t-call="mass_mailing_custom_unsubscribe.reason">
<t t-set="extra_class" t-value="'d-none'"/>
<t t-set="extra_class" t-value="'d-none'" />
</t>
</form>
</xpath>

View File

@ -1,15 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8" ?>
<!-- Copyright 2016 Jairo Llopis <jairo.llopis@tecnativa.com>
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -->
<odoo>
<template id="assets_backend"
inherit_id="mass_mailing.assets_backend">
<template id="assets_backend" inherit_id="mass_mailing.assets_backend">
<!-- Need to replace the asset because upstream is impossible to extend -->
<xpath expr="//script[@src='/mass_mailing/static/src/js/unsubscribe.js']" position="replace">
<script type="text/javascript"
src="/mass_mailing_custom_unsubscribe/static/src/js/unsubscribe.js"/>
<xpath
expr="//script[@src='/mass_mailing/static/src/js/unsubscribe.js']"
position="replace"
>
<script
type="text/javascript"
src="/mass_mailing_custom_unsubscribe/static/src/js/unsubscribe.js"
/>
</xpath>
</template>

View File

@ -1,16 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8" ?>
<!-- Copyright 2016 Pedro M. Baeza <pedro.baeza@tecnativa.com>
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -->
<odoo>
<record id="view_mail_mass_mailing_list_form" model="ir.ui.view">
<field name="model">mailing.list</field>
<field name="inherit_id" ref="mass_mailing.mailing_list_view_form"/>
<field name="inherit_id" ref="mass_mailing.mailing_list_view_form" />
<field name="arch" type="xml">
<field name="is_public" position="after">
<field name="not_cross_unsubscriptable"
attrs="{'invisible': [('is_public', '=', False)]}"/>
<field
name="not_cross_unsubscriptable"
attrs="{'invisible': [('is_public', '=', False)]}"
/>
</field>
</field>
</record>

View File

@ -1,7 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8" ?>
<!-- Copyright 2016 Jairo Llopis <jairo.llopis@tecnativa.com>
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -->
<odoo>
<record id="mail_unsubscription_reason_view_form" model="ir.ui.view">
@ -11,11 +10,11 @@
<form>
<sheet>
<group>
<field name="name"/>
<field name="details_required"/>
<field name="sequence"/>
<field name="name" />
<field name="details_required" />
<field name="sequence" />
</group>
<div class="oe_chatter"/>
<div class="oe_chatter" />
</sheet>
</form>
</field>
@ -26,9 +25,9 @@
<field name="model">mail.unsubscription.reason</field>
<field name="arch" type="xml">
<tree>
<field name="sequence" widget="handle"/>
<field name="name"/>
<field name="details_required"/>
<field name="sequence" widget="handle" />
<field name="name" />
<field name="details_required" />
</tree>
</field>
</record>
@ -38,20 +37,22 @@
<field name="model">mail.unsubscription.reason</field>
<field name="arch" type="xml">
<search>
<field name="name"/>
<field name="details_required"/>
<field name="name" />
<field name="details_required" />
</search>
</field>
</record>
<act_window
id="mail_unsubscription_reason_action"
name="Unsubscription Reasons"
res_model="mail.unsubscription.reason"/>
id="mail_unsubscription_reason_action"
name="Unsubscription Reasons"
res_model="mail.unsubscription.reason"
/>
<menuitem
id="mail_unsubscription_reason_menu"
parent="mass_mailing.mass_mailing_configuration"
action="mail_unsubscription_reason_action"/>
id="mail_unsubscription_reason_menu"
parent="mass_mailing.mass_mailing_configuration"
action="mail_unsubscription_reason_action"
/>
</odoo>

View File

@ -1,7 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8" ?>
<!-- Copyright 2016 Jairo Llopis <jairo.llopis@tecnativa.com>
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -->
<odoo>
<record id="mail_unsubscription_view_form" model="ir.ui.view">
@ -11,26 +10,31 @@
<form>
<sheet>
<group>
<field name="date"/>
<field name="mass_mailing_id"/>
<field name="unsubscriber_id"/>
<field name="mailing_list_ids" widget="many2many_tags"/>
<field name="email"/>
<field name="action"/>
<field name="reason_id"
attrs="{'required': [('action', '=', 'unsubscription')]}"/>
<field name="details"
attrs="{'required': [('details_required', '=', True)]}"/>
<field name="details_required" invisible="True"/>
<field name="metadata"/>
<field name="date" />
<field name="mass_mailing_id" />
<field name="unsubscriber_id" />
<field name="mailing_list_ids" widget="many2many_tags" />
<field name="email" />
<field name="action" />
<field
name="reason_id"
attrs="{'required': [('action', '=', 'unsubscription')]}"
/>
<field
name="details"
attrs="{'required': [('details_required', '=', True)]}"
/>
<field name="details_required" invisible="True" />
<field name="metadata" />
</group>
</sheet>
<div class="oe_chatter">
<field name="message_follower_ids"
widget="mail_followers"
groups="base.group_user"/>
<field name="message_ids"
widget="mail_thread"/>
<field
name="message_follower_ids"
widget="mail_followers"
groups="base.group_user"
/>
<field name="message_ids" widget="mail_thread" />
</div>
</form>
</field>
@ -41,14 +45,14 @@
<field name="model">mail.unsubscription</field>
<field name="arch" type="xml">
<tree decoration-warning="action in ['unsubscription', 'blacklist_add']">
<field name="date"/>
<field name="mass_mailing_id"/>
<field name="unsubscriber_id"/>
<field name="mailing_list_ids" widget="many2many_tags"/>
<field name="email" invisible="True"/>
<field name="action"/>
<field name="reason_id"/>
<field name="details" invisible="True"/>
<field name="date" />
<field name="mass_mailing_id" />
<field name="unsubscriber_id" />
<field name="mailing_list_ids" widget="many2many_tags" />
<field name="email" invisible="True" />
<field name="action" />
<field name="reason_id" />
<field name="details" invisible="True" />
</tree>
</field>
</record>
@ -58,32 +62,44 @@
<field name="model">mail.unsubscription</field>
<field name="arch" type="xml">
<search>
<field name="mass_mailing_id"/>
<field name="unsubscriber_id"/>
<field name="mailing_list_ids"/>
<field name="email"/>
<field name="reason_id"/>
<field name="details"/>
<separator/>
<field name="mass_mailing_id" />
<field name="unsubscriber_id" />
<field name="mailing_list_ids" />
<field name="email" />
<field name="reason_id" />
<field name="details" />
<separator />
<group string="Group by">
<filter name="group_by_month"
<filter
name="group_by_month"
string="Month"
context="{'group_by': 'date:month'}"/>
<filter name="group_by_year"
context="{'group_by': 'date:month'}"
/>
<filter
name="group_by_year"
string="Year"
context="{'group_by': 'date:year'}"/>
<filter name="group_by_action"
context="{'group_by': 'date:year'}"
/>
<filter
name="group_by_action"
string="Action"
context="{'group_by': 'action'}"/>
<filter name="group_by_email"
context="{'group_by': 'action'}"
/>
<filter
name="group_by_email"
string="Email"
context="{'group_by': 'email'}"/>
<filter name="group_by_reason"
context="{'group_by': 'email'}"
/>
<filter
name="group_by_reason"
string="Reason"
context="{'group_by': 'reason_id'}"/>
<filter name="group_by_mass_mailing"
context="{'group_by': 'reason_id'}"
/>
<filter
name="group_by_mass_mailing"
string="Mass mailing"
context="{'group_by': 'mass_mailing_id'}"/>
context="{'group_by': 'mass_mailing_id'}"
/>
</group>
</search>
</field>
@ -94,9 +110,9 @@
<field name="model">mail.unsubscription</field>
<field name="arch" type="xml">
<pivot string="(Un)subscriptions">
<field name="reason_id" type="row"/>
<field name="mailing_list_ids" type="row"/>
<field name="action" type="col"/>
<field name="reason_id" type="row" />
<field name="mailing_list_ids" type="row" />
<field name="action" type="col" />
</pivot>
</field>
</record>
@ -106,18 +122,23 @@
<field name="model">mail.unsubscription</field>
<field name="arch" type="xml">
<graph string="(Un)subscriptions">
<field name="date" type="row"/>
<field name="action" type="col"/>
<field name="date" type="row" />
<field name="action" type="col" />
</graph>
</field>
</record>
<act_window id="mail_unsubscription_action"
name="(Un)subscriptions"
view_mode="tree,form,pivot,graph"
res_model="mail.unsubscription"/>
<act_window
id="mail_unsubscription_action"
name="(Un)subscriptions"
view_mode="tree,form,pivot,graph"
res_model="mail.unsubscription"
/>
<menuitem id="mail_unsubscription_menu" parent="mass_mailing.mass_mailing_menu_root"
action="mail_unsubscription_action"/>
<menuitem
id="mail_unsubscription_menu"
parent="mass_mailing.mass_mailing_menu_root"
action="mail_unsubscription_action"
/>
</odoo>

View File

@ -1,14 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8" ?>
<!-- Copyright 2020 Tecnativa - João Marques
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -->
<odoo>
<template id="assets_frontend_demo"
inherit_id="web.assets_frontend">
<template id="assets_frontend_demo" inherit_id="web.assets_frontend">
<xpath expr=".">
<script type="text/javascript"
src="/mass_mailing_custom_unsubscribe_event/static/src/js/tour.js"/>
<script
type="text/javascript"
src="/mass_mailing_custom_unsubscribe_event/static/src/js/tour.js"
/>
</xpath>
</template>

View File

@ -1,47 +1,45 @@
/* Copyright 2020 Tecnativa - João Marques
* License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). */
odoo.define("mass_mailing_custom_unsubscribe_event.tour",
function (require) {
"use strict";
var base = require("web_editor.base");
var tour = require("web_tour.tour");
odoo.define("mass_mailing_custom_unsubscribe_event.tour", function(require) {
"use strict";
var base = require("web_editor.base");
var tour = require("web_tour.tour");
// Allow to know if an element is required
$.extend($.expr[':'], {
propRequired: function (element) {
return $(element).prop("required");
},
});
// Allow to know if an element is required
$.extend($.expr[":"], {
propRequired: function(element) {
return $(element).prop("required");
},
});
tour.register(
"mass_mailing_custom_unsubscribe_event_tour",
tour.register(
"mass_mailing_custom_unsubscribe_event_tour",
{
test: true,
wait_for: base.ready(),
},
[
{
test: true,
wait_for: base.ready(),
content: "Choose other reason",
trigger: ".radio:contains('Other reason') :radio:not(:checked)",
extra_trigger: "#reason_form #custom_div_feedback",
},
[
{
content: "Choose other reason",
trigger: ".radio:contains('Other reason') :radio:not(:checked)",
extra_trigger: "#reason_form #custom_div_feedback",
},
{
content: "Switch to not interested reason",
trigger:
".radio:contains(\"I'm not interested\") :radio:not(:checked)",
extra_trigger: "[name='details']:propRequired",
},
{
content: "Unsubscribe",
trigger: "#reason_form button:submit",
extra_trigger: "body:not(:has([name='details']:propRequired))",
},
{
content: "Successfully unsubscribed",
trigger: "body:not(:has(#reason_form)) #subscription_info " +
":contains('successfully unsubscribed')",
},
]
);
}
);
{
content: "Switch to not interested reason",
trigger: '.radio:contains("I\'m not interested") :radio:not(:checked)',
extra_trigger: "[name='details']:propRequired",
},
{
content: "Unsubscribe",
trigger: "#reason_form button:submit",
extra_trigger: "body:not(:has([name='details']:propRequired))",
},
{
content: "Successfully unsubscribed",
trigger:
"body:not(:has(#reason_form)) #subscription_info " +
":contains('successfully unsubscribed')",
},
]
);
});

View File

@ -1,13 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8" ?>
<!-- Copyright 2020 Tecnativa - Pedro M. Baeza
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -->
<odoo>
<record id="view_event_registration_form" model="ir.ui.view">
<field name="name">event.registration.form - Add opt_out</field>
<field name="model">event.registration</field>
<field name="inherit_id" ref="event.view_event_registration_form"/>
<field name="inherit_id" ref="event.view_event_registration_form" />
<field name="arch" type="xml">
<field name="email" position="after">
<field name="opt_out" />

View File

@ -1,7 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8" ?>
<!-- Copyright 2016 Antonio Espinosa <antonio.espinosa@tecnativa.com>
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -->
<odoo noupdate="1">
<record id="event_draft" model="event.registration.state">

Some files were not shown because too many files have changed in this diff Show More