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 insert_final_newline = true
trim_trailing_whitespace = true trim_trailing_whitespace = true
[.eslintrc,*.{json,yml,yaml,rst,md}] [*.{json,yml,yaml,rst,md}]
indent_size = 2 indent_size = 2
# Do not configure editor for libs and autogenerated content # 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 charset = unset
end_of_line = unset end_of_line = unset
indent_size = 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 # Byte-compiled / optimized / DLL files
__pycache__/ __pycache__/
*.py[cod] *.py[cod]
/.venv
/.pytest_cache
# C extensions # C extensions
*.so *.so
@ -8,12 +10,11 @@ __pycache__/
# Distribution / packaging # Distribution / packaging
.Python .Python
env/ env/
bin/
build/ build/
develop-eggs/ develop-eggs/
dist/ dist/
downloads/
eggs/ eggs/
.eggs/
lib/ lib/
lib64/ lib64/
parts/ parts/
@ -22,12 +23,7 @@ var/
*.egg-info/ *.egg-info/
.installed.cfg .installed.cfg
*.egg *.egg
*.eggs
# 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
# Installer logs # Installer logs
pip-log.txt pip-log.txt
@ -37,11 +33,9 @@ pip-delete-this-directory.txt
htmlcov/ htmlcov/
.tox/ .tox/
.coverage .coverage
.coverage.*
.cache .cache
nosetests.xml nosetests.xml
coverage.xml coverage.xml
*,cover
# Translations # Translations
*.mo *.mo
@ -49,11 +43,33 @@ coverage.xml
# Pycharm # Pycharm
.idea .idea
# Eclipse
.settings
# Visual Studio cache/options directory
.vs/
.vscode
# OSX Files
.DS_Store
# Django stuff: # Django stuff:
*.log *.log
# Mr Developer
.mr.developer.cfg
.project
.pydevproject
# Rope
.ropeproject
# Sphinx documentation # Sphinx documentation
docs/_build/ docs/_build/
# PyBuilder # Backup files
target/ *~
*.swp
# OCA rules
!static/lib/

View File

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

View File

@ -1,14 +1,68 @@
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: default_language_version:
python: python3 python: python3
node: "14.13.0" node: "14.13.0"
repos: repos:
- repo: https://github.com/psf/black - repo: local
rev: 19.3b0 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: hooks:
- id: black - id: black
- repo: https://github.com/pre-commit/pre-commit-hooks - repo: https://github.com/pre-commit/mirrors-prettier
rev: v2.3.0 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: hooks:
- id: trailing-whitespace - id: trailing-whitespace
# exclude autogenerated files # exclude autogenerated files
@ -17,6 +71,36 @@ repos:
# exclude autogenerated files # exclude autogenerated files
exclude: /README\.rst$|\.pot?$ exclude: /README\.rst$|\.pot?$
- id: debug-statements - 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 - id: flake8
name: flake8 except __init__.py name: flake8 except __init__.py
exclude: /__init__\.py$ exclude: /__init__\.py$
@ -26,44 +110,19 @@ repos:
args: ["--extend-ignore=F401"] # ignore unused imports in __init__.py args: ["--extend-ignore=F401"] # ignore unused imports in __init__.py
files: /__init__\.py$ files: /__init__\.py$
additional_dependencies: ["flake8-bugbear==19.8.0"] additional_dependencies: ["flake8-bugbear==19.8.0"]
- id: fix-encoding-pragma - repo: https://github.com/pre-commit/mirrors-pylint
args: ["--remove"] rev: v2.5.3
- 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: hooks:
- id: pylint - id: pylint
name: pylint with optional checks name: pylint with optional checks
args: ["--rcfile=.pylintrc", "--exit-zero"] args:
- --rcfile=.pylintrc
- --exit-zero
verbose: true verbose: true
additional_dependencies: ["pylint-odoo==3.5.0"] additional_dependencies: &pylint_deps
- pylint-odoo==3.5.0
- id: pylint - id: pylint
name: pylint with mandatory checks name: pylint with mandatory checks
args: ["--rcfile=.pylintrc-mandatory"] args:
additional_dependencies: ["pylint-odoo==3.5.0"] - --rcfile=.pylintrc-mandatory
- repo: https://github.com/asottile/pyupgrade additional_dependencies: *pylint_deps
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

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

View File

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

View File

@ -14,30 +14,23 @@ addons:
- expect-dev # provides unbuffer utility - expect-dev # provides unbuffer utility
stages: stages:
- linting
- test - test
jobs: jobs:
include: 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 - stage: test
env: env:
- TESTS="1" ODOO_REPO="odoo/odoo" MAKEPOT="1" - TESTS=1 ODOO_REPO="odoo/odoo" MAKEPOT="1"
- stage: test - stage: test
env: env:
- TESTS="1" ODOO_REPO="OCA/OCB" - TESTS=1 ODOO_REPO="OCA/OCB"
env: env:
global: global:
- VERSION="13.0" TESTS="0" LINT_CHECK="0" MAKEPOT="0" - VERSION="13.0" TESTS="0" LINT_CHECK="0" MAKEPOT="0"
install: 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} - export PATH=${HOME}/maintainer-quality-tools/travis:${PATH}
- travis_install_nightly - 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 GNU AFFERO GENERAL PUBLIC LICENSE
Version 3, 19 November 2007 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 Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed. 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> Copyright (C) <year> <name of author>
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU Affero General Public License as published by
by the Free Software Foundation, either version 3 of the License, or the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. (at your option) any later version.
This program is distributed in the hope that it will be useful, 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. GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License 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. 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, 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. 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 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) [![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) [![Build Status](https://travis-ci.com/OCA/social.svg?branch=13.0)](https://travis-ci.com/OCA/social)
[![Coverage Status](https://coveralls.io/repos/OCA/social/badge.svg?branch=13.0)](https://coveralls.io/r/OCA/social?branch=13.0) [![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) [//]: # (addons)
@ -44,7 +50,18 @@ addon | version | summary
[//]: # (end addons) [//]: # (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"> <odoo noupdate="1">
<record id="subject_gin_idx" model="trgm.index"> <record id="subject_gin_idx" model="trgm.index">
<field name="index_type">gin</field> <field name="index_type">gin</field>
<field name="field_id" <field
search="[('model','=','mail.message'),('name','=','subject')]"/> name="field_id"
search="[('model','=','mail.message'),('name','=','subject')]"
/>
</record> </record>
<record id="body_gin_idx" model="trgm.index"> <record id="body_gin_idx" model="trgm.index">
<field name="index_type">gin</field> <field name="index_type">gin</field>
<field name="field_id" <field
search="[('model','=','mail.message'),('name','=','body')]"/> name="field_id"
search="[('model','=','mail.message'),('name','=','body')]"
/>
</record> </record>
<record id="record_name_gin_idx" model="trgm.index"> <record id="record_name_gin_idx" model="trgm.index">
<field name="index_type">gin</field> <field name="index_type">gin</field>
<field name="field_id" <field
search="[('model','=','mail.message'),('name','=','record_name')]"/> name="field_id"
search="[('model','=','mail.message'),('name','=','record_name')]"
/>
</record> </record>
<record id="email_from_gin_idx" model="trgm.index"> <record id="email_from_gin_idx" model="trgm.index">
<field name="index_type">gin</field> <field name="index_type">gin</field>
<field name="field_id" <field
search="[('model','=','mail.message'),('name','=','email_from')]"/> name="field_id"
search="[('model','=','mail.message'),('name','=','email_from')]"
/>
</record> </record>
<record id="reply_to_gin_idx" model="trgm.index"> <record id="reply_to_gin_idx" model="trgm.index">
<field name="index_type">gin</field> <field name="index_type">gin</field>
<field name="field_id" <field
search="[('model','=','mail.message'),('name','=','reply_to')]"/> name="field_id"
search="[('model','=','mail.message'),('name','=','reply_to')]"
/>
</record> </record>
</odoo> </odoo>

View File

@ -1,9 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8" ?>
<odoo> <odoo>
<template id="view_email_template_corporate_identity"> <template id="view_email_template_corporate_identity">
<body> <body>
<html> <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, <!-- if some template calling us sets this variable,
we print a h1 tag /--> we print a h1 tag /-->
<h1 t-if="email_heading"><t t-esc="email_heading" /></h1> <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> <odoo>
<record id="email_template_demo1" model="mail.template"> <record id="email_template_demo1" model="mail.template">
<field name="name">QWeb demo</field> <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> <odoo>
<record id="email_template_form" model="ir.ui.view"> <record id="email_template_form" model="ir.ui.view">
<field name="model">mail.template</field> <field name="model">mail.template</field>
@ -9,12 +9,21 @@
</field> </field>
<field name="body_html" position="before"> <field name="body_html" position="before">
<group attrs="{'invisible': [('body_type', '!=', 'qweb')]}"> <group attrs="{'invisible': [('body_type', '!=', 'qweb')]}">
<field name="body_view_id" attrs="{'required': [('body_type', '=', 'qweb')]}" /> <field
<field name="body_view_arch" widget="ace" attrs="{'required': [('body_type', '=', 'qweb')], 'invisible': [('body_view_id', '=', False)]}" /> 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> </group>
</field> </field>
<field name="body_html" position="attributes"> <field name="body_html" position="attributes">
<attribute name="attrs">{'invisible': [('body_type', '!=', 'jinja2')]}</attribute> <attribute
name="attrs"
>{'invisible': [('body_type', '!=', 'jinja2')]}</attribute>
</field> </field>
</field> </field>
</record> </record>

View File

@ -5,7 +5,7 @@
"summary": "Post unkonwn messages to an existing thread", "summary": "Post unkonwn messages to an existing thread",
"version": "13.0.1.0.0", "version": "13.0.1.0.0",
"category": "Discuss", "category": "Discuss",
"website": "https://www.github.com/social", "website": "https://github.com/OCA/social",
"author": "Tecnativa, Odoo Community Association (OCA)", "author": "Tecnativa, Odoo Community Association (OCA)",
"license": "AGPL-3", "license": "AGPL-3",
"application": False, "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> <!-- Copyright 2017 Tecnativa - Jairo Llopis <jairo.llopis@tecnativa.com>
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). --> License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -->
<odoo> <odoo>
<record id="demo_sink" model="mail.channel"> <record id="demo_sink" model="mail.channel">
<field name="name">mailsink</field> <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="description">Unbounded email sink</field>
<field name="alias_contact">everyone</field> <field name="alias_contact">everyone</field>
<field name="public">private</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>
<record id="demo_server" model="fetchmail.server"> <record id="demo_server" model="fetchmail.server">
<field name="name">Demo server</field> <field name="name">Demo server</field>
<field name="server_type">pop</field> <field name="server_type">pop</field>
<field name="server">pop3.example.com</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> --> <!-- <field name="default_thread_id">mail.channel,%(demo_sink)d</field> -->
</record> </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> <!-- Copyright 2017 Tecnativa - Jairo Llopis <jairo.llopis@tecnativa.com>
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). --> License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -->
<odoo> <odoo>
<record id="view_email_server_form" model="ir.ui.view"> <record id="view_email_server_form" model="ir.ui.view">
<field name="name">Add default thread</field> <field name="name">Add default thread</field>
<field name="model">fetchmail.server</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"> <field name="arch" type="xml">
<xpath expr="//field[@name='object_id']" position="after"> <xpath expr="//field[@name='object_id']" position="after">
<field name="default_thread_id"/> <field name="default_thread_id" />
</xpath> </xpath>
</field> </field>
</record> </record>

View File

@ -1,14 +1,13 @@
/* Copyright 2018 David Juaneda /* Copyright 2018 David Juaneda
* License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). */ * 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"; "use strict";
var chatter = require('mail.Chatter'); var chatter = require("mail.Chatter");
chatter.include({ chatter.include({
events: _.extend({}, chatter.prototype.events, { 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 * @private
*/ */
_onListActivity: function () { _onListActivity: function() {
this._rpc({ this._rpc({
model: this.record.model, model: this.record.model,
method: 'redirect_to_activities', method: "redirect_to_activities",
args: [[]], args: [[]],
kwargs: { kwargs: {
'id':this.record.res_id, id: this.record.res_id,
'model':this.record.model, model: this.record.model,
}, },
context: this.record.getContext(), context: this.record.getContext(),
}).then($.proxy(this, "do_action")); }).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"> <templates id="template" xml:space="preserve">
<t t-extend="mail.chatter.Buttons"> <t t-extend="mail.chatter.Buttons">
<t t-jquery="button.o_chatter_button_schedule_activity" t-operation="after"> <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" <button
title="See activities list" type="button" disabled="disabled"> t-if="scheduleActivityButton"
<i class="fa fa-list"/> Activities class="btn btn-link o_chatter_button_list_activity"
title="See activities list"
type="button"
disabled="disabled"
>
<i class="fa fa-list" /> Activities
</button> </button>
</t> </t>
</t> </t>

View File

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

View File

@ -1,8 +1,15 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8" ?>
<odoo> <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"> <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> </xpath>
</template> </template>
</odoo> </odoo>

View File

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

View File

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

View File

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

View File

@ -1,12 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8" ?>
<odoo> <odoo>
<!-- <!--
Copyright 2018-20 ForgeFlow <http://www.forgeflow.com> Copyright 2018-20 ForgeFlow <http://www.forgeflow.com>
License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). 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"> <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> </xpath>
</template> </template>
</odoo> </odoo>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,15 +1,21 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8" ?>
<odoo> <odoo>
<record model="ir.ui.view" id="email_compose_message_wizard_inherit_form"> <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="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"> <field name="arch" type="xml">
<xpath expr="//field[@name='attachment_ids']" position="after"> <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)]}"> <div attrs="{'invisible': [('can_attach_attachment', '=', False)]}">
<br /> <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> </div>
</xpath> </xpath>
</field> </field>

View File

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

View File

@ -6,6 +6,7 @@
"author": "Therp BV,Odoo Community Association (OCA)", "author": "Therp BV,Odoo Community Association (OCA)",
"license": "AGPL-3", "license": "AGPL-3",
"category": "Discuss", "category": "Discuss",
"website": "https://github.com/OCA/social",
"summary": "Attach emails to Odoo by dragging them from your desktop", "summary": "Attach emails to Odoo by dragging them from your desktop",
"depends": ["mail", "web_drop_target"], "depends": ["mail", "web_drop_target"],
"external_dependencies": {"python": ["extract_msg"]}, "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> // Copyright 2018 Therp BV <https://therp.nl>
// License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). // 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"; "use strict";
var Chatter = require('mail.Chatter'); var Chatter = require("mail.Chatter");
var web_drop_target = require('web_drop_target'); var web_drop_target = require("web_drop_target");
Chatter.include(web_drop_target.DropTargetMixin); Chatter.include(web_drop_target.DropTargetMixin);
Chatter.include({ Chatter.include({
_drop_allowed_types: ['message/rfc822'], _drop_allowed_types: ["message/rfc822"],
_get_record_id: function() { _get_record_id: function() {
return this.record.res_id; return this.record.res_id;
}, },
_handle_drop_items: function (drop_items, e) { _handle_drop_items: function(drop_items) {
var self = this; var self = this;
_.each(drop_items, function(item, e) { _.each(drop_items, function(item, e) {
return self._handle_file_drop_proxy(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 self = this;
var file = item; var file = item;
if (!file || !(file instanceof Blob)) { if (!file || !(file instanceof Blob)) {
@ -31,20 +31,18 @@ odoo.define('mail_drop_target', function (require) {
reader.onloadend = self.proxy( reader.onloadend = self.proxy(
_.partial(self._handle_file_drop, file, reader, e) _.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); reader.readAsArrayBuffer(file);
}, },
_handle_file_drop: function (drop_file, reader, e) { _handle_file_drop: function(drop_file, reader) {
var self = this, var self = this,
mail_processor = '', mail_processor = "",
data = ''; data = "";
if (drop_file.name.endsWith('.msg')) { if (drop_file.name.endsWith(".msg")) {
mail_processor = 'message_process_msg'; mail_processor = "message_process_msg";
data = base64js.fromByteArray( data = base64js.fromByteArray(new Uint8Array(reader.result));
new Uint8Array(reader.result)
);
} else { } else {
mail_processor = 'message_drop'; mail_processor = "message_drop";
var reader_array = new Uint8Array(reader.result); var reader_array = new Uint8Array(reader.result);
data = ""; data = "";
for (var i = 0; i < reader_array.length; i++) { 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 // TODO: read some config parameter if this should set
// some of the context keys to suppress mail.thread's behavior // some of the context keys to suppress mail.thread's behavior
return this._rpc({ return this._rpc({
model:'mail.thread', model: "mail.thread",
method:mail_processor, method: mail_processor,
args: [this.record.model, data], args: [this.record.model, data],
kwargs: { kwargs: {
thread_id: this.record.data.id, thread_id: this.record.data.id,
}, },
}).then(function() { }).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> <odoo>
<data> <data>
<record id="res_config_settings_view_form" model="ir.ui.view"> <record id="res_config_settings_view_form" model="ir.ui.view">
<field name="name">res.config.settings.view.form.inherit.mail</field> <field name="name">res.config.settings.view.form.inherit.mail</field>
<field name="model">res.config.settings</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"> <field name="arch" type="xml">
<div id="emails" position="inside"> <div id="emails" position="inside">
<div class="col-xs-12 col-md-6 o_setting_box"> <div class="col-xs-12 col-md-6 o_setting_box">
<div class="o_setting_left_pane"> <div class="o_setting_left_pane">
<field name="disable_notify_mail_drop_target"/> <field name="disable_notify_mail_drop_target" />
</div> </div>
<div class="o_setting_right_pane"> <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"> <div class="text-muted">
When a user drops an email into an existing thread When a user drops an email into an existing thread
the followers of the thread will not be notified. 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> <openerp>
<data> <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"> <xpath expr="." position="inside">
<script type="text/javascript" src="/mail_drop_target/static/src/js/mail_drop_target.js"></script> <script
<link rel="stylesheet" href="/mail_drop_target/static/src/css/mail_drop_target.css"/> 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> </xpath>
</template> </template>
</data> </data>

View File

@ -1,32 +1,33 @@
/* Copyright 2014-2015 Grupo ESOC <http://www.grupoesoc.es> /* Copyright 2014-2015 Grupo ESOC <http://www.grupoesoc.es>
* License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). * 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"; "use strict";
var ThreadWidget = require('mail.widget.Thread'); var ThreadWidget = require("mail.widget.Thread");
ThreadWidget.include({ ThreadWidget.include({
events: _.defaults({ events: _.defaults(
{
"click .o_full_expand": "_onClickMessageFullExpand", "click .o_full_expand": "_onClickMessageFullExpand",
}, ThreadWidget.prototype.events), },
ThreadWidget.prototype.events
),
_onClickMessageFullExpand: function (event) { _onClickMessageFullExpand: function(event) {
// Get the action data and execute it to open the full view // Get the action data and execute it to open the full view
var do_action = this.do_action, var do_action = this.do_action,
msg_id = $(event.currentTarget).data('message-id'); msg_id = $(event.currentTarget).data("message-id");
this._rpc({ this._rpc({
route: "/web/action/load", route: "/web/action/load",
params: { params: {
action_id: "mail_full_expand.mail_message_action", action_id: "mail_full_expand.mail_message_action",
}, },
}) }).then(function(action) {
.then(function (action) {
action.res_id = msg_id; action.res_id = msg_id;
do_action(action); 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). --> <!-- Copyright 2014-2015 Grupo ESOC <http://www.grupoesoc.es>License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -->
<template> <template>
<t t-extend="mail.widget.Thread.Message"> <t t-extend="mail.widget.Thread.Message">
<t t-jquery=".o_mail_info" t-operation="append"> <t t-jquery=".o_mail_info" t-operation="append">
<span class="o_thread_full_spand_icon"> <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> </span>
</t> </t>
</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). --> <!-- Copyright 2014-2015 Grupo ESOC <http://www.grupoesoc.es>License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -->
<odoo> <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"> <xpath expr="." position="inside">
<link href="/mail_full_expand/static/src/scss/mail_full_expand.scss" rel="stylesheet" type="text/scss"/> <link
<script src="/mail_full_expand/static/src/js/mail_full_expand.js" type="text/javascript"/> 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> </xpath>
</template> </template>
</odoo> </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). --> <!-- Copyright 2014-2015 Grupo ESOC <http://www.grupoesoc.es>License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -->
<odoo> <odoo>
<record id="mail_message_view_form" model="ir.ui.view"> <record id="mail_message_view_form" model="ir.ui.view">
@ -8,18 +8,18 @@
<field name="arch" type="xml"> <field name="arch" type="xml">
<form string="Message"> <form string="Message">
<group> <group>
<field name="author_id" readonly="1"/> <field name="author_id" readonly="1" />
<field name="email_from" readonly="1"/> <field name="email_from" readonly="1" />
<field name="date" readonly="1"/> <field name="date" readonly="1" />
<field name="partner_ids" readonly="1" widget="many2many_tags"/> <field name="partner_ids" readonly="1" widget="many2many_tags" />
</group> </group>
<h1> <h1>
<field name="subject" readonly="1"/> <field name="subject" readonly="1" />
</h1> </h1>
<field name="body" readonly="1"/> <field name="body" readonly="1" />
<field name="attachment_ids" readonly="1" widget="many2many_binary"/> <field name="attachment_ids" readonly="1" widget="many2many_binary" />
<footer> <footer>
<button class="btn-secondary" special="cancel" string="Close"/> <button class="btn-secondary" special="cancel" string="Close" />
</footer> </footer>
</form> </form>
</field> </field>
@ -28,7 +28,7 @@
<field name="name">Read Full Email</field> <field name="name">Read Full Email</field>
<field name="res_model">mail.message</field> <field name="res_model">mail.message</field>
<field name="type">ir.actions.act_window</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="view_mode">form</field>
<field name="target">new</field> <field name="target">new</field>
</record> </record>

View File

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8" ?>
<odoo noupdate="0"> <odoo noupdate="0">
<record id="email_template_demo" model="mail.template"> <record id="email_template_demo" model="mail.template">
<field name="name">Inline styles demo</field> <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"> <odoo noupdate="0">
<template id="demo_email_hello" name="demo email hello"> <template id="demo_email_hello" name="demo email hello">
<html> <html>
@ -48,7 +48,10 @@
<tbody> <tbody>
<tr> <tr>
<td class="left"> <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>
<td class="right"> <td class="right">
<span class="date_today" t-esc="time.strftime('%%d %%B %%Y')" /> <span class="date_today" t-esc="time.strftime('%%d %%B %%Y')" />
@ -62,18 +65,23 @@
<p>Hello <span t-field="object.name" />.</p> <p>Hello <span t-field="object.name" />.</p>
</div> </div>
<div id="content"> <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> </div>
</main> </main>
<footer id="main_footer"> <footer id="main_footer">
<div class="company_info"> <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 class="address">
<div t-field="env.user.company_id.partner_id" <div
t-field="env.user.company_id.partner_id"
t-options='{ t-options='{
"widget": "contact", "widget": "contact",
"fields": ["name", "address", "phone", "mobile", "email"] "fields": ["name", "address", "phone", "mobile", "email"]
}'/> }'
/>
</div> </div>
</div> </div>
</footer> </footer>

View File

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8" ?>
<odoo> <odoo>
<template id="email_templates_list"> <template id="email_templates_list">
@ -17,7 +17,8 @@
<li>Copy the link</li> <li>Copy the link</li>
<li> <li>
Paste into the address bar and change the record ID (the last value in the URL). 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>
<li>Load it</li> <li>Load it</li>
</ol> </ol>
@ -31,10 +32,16 @@
<strong>XMLID:</strong> <span class="xid" t-esc="xid" /><br /> <strong>XMLID:</strong> <span class="xid" t-esc="xid" /><br />
</t> </t>
<strong>Link:</strong> <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 Preview
</a><br /> </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 View in backend
</a> </a>
</li> </li>

View File

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

View File

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

View File

@ -1,12 +1,15 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8" ?>
<odoo> <odoo>
<record model="ir.ui.view" id="email_compose_message_wizard_inherit_form"> <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="name">mail.compose.message.form (mail_optional_autofollow)</field>
<field name="model">mail.compose.message</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"> <field name="arch" type="xml">
<xpath expr="//div[field[@name='partner_ids']]" position="after"> <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> </xpath>
</field> </field>
</record> </record>

View File

@ -1,15 +1,22 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8" ?>
<odoo> <odoo>
<record id="email_compose_message_wizard_form" model="ir.ui.view"> <record id="email_compose_message_wizard_form" model="ir.ui.view">
<field name="model">mail.compose.message</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"> <field name="arch" type="xml">
<span name="document_followers_text" position="before"> <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>
<span name="document_followers_text" position="after"> <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> </span>
</field> </field>
</record> </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. Copyright 2017 LasLabs Inc.
License LGPL-3 or later (http://www.gnu.org/licenses/lgpl.html). License LGPL-3 or later (http://www.gnu.org/licenses/lgpl.html).
--> -->
<odoo> <odoo>
<record id="ir_mail_server_form" model="ir.ui.view"> <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="inherit_id" ref="base.ir_mail_server_form" />
<field name="arch" type="xml"> <field name="arch" type="xml">
<xpath expr="//field[@name='smtp_pass']" position="after"> <xpath expr="//field[@name='smtp_pass']" position="after">
<field name="domain_whitelist"/> <field name="domain_whitelist" />
<field name="smtp_from" widget="email"/> <field name="smtp_from" widget="email" />
</xpath> </xpath>
</field> </field>
</record> </record>

View File

@ -3,27 +3,24 @@
License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). 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"; "use strict";
var preview = require("mail_preview_base.preview"); var preview = require("mail_preview_base.preview");
var DocumentViewer = preview.DocumentViewer; var DocumentViewer = preview.DocumentViewer;
DocumentViewer.include({ DocumentViewer.include({
_checkAttachment: function (attachment) { _checkAttachment: function(attachment) {
if ( if (attachment.type !== "url" && attachment.mimetype.match("audio")) {
attachment.type !== 'url' && attachment.mimetype.match("audio") attachment.type = "audio";
) {
attachment.type = 'audio';
attachment.source_url = this._getImageUrl(attachment); attachment.source_url = this._getImageUrl(attachment);
return true; return true;
} }
return this._super.apply(this, arguments); return this._super.apply(this, arguments);
}, },
_hasPreview: function (type) { _hasPreview: function(type) {
var result = this._super.apply(this, arguments); 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"> <templates xml:space="preserve">
<t t-extend="DocumentViewer.Content"> <t t-extend="DocumentViewer.Content">
<t t-jquery=".fa-video-camera" t-operation="after"> <t t-jquery=".fa-video-camera" t-operation="after">
<i class="fa fa-volume-up mr8" <i
class="fa fa-volume-up mr8"
t-if="widget.activeAttachment.type == 'audio'" t-if="widget.activeAttachment.type == 'audio'"
role="img" aria-label="Audio" title="Audio"/> role="img"
aria-label="Audio"
title="Audio"
/>
</t> </t>
<t t-jquery=".o_viewer_video" t-operation="after"> <t t-jquery=".o_viewer_video" t-operation="after">
<audio t-if="widget.activeAttachment.type == 'audio'" class="o_viewer_audio" controls="controls"> <audio
<source t-attf-src="#{widget.activeAttachment.source_url}" t-att-data-type="widget.activeAttachment.mimetype"/> 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> </audio>
</t> </t>
</t> </t>

View File

@ -8,7 +8,10 @@
<odoo> <odoo>
<template id="assets_backend" name="audio_assets" inherit_id="web.assets_backend"> <template id="assets_backend" name="audio_assets" inherit_id="web.assets_backend">
<xpath expr="//script[last()]" position="after"> <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> </xpath>
</template> </template>
</odoo> </odoo>

View File

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

View File

@ -6,12 +6,22 @@
--> -->
<odoo> <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"> <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>
<xpath expr="//link[last()]" position="after"> <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> </xpath>
</template> </template>
</odoo> </odoo>

View File

@ -8,6 +8,7 @@
"author": "Therp BV,Creu Blanca,Odoo Community Association (OCA)", "author": "Therp BV,Creu Blanca,Odoo Community Association (OCA)",
"license": "AGPL-3", "license": "AGPL-3",
"category": "Social Network", "category": "Social Network",
"website": "https://github.com/OCA/social",
"summary": "Define a domain from which followers can be selected", "summary": "Define a domain from which followers can be selected",
"depends": ["mail"], "depends": ["mail"],
"data": ["data/ir_config_parameter.xml", "data/ir_actions.xml"], "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> <odoo>
<act_window id="action_setup" res_model="ir.config_parameter" <act_window
name="Configure the restriction on followers" view_mode="form" /> 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"> <record id="action_setup" model="ir.actions.act_window">
<field name="res_id" ref="parameter_domain" /> <field name="res_id" ref="parameter_domain" />
</record> </record>

View File

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

View File

@ -9,7 +9,7 @@
"summary": "Email tracking system for all mails sent", "summary": "Email tracking system for all mails sent",
"version": "13.0.1.0.6", "version": "13.0.1.0.6",
"category": "Social Network", "category": "Social Network",
"website": "http://github.com/OCA/social", "website": "https://github.com/OCA/social",
"author": ("Tecnativa, " "Odoo Community Association (OCA)"), "author": ("Tecnativa, " "Odoo Community Association (OCA)"),
"license": "AGPL-3", "license": "AGPL-3",
"application": False, "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> <!-- Copyright 2016 Antonio Espinosa - <antonio.espinosa@tecnativa.com>
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). --> License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -->
<odoo> <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="name">MailTracking Timestamp</field>
<field name="digits">6</field> <field name="digits">6</field>
</record> </record>

View File

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8" ?>
<odoo> <odoo>
<data noupdate="0"> <data noupdate="0">
@ -8,7 +8,9 @@
<field name="res_id" ref="base.partner_demo" /> <field name="res_id" ref="base.partner_demo" />
<field name="message_type">comment</field> <field name="message_type">comment</field>
<field name="subtype_id" ref="mail.mt_comment" /> <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="mail_tracking_needs_action">1</field>
<field name="body"><![CDATA[<p>This is a message with CC</p>]]></field> <field name="body"><![CDATA[<p>This is a message with CC</p>]]></field>
<field name="email_from">wood.corner26@example.com</field> <field name="email_from">wood.corner26@example.com</field>
@ -24,7 +26,7 @@
<field name="recipient">demo@yourcompany.example.com</field> <field name="recipient">demo@yourcompany.example.com</field>
<field name="sender">wood.corner26@example.com</field> <field name="sender">wood.corner26@example.com</field>
<field name="state">sent</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> </record>
<!-- Failed Message A --> <!-- Failed Message A -->
@ -48,7 +50,7 @@
<field name="recipient">demo@yourcompany.example.com</field> <field name="recipient">demo@yourcompany.example.com</field>
<field name="sender">wood.corner26@example.com</field> <field name="sender">wood.corner26@example.com</field>
<field name="state">error</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> </record>
<!-- Failed Message B --> <!-- Failed Message B -->
@ -72,7 +74,7 @@
<field name="recipient">demo@yourcompany.example.com</field> <field name="recipient">demo@yourcompany.example.com</field>
<field name="sender">jackson.group82@example.com</field> <field name="sender">jackson.group82@example.com</field>
<field name="state">error</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> </record>
<!-- Failed Message C --> <!-- Failed Message C -->
@ -96,7 +98,7 @@
<field name="recipient">demo@yourcompany.example.com</field> <field name="recipient">demo@yourcompany.example.com</field>
<field name="sender">admin@example.com</field> <field name="sender">admin@example.com</field>
<field name="state">error</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> </record>
</data> </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> <!-- Copyright 2016 Antonio Espinosa - <antonio.espinosa@tecnativa.com>
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). --> License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -->
<odoo> <odoo>
<record model="ir.rule" id="mail_tracking_email_portal_public_rule"> <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
<field name="model_id" ref="model_mail_tracking_email"/> 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="domain_force">[('partner_id', '=', user.partner_id.id)]</field>
<field name="groups" eval="[(4, ref('base.group_portal')), (4, ref('base.group_public'))]"/> <field
<field name="perm_create" eval="False"/> name="groups"
<field name="perm_unlink" eval="False"/> eval="[(4, ref('base.group_portal')), (4, ref('base.group_public'))]"
<field name="perm_write" eval="False"/> />
<field name="perm_create" eval="False" />
<field name="perm_unlink" eval="False" />
<field name="perm_write" eval="False" />
</record> </record>
</odoo> </odoo>

View File

@ -31,7 +31,7 @@
} }
i.fa-caret-down:before { i.fa-caret-down:before {
content: '\f0da'; content: "\f0da";
} }
} }
} }
@ -52,7 +52,7 @@
} }
.o_thread_typing_notification_free_space { .o_thread_typing_notification_free_space {
flex-grow: 1, flex-grow: 1;
} }
.o_thread_typing_notification_bar { .o_thread_typing_notification_bar {
@ -60,7 +60,7 @@
background-color: rgba($white, 0.75); background-color: rgba($white, 0.75);
padding: 5px; padding: 5px;
text-align: center; text-align: center;
color: gray('600'); color: gray("600");
&.o_thread_order_asc { &.o_thread_order_asc {
@include o-position-sticky($bottom: 0px); @include o-position-sticky($bottom: 0px);
@ -83,7 +83,7 @@
margin-top: 0px; margin-top: 0px;
margin-bottom: 15px; margin-bottom: 15px;
} }
border-bottom: 1px solid gray('400'); border-bottom: 1px solid gray("400");
text-align: center; text-align: center;
.o_thread_date { .o_thread_date {
@ -116,8 +116,8 @@
margin-bottom: 0px; margin-bottom: 0px;
&.o_mail_not_discussion { &.o_mail_not_discussion {
background-color: rgba(gray('300'), 0.5); background-color: rgba(gray("300"), 0.5);
border-bottom: 1px solid gray('400'); border-bottom: 1px solid gray("400");
} }
.o_thread_message_sidebar { .o_thread_message_sidebar {
@ -155,7 +155,8 @@
} }
} }
&:hover, &.o_thread_selected_message { &:hover,
&.o_thread_selected_message {
.o_thread_message_side_date { .o_thread_message_side_date {
opacity: $o-mail-thread-side-date-opacity; opacity: $o-mail-thread-side-date-opacity;
} }
@ -182,8 +183,6 @@
text-align: justify; text-align: justify;
} }
.o_mail_subject { .o_mail_subject {
font-style: italic; font-style: italic;
} }
@ -193,7 +192,8 @@
color: gray; 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; 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; padding: 4px;
} }
@ -232,14 +235,16 @@
&.o_thread_message_email_ready { &.o_thread_message_email_ready {
color: grey; color: grey;
} }
&.o_thread_message_email_exception, &.o_thread_message_email_bounce { &.o_thread_message_email_exception,
&.o_thread_message_email_bounce {
color: red; color: red;
opacity: 1; opacity: 1;
cursor: pointer; cursor: pointer;
} }
} }
.o_attachments_list, .o_attachments_previews { .o_attachments_list,
.o_attachments_previews {
&:last-child { &:last-child {
margin-bottom: $grid-gutter-width; margin-bottom: $grid-gutter-width;
} }
@ -299,7 +304,7 @@
.o_activity_info { .o_activity_info {
vertical-align: baseline; vertical-align: baseline;
padding: 4px 6px; padding: 4px 6px;
background: theme-color('light'); background: theme-color("light");
border-radius: 2px 2px 0 0; border-radius: 2px 2px 0 0;
@include o-hover-opacity(1, 1); @include o-hover-opacity(1, 1);

View File

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

View File

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

View File

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

View File

@ -1,25 +1,31 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8" ?>
<templates> <templates>
<t t-name="mail_tracking.SidebarFailedCounter"> <t t-name="mail_tracking.SidebarFailedCounter">
<span t-attf-class="o_mail_sidebar_failed badge badge-pill #{(counter ? '' : 'd-none')}"> <span
<t t-esc="counter"/> t-attf-class="o_mail_sidebar_failed badge badge-pill #{(counter ? '' : 'd-none')}"
>
<t t-esc="counter" />
</span> </span>
</t> </t>
<t t-name="mail_tracking.SidebarFailed"> <t t-name="mail_tracking.SidebarFailed">
<div t-attf-class="o_mail_discuss_title_main o_mail_discuss_item #{(activeThreadID === 'mailbox_failed') ? 'o_active': ''}" <div
data-thread-id="mailbox_failed"> t-attf-class="o_mail_discuss_title_main o_mail_discuss_item #{(activeThreadID === 'mailbox_failed') ? 'o_active': ''}"
<span class="o_thread_name"><i class="fa fa-exclamation mr8"/>Failed</span> data-thread-id="mailbox_failed"
<t t-set="counter" t-value="failedCounter"/> >
<t t-call="mail_tracking.SidebarFailedCounter"/> <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> </div>
</t> </t>
<t t-extend="mail.widget.Thread.Empty"> <t t-extend="mail.widget.Thread.Empty">
<t t-jquery="t:last-child" t-operation="after"> <t t-jquery="t:last-child" t-operation="after">
<t t-if="thread.getID() === 'mailbox_failed'"> <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> <div>Failed messages appear here.</div>
</t> </t>
</t> </t>
@ -27,7 +33,11 @@
<t t-extend="mail.discuss.ControlButtons"> <t t-extend="mail.discuss.ControlButtons">
<t t-jquery="div" t-operation="append"> <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>
</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"> <templates xml:space="preserve">
<t t-name="mail_tracking.failed_message_items"> <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"> <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 Failed messages
<small class="o_chatter_failed_message_summary ml8"> <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> </small>
</a> </a>
</div> </div>
@ -16,40 +22,60 @@
<div class="o_thread_message" style="margin-bottom: 10px"> <div class="o_thread_message" style="margin-bottom: 10px">
<div class="o_thread_message_sidebar"> <div class="o_thread_message_sidebar">
<div class="o_avatar_stack"> <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]"/> <img
<i t-att-class="'o_avatar_icon fa fa-exclamation bg-danger-full'" t-attf-src="/web/image/res.partner/#{message.author[0]}/image_small"
title="Failed"/> 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> </div>
<div class="o_thread_message_core"> <div class="o_thread_message_core">
<div class="o_mail_info text-muted"> <div class="o_mail_info text-muted">
<strong class="o_thread_author"> <strong class="o_thread_author">
<t t-esc="message.author[1]"/> <t t-esc="message.author[1]" />
</strong> </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"> <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"> <a
<i class="fa fa-check"/> Set as Reviewed 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>
<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"> <a
<i class="fa fa-retweet"/> Retry 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> </a>
</span> </span>
<br/> <br />
<strong class="text-danger">Failed Recipients:</strong> <strong class="text-danger">Failed Recipients:</strong>
<t t-foreach="message.failed_recipients" t-as="recipient"> <t t-foreach="message.failed_recipients" t-as="recipient">
<t t-if="!recipient_first"> <t t-if="!recipient_first">
- -
</t> </t>
<a class="o_mail_action_tracking_partner" <a
class="o_mail_action_tracking_partner"
t-att-data-partner="recipient[0]" t-att-data-partner="recipient[0]"
t-attf-href="#model=res.partner&amp;id=#{recipient[0]}"> t-attf-href="#model=res.partner&amp;id=#{recipient[0]}"
<t t-esc="recipient[1]"/> >
<t t-esc="recipient[1]" />
</a> </a>
</t> </t>
</div> </div>
<div class="o_thread_message_note small"> <div class="o_thread_message_note small">
<t t-raw="message.body"/> <t t-raw="message.body" />
</div> </div>
</div> </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 2016 Antonio Espinosa - <antonio.espinosa@tecnativa.com>
Copyright 2019 Alexandre Díaz Copyright 2019 Alexandre Díaz
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). --> 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-name="mail.tracking.status">
<t t-if="tracking['isCc']"> <t t-if="tracking['isCc']">
<span class="mail_tracking_cc"> <span class="mail_tracking_cc">
<i class="fa fa-cc"></i> <i class="fa fa-cc" />
</span> </span>
</t> </t>
<t t-elif="!tracking['isCc'] &amp;&amp; !tracking['partner_id']"> <t t-elif="!tracking['isCc'] &amp;&amp; !tracking['partner_id']">
<span class="mail_anon_recipient"> <span class="mail_anon_recipient">
<i class="fa fa-low-vision"></i> <i class="fa fa-low-vision" />
</span> </span>
</t> </t>
<t t-elif="tracking['status'] === 'unknown'"> <t t-elif="tracking['status'] === 'unknown'">
<span class="mail_tracking_unknown"> <span class="mail_tracking_unknown">
<i class="fa fa-ban"></i> <i class="fa fa-ban" />
</span> </span>
</t> </t>
<t t-elif="tracking['status'] === 'waiting'"> <t t-elif="tracking['status'] === 'waiting'">
<span class="mail_tracking_waiting mail_tracking_pointer"> <span class="mail_tracking_waiting mail_tracking_pointer">
<i class="fa fa-clock-o"></i> <i class="fa fa-clock-o" />
</span> </span>
</t> </t>
<t t-elif="tracking['status'] === 'error'"> <t t-elif="tracking['status'] === 'error'">
<span class="mail_tracking_error mail_tracking_pointer"> <span class="mail_tracking_error mail_tracking_pointer">
<i t-if="tracking['error_type'] === 'no_recipient'" class="fa fa-user-times"></i> <i
<i t-else="" class="fa fa-remove"></i> t-if="tracking['error_type'] === 'no_recipient'"
class="fa fa-user-times"
/>
<i t-else="" class="fa fa-remove" />
</span> </span>
</t> </t>
<t t-elif="tracking['status'] === 'sent'"> <t t-elif="tracking['status'] === 'sent'">
<span class="mail_tracking_sent mail_tracking_pointer"> <span class="mail_tracking_sent mail_tracking_pointer">
<i class="fa fa-check"></i> <i class="fa fa-check" />
</span> </span>
</t> </t>
<t t-elif="tracking['status'] === 'delivered'"> <t t-elif="tracking['status'] === 'delivered'">
<span class="fa-stack mail_tracking_delivered mail_tracking_pointer"> <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-stack-1x" style="margin-left:1px" />
<i class="fa fa-check fa-inverse fa-stack-1x" style="margin-left:-2px;"></i> <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"></i> <i class="fa fa-check fa-stack-1x" style="margin-left:-3px" />
</span> </span>
</t> </t>
<t t-elif="tracking['status'] === 'opened'"> <t t-elif="tracking['status'] === 'opened'">
<span class="fa-stack mail_tracking_opened mail_tracking_pointer"> <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-stack-1x" style="margin-left:1px" />
<i class="fa fa-check fa-inverse fa-stack-1x" style="margin-left:-2px;"></i> <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"></i> <i class="fa fa-check fa-stack-1x" style="margin-left:-3px" />
</span> </span>
</t> </t>
</t> </t>
<t t-extend="mail.widget.Thread.Message"> <t t-extend="mail.widget.Thread.Message">
<t t-jquery="p.o_mail_info" t-operation="after"> <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"> <p class="o_mail_tracking">
<strong>To:</strong> <strong>To:</strong>
<t t-foreach="message.getPartnerTrackings()" t-as="tracking"> <t t-foreach="message.getPartnerTrackings()" t-as="tracking">
@ -62,21 +65,35 @@
- -
</t> </t>
<t t-if="tracking['partner_id']"> <t t-if="tracking['partner_id']">
<a t-attf-class="o_mail_action_tracking_partner #{tracking['isCc'] ? 'o_mail_cc' : ''}" <a
t-attf-class="o_mail_action_tracking_partner #{tracking['isCc'] ? 'o_mail_cc' : ''}"
t-att-data-partner="tracking['partner_id']" t-att-data-partner="tracking['partner_id']"
t-attf-href="#model=res.partner&amp;id=#{tracking['partner_id']}"> t-attf-href="#model=res.partner&amp;id=#{tracking['partner_id']}"
<t t-esc="tracking['recipient']"/> >
<t t-esc="tracking['recipient']" />
</a> </a>
</t> </t>
<t t-else=""> <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 t-if="tracking['status'] === 'error' &amp;&amp; tracking['error_type'] === 'no_recipient'" t-set="title_status" t-value="tracking['error_description']" /> <t
<t t-else="" t-set="title_status" t-value="tracking['status_human']" /> t-if="tracking['status'] === 'error' &amp;&amp; tracking['error_type'] === 'no_recipient'"
<span class="mail_tracking o_mail_action_tracking_status" 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-data-tracking="tracking['tracking_id']"
t-att-title="title_status"> t-att-title="title_status"
<t t-call="mail.tracking.status"/> >
<t t-call="mail.tracking.status" />
</span> </span>
</t> </t>
</p> </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> <!-- Copyright 2016 Antonio Espinosa - <antonio.espinosa@tecnativa.com>
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). --> License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -->
<odoo> <odoo>
<template id="assets_backend" <template
id="assets_backend"
name="mail_tracking assets" name="mail_tracking assets"
inherit_id="web.assets_backend"> inherit_id="web.assets_backend"
>
<xpath expr="." position="inside"> <xpath expr="." position="inside">
<link rel="stylesheet" <link
href="/mail_tracking/static/src/css/mail_tracking.scss"/> rel="stylesheet"
<link rel="stylesheet" href="/mail_tracking/static/src/css/mail_tracking.scss"
href="/mail_tracking/static/src/css/failed_message.scss"/> />
<script type="text/javascript" <link
src="/mail_tracking/static/src/js/mail_tracking.js"/> rel="stylesheet"
<script type="text/javascript" href="/mail_tracking/static/src/css/failed_message.scss"
src="/mail_tracking/static/src/js/failed_message/discuss.js"/> />
<script type="text/javascript" <script
src="/mail_tracking/static/src/js/failed_message/thread.js"/> 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> </xpath>
</template> </template>
</odoo> </odoo>

View File

@ -1,9 +1,9 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8" ?>
<odoo> <odoo>
<record model="ir.ui.view" id="view_message_form"> <record model="ir.ui.view" id="view_message_form">
<field name="model">mail.message</field> <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="arch" type="xml">
<field name="subtype_id" position="after"> <field name="subtype_id" position="after">
<field name="mail_tracking_needs_action" /> <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> <!-- Copyright 2016 Antonio Espinosa - <antonio.espinosa@tecnativa.com>
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). --> License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -->
<odoo> <odoo>
@ -9,50 +9,54 @@
<field name="arch" type="xml"> <field name="arch" type="xml">
<form string="MailTracking event" create="false" edit="false" delete="false"> <form string="MailTracking event" create="false" edit="false" delete="false">
<header> <header>
<field name="state" widget="statusbar"/> <field name="state" widget="statusbar" />
</header> </header>
<sheet> <sheet>
<group> <group>
<field name="name"/> <field name="name" />
</group> </group>
<group> <group>
<group> <group>
<field name="mail_message_id"/> <field name="mail_message_id" />
<field name="mail_id"/> <field name="mail_id" />
<field name="partner_id"/> <field name="partner_id" />
<field name="recipient"/> <field name="recipient" />
<field name="sender"/> <field name="sender" />
</group> </group>
<group> <group>
<field name="timestamp"/> <field name="timestamp" />
<field name="time"/> <field name="time" />
<field name="date"/> <field name="date" />
</group> </group>
</group> </group>
<group attrs="{'invisible': [('bounce_type', '=', False)]}"> <group attrs="{'invisible': [('bounce_type', '=', False)]}">
<field name="bounce_type"/> <field name="bounce_type" />
<field name="bounce_description"/> <field name="bounce_description" />
</group> </group>
<group attrs="{'invisible': [('error_type', '=', False)]}"> <group attrs="{'invisible': [('error_type', '=', False)]}">
<field name="error_smtp_server" <field
attrs="{'invisible': [('error_smtp_server', '=', False)]}"/> name="error_smtp_server"
<field name="error_type"/> attrs="{'invisible': [('error_smtp_server', '=', False)]}"
<field name="error_description"/> />
<field name="error_type" />
<field name="error_description" />
</group> </group>
<label for="tracking_event_ids"/> <label for="tracking_event_ids" />
<div> <div>
<field name="tracking_event_ids"> <field name="tracking_event_ids">
<tree string="Tracking events" <tree
string="Tracking events"
decoration-muted="event_type == 'deferral'" decoration-muted="event_type == 'deferral'"
decoration-danger="event_type in ('hard_bounce', 'soft_bounce', 'spam', 'reject')" decoration-danger="event_type in ('hard_bounce', 'soft_bounce', 'spam', 'reject')"
decoration-info="event_type in ('unsub', 'click', 'open')"> decoration-info="event_type in ('unsub', 'click', 'open')"
<field name="time"/> >
<field name="event_type"/> <field name="time" />
<field name="ip"/> <field name="event_type" />
<field name="url"/> <field name="ip" />
<field name="user_country_id" string="Country"/> <field name="url" />
<field name="os_family" string="OS"/> <field name="user_country_id" string="Country" />
<field name="ua_family" string="User agent"/> <field name="os_family" string="OS" />
<field name="ua_family" string="User agent" />
</tree> </tree>
</field> </field>
</div> </div>
@ -65,17 +69,22 @@
<field name="name">mail.tracking.email.tree</field> <field name="name">mail.tracking.email.tree</field>
<field name="model">mail.tracking.email</field> <field name="model">mail.tracking.email</field>
<field name="arch" type="xml"> <field name="arch" type="xml">
<tree string="MailTracking emails" create="false" edit="false" delete="false" <tree
string="MailTracking emails"
create="false"
edit="false"
delete="false"
decoration-muted="state in (False, 'deferred')" decoration-muted="state in (False, 'deferred')"
decoration-success="state == 'opened'" decoration-success="state == 'opened'"
decoration-danger="state in ('rejected', 'spam', 'bounced', 'soft-bounced', 'error')" decoration-danger="state in ('rejected', 'spam', 'bounced', 'soft-bounced', 'error')"
decoration-info="state == 'unsub'"> decoration-info="state == 'unsub'"
<field name="time"/> >
<field name="date" invisible="1"/> <field name="time" />
<field name="name"/> <field name="date" invisible="1" />
<field name="sender" string="Sender"/> <field name="name" />
<field name="recipient" string="Recipient"/> <field name="sender" string="Sender" />
<field name="state"/> <field name="recipient" string="Recipient" />
<field name="state" />
</tree> </tree>
</field> </field>
</record> </record>
@ -85,25 +94,63 @@
<field name="model">mail.tracking.email</field> <field name="model">mail.tracking.email</field>
<field name="arch" type="xml"> <field name="arch" type="xml">
<search string="MailTracking email search"> <search string="MailTracking email search">
<field name="display_name" string="Email" <field
filter_domain="['|', ('sender', 'ilike', self), ('recipient', 'ilike', self)]"/> name="display_name"
<field name="sender" string="Sender"/> string="Email"
<field name="recipient" string="Recipient"/> filter_domain="['|', ('sender', 'ilike', self), ('recipient', 'ilike', self)]"
<field name="name" string="Subject"/> />
<field name="time" string="Time"/> <field name="sender" string="Sender" />
<field name="date" string="Date"/> <field name="recipient" string="Recipient" />
<filter name="sent" string="Sent" domain="[('state', 'in', ('sent',))]"/> <field name="name" string="Subject" />
<filter name="deferred" string="Deferred" domain="[('state', '=', 'deferred')]"/> <field name="time" string="Time" />
<filter name="delivered" string="Delivered" domain="[('state', 'in', ('delivered', 'opened'))]"/> <field name="date" string="Date" />
<filter name="unsub" string="Unsubscribed" domain="[('state', '=', 'unsub')]"/> <filter name="sent" string="Sent" domain="[('state', 'in', ('sent',))]" />
<filter name="exception" string="Failed" <filter
domain="[('state', 'in', ('error', 'rejected', 'spam', 'bounced', 'soft-bounced'))]"/> name="deferred"
<separator/> 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"> <group expand="0" string="Group By">
<filter string="State" name="group_by_state" domain="[]" context="{'group_by': 'state'}"/> <filter
<filter string="Subject" name="group_by_subject" domain="[]" context="{'group_by': 'name'}"/> string="State"
<filter string="Sender" name="group_by_sender" domain="[]" context="{'group_by': 'sender'}"/> name="group_by_state"
<filter string="Month" name="group_by_month" domain="[]" context="{'group_by': 'date'}"/> 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> </group>
</search> </search>
</field> </field>
@ -114,12 +161,15 @@
<field name="name">MailTracking emails</field> <field name="name">MailTracking emails</field>
<field name="res_model">mail.tracking.email</field> <field name="res_model">mail.tracking.email</field>
<field name="view_mode">tree,form</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> </record>
<!-- Add menu entry in Settings/Email --> <!-- Add menu entry in Settings/Email -->
<menuitem name="Tracking emails" id="menu_mail_tracking_email" <menuitem
name="Tracking emails"
id="menu_mail_tracking_email"
parent="base.menu_email" parent="base.menu_email"
action="action_view_mail_tracking_email"/> action="action_view_mail_tracking_email"
/>
</odoo> </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 2016 Antonio Espinosa - <antonio.espinosa@tecnativa.com>
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). --> License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -->
<odoo> <odoo>
@ -11,40 +11,46 @@
<sheet> <sheet>
<group> <group>
<group> <group>
<field name="tracking_email_id"/> <field name="tracking_email_id" />
<field name="recipient"/> <field name="recipient" />
<field name="event_type"/> <field name="event_type" />
</group> </group>
<group> <group>
<field name="timestamp"/> <field name="timestamp" />
<field name="time"/> <field name="time" />
<field name="date"/> <field name="date" />
</group> </group>
</group> </group>
<group attrs="{'invisible': [('event_type', 'not in', ('sent',))]}"> <group attrs="{'invisible': [('event_type', 'not in', ('sent',))]}">
<field name="smtp_server"/> <field name="smtp_server" />
</group> </group>
<group attrs="{'invisible': [('event_type', 'not in', ('open', 'click'))]}"> <group
<field name="url"/> attrs="{'invisible': [('event_type', 'not in', ('open', 'click'))]}"
>
<field name="url" />
</group> </group>
<group attrs="{'invisible': [('event_type', 'not in', ('open', 'click'))]}"> <group
attrs="{'invisible': [('event_type', 'not in', ('open', 'click'))]}"
>
<group> <group>
<field name="mobile"/> <field name="mobile" />
<field name="ip"/> <field name="ip" />
<field name="user_country_id"/> <field name="user_country_id" />
</group> </group>
<group> <group>
<field name="user_agent"/> <field name="user_agent" />
<field name="ua_family"/> <field name="ua_family" />
<field name="ua_type"/> <field name="ua_type" />
<field name="os_family"/> <field name="os_family" />
</group> </group>
</group> </group>
<group string="Error" <group
attrs="{'invisible': [('error_type', '=', False)]}"> string="Error"
<field name="error_type"/> attrs="{'invisible': [('error_type', '=', False)]}"
<field name="error_description"/> >
<field name="error_details"/> <field name="error_type" />
<field name="error_description" />
<field name="error_details" />
</group> </group>
</sheet> </sheet>
</form> </form>
@ -55,21 +61,29 @@
<field name="name">mail.tracking.event.tree</field> <field name="name">mail.tracking.event.tree</field>
<field name="model">mail.tracking.event</field> <field name="model">mail.tracking.event</field>
<field name="arch" type="xml"> <field name="arch" type="xml">
<tree string="MailTracking events" create="false" edit="false" delete="false" <tree
string="MailTracking events"
create="false"
edit="false"
delete="false"
decoration-muted="event_type == 'deferred'" decoration-muted="event_type == 'deferred'"
decoration-danger="event_type in ('hard_bounce', 'soft_bounce', 'spam', 'reject')" decoration-danger="event_type in ('hard_bounce', 'soft_bounce', 'spam', 'reject')"
decoration-info="event_type in ('unsub', 'click', 'open')"> decoration-info="event_type in ('unsub', 'click', 'open')"
<field name="time"/> >
<field name="tracking_email_id"/> <field name="time" />
<field name="recipient"/> <field name="tracking_email_id" />
<field name="event_type"/> <field name="recipient" />
<field name="error_details" invisible="not context.get('event_error_filter', False)"/> <field name="event_type" />
<field name="date" invisible="1"/> <field
<field name="ip"/> name="error_details"
<field name="url"/> invisible="not context.get('event_error_filter', False)"
<field name="user_country_id" string="Country"/> />
<field name="os_family" string="OS"/> <field name="date" invisible="1" />
<field name="ua_family" string="User agent"/> <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> </tree>
</field> </field>
</record> </record>
@ -79,32 +93,88 @@
<field name="model">mail.tracking.event</field> <field name="model">mail.tracking.event</field>
<field name="arch" type="xml"> <field name="arch" type="xml">
<search string="MailTracking event search"> <search string="MailTracking event search">
<field name="tracking_email_id" string="Message" <field
filter_domain="[('tracking_email_id', 'ilike', self)]"/> name="tracking_email_id"
<field name="recipient" string="Recipient"/> string="Message"
<field name="time" string="Time"/> filter_domain="[('tracking_email_id', 'ilike', self)]"
<field name="date" string="Date"/> />
<field name="ip" string="IP"/> <field name="recipient" string="Recipient" />
<field name="url" string="URL"/> <field name="time" string="Time" />
<filter name="sent" string="Sent" domain="[('event_type', '=', 'sent')]"/> <field name="date" string="Date" />
<filter name="delivered" string="Delivered" domain="[('event_type', '=', 'delivered')]"/> <field name="ip" string="IP" />
<filter name="click" string="Click" domain="[('event_type', '=', 'click')]"/> <field name="url" string="URL" />
<filter name="open" string="Open" domain="[('event_type', '=', 'open')]"/> <filter name="sent" string="Sent" domain="[('event_type', '=', 'sent')]" />
<filter name="unsub" string="Unsubscribe" domain="[('event_type', '=', 'unsub')]"/> <filter
<filter name="bounce" string="Bounce" name="delivered"
domain="[('event_type', 'in', ('hard_bounce', 'soft_bounce'))]"/> string="Delivered"
<filter name="exception" string="Failed" 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'))]" domain="[('event_type', 'in', ('reject', 'spam'))]"
context="{'event_error_filter': True}"/> context="{'event_error_filter': True}"
<separator/> />
<separator />
<group expand="0" string="Group By"> <group expand="0" string="Group By">
<filter string="Type" name="group_by_type" domain="[]" context="{'group_by': 'event_type'}"/> <filter
<filter string="Message" name="group_by_message" domain="[]" context="{'group_by': 'tracking_email_id'}"/> string="Type"
<filter string="OS" name="group_by_os" domain="[('os_family', '!=', False)]" context="{'group_by': 'os_family'}"/> name="group_by_type"
<filter string="User agent" name="group_by_user_agent" domain="[('ua_family', '!=', False)]" context="{'group_by': 'ua_family'}"/> domain="[]"
<filter string="User agent type" name="group_by_user_agent_type" domain="[('ua_type', '!=', False)]" context="{'group_by': 'ua_type'}"/> context="{'group_by': 'event_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="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> </group>
</search> </search>
</field> </field>
@ -114,13 +184,16 @@
<field name="name">MailTracking events</field> <field name="name">MailTracking events</field>
<field name="res_model">mail.tracking.event</field> <field name="res_model">mail.tracking.event</field>
<field name="view_mode">tree,form</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> </record>
<!-- Add menu entry in Settings/Email --> <!-- Add menu entry in Settings/Email -->
<menuitem name="Tracking events" id="menu_mail_tracking_event" <menuitem
name="Tracking events"
id="menu_mail_tracking_event"
parent="base.menu_email" parent="base.menu_email"
action="action_view_mail_tracking_event"/> action="action_view_mail_tracking_event"
/>
</odoo> </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 2016 Antonio Espinosa - <antonio.espinosa@tecnativa.com>
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). --> License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -->
<odoo> <odoo>
@ -7,26 +7,32 @@
<field name="name">Partner Form with tracking emails</field> <field name="name">Partner Form with tracking emails</field>
<field name="model">res.partner</field> <field name="model">res.partner</field>
<field name="priority">46</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"> <field name="arch" type="xml">
<div name="button_box" position="inside"> <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, context="{'search_default_recipient': email,
'default_recipient': email}" 'default_recipient': email}"
type="action" type="action"
class="oe_stat_button" class="oe_stat_button"
icon="fa-envelope-o" icon="fa-envelope-o"
attrs="{'invisible': [('email', '=', False)]}"> attrs="{'invisible': [('email', '=', False)]}"
<field name="tracking_emails_count" >
<field
name="tracking_emails_count"
widget="statinfo" widget="statinfo"
string="Tracking emails"/> string="Tracking emails"
/>
</button> </button>
</div> </div>
<xpath expr="//field[@name='email']/.." position="after"> <xpath expr="//field[@name='email']/.." position="after">
<field name="email_score" widget="progressbar" <field
attrs="{'invisible': [('email', '=', False)]}"/> name="email_score"
<field name="email_bounced" widget="progressbar"
attrs="{'invisible': [('email', '=', False)]}"/> attrs="{'invisible': [('email', '=', False)]}"
/>
<field name="email_bounced" attrs="{'invisible': [('email', '=', False)]}" />
</xpath> </xpath>
</field> </field>
</record> </record>
@ -34,12 +40,15 @@
<record model="ir.ui.view" id="view_res_partner_filter"> <record model="ir.ui.view" id="view_res_partner_filter">
<field name="name">Filter bounced partners</field> <field name="name">Filter bounced partners</field>
<field name="model">res.partner</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"> <field name="arch" type="xml">
<filter name="type_company" position="after"> <filter name="type_company" position="after">
<separator/> <separator />
<filter string="Email bounced" name="email_bounced" <filter
domain="[('email', '!=' , False), ('email_bounced', '=', True)]"/> string="Email bounced"
name="email_bounced"
domain="[('email', '!=' , False), ('email_bounced', '=', True)]"
/>
</filter> </filter>
</field> </field>
</record> </record>

View File

@ -1,14 +1,17 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8" ?>
<odoo> <odoo>
<record id="mailgun_manual_check" model="ir.ui.view"> <record id="mailgun_manual_check" model="ir.ui.view">
<field name="name">Manual Mailgun check</field> <field name="name">Manual Mailgun check</field>
<field name="model">mail.tracking.email</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="arch" type="xml">
<field name="state" position="before"> <field name="state" position="before">
<button name="action_manual_check_mailgun" <button
type="object" string="Re-sync Mailgun"/> name="action_manual_check_mailgun"
type="object"
string="Re-sync Mailgun"
/>
</field> </field>
</field> </field>
</record> </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 Tecnativa - Pedro M. Baeza
Copyright 2016 Carlos Dauden - Tecnativa <carlos.dauden@tecnativa.com> Copyright 2016 Carlos Dauden - Tecnativa <carlos.dauden@tecnativa.com>
Copyright 2017 Tecnativa <vicent.cubellsn@tecnativa.com> Copyright 2017 Tecnativa <vicent.cubellsn@tecnativa.com>
@ -8,18 +8,44 @@
<record id="view_partner_form_mailgun" model="ir.ui.view"> <record id="view_partner_form_mailgun" model="ir.ui.view">
<field name="name">Partner Mailgun button</field> <field name="name">Partner Mailgun button</field>
<field name="model">res.partner</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="arch" type="xml">
<field name="email_bounced" position="after"> <field name="email_bounced" position="after">
<label for="check_email_bounced" string="Mailgun" <label
attrs="{'invisible': [('email', '=', False)]}"/> for="check_email_bounced"
<div name="mailgun_buttons" attrs="{'invisible': [('email', '=', False)]}"> string="Mailgun"
<button name="check_email_bounced" type="object" string="Check Mailgun" class="oe_link"/> attrs="{'invisible': [('email', '=', False)]}"
<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" <div
attrs="{'invisible': [('email_bounced', '=', True)]}"/> name="mailgun_buttons"
<button name="force_unset_bounced" type="object" string="Unset Bounced" class="oe_link" attrs="{'invisible': [('email', '=', False)]}"
attrs="{'invisible': [('email_bounced', '=', 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> </div>
</field> </field>
</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 2016 Antonio Espinosa - <antonio.espinosa@tecnativa.com>
Copyright 2017 Vicent Cubells - <vicent.cubells@tecnativa.com> Copyright 2017 Vicent Cubells - <vicent.cubells@tecnativa.com>
Copyright 2017 David Vidal - <david.vidal@tecnativa.com> Copyright 2017 David Vidal - <david.vidal@tecnativa.com>
@ -19,15 +19,27 @@
<field name="domain">[('mass_mailing_id', '!=', False)]</field> <field name="domain">[('mass_mailing_id', '!=', False)]</field>
</record> </record>
<menuitem name="Mail tracking" id="mail_tracking_menu" <menuitem
parent="mass_mailing.mass_mailing_menu_root" sequence="50"/> name="Mail tracking"
id="mail_tracking_menu"
parent="mass_mailing.mass_mailing_menu_root"
sequence="50"
/>
<menuitem name="Emails" id="mail_tracking_email_menu" <menuitem
parent="mail_tracking_menu" sequence="1" name="Emails"
action="action_view_mail_tracking_email"/> 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" <menuitem
parent="mail_tracking_menu" sequence="2" name="Events"
action="action_view_mail_tracking_event"/> id="mail_tracking_event_menu"
parent="mail_tracking_menu"
sequence="2"
action="action_view_mail_tracking_event"
/>
</odoo> </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 2016 Antonio Espinosa - <antonio.espinosa@tecnativa.com>
Copyright 2017 Vicent Cubells - <vicent.cubells@tecnativa.com> Copyright 2017 Vicent Cubells - <vicent.cubells@tecnativa.com>
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). --> 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"> <record model="ir.ui.view" id="mailing_trace_view_form">
<field name="name">Add tracking email info</field> <field name="name">Add tracking email info</field>
<field name="model">mailing.trace</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"> <field name="arch" type="xml">
<xpath expr="//form/sheet" position="inside"> <xpath expr="//form/sheet" position="inside">
<group> <group>
<field name="mail_tracking_id" /> <field name="mail_tracking_id" />
</group> </group>
<label for="tracking_event_ids"/> <label for="tracking_event_ids" />
<div> <div>
<field name="tracking_event_ids"> <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')"> <tree
<field name="time"/> string="Tracking events"
<field name="event_type"/> 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="ip"/> >
<field name="url"/> <field name="time" />
<field name="user_country_id" string="Country"/> <field name="event_type" />
<field name="os_family" string="OS"/> <field name="ip" />
<field name="ua_family" string="User agent"/> <field name="url" />
<field name="user_country_id" string="Country" />
<field name="os_family" string="OS" />
<field name="ua_family" string="User agent" />
</tree> </tree>
</field> </field>
</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 2016 Antonio Espinosa - <antonio.espinosa@tecnativa.com>
Copyright 2017 Vicent Cubells - <vicent.cubells@tecnativa.com> Copyright 2017 Vicent Cubells - <vicent.cubells@tecnativa.com>
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). --> 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"> <record model="ir.ui.view" id="view_mail_tracking_email_form">
<field name="name">Add mass mailing and mail stadistics</field> <field name="name">Add mass mailing and mail stadistics</field>
<field name="model">mail.tracking.email</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="arch" type="xml">
<field name="date" position="after"> <field name="date" position="after">
<field name="mass_mailing_id"/> <field name="mass_mailing_id" />
<field name="mail_stats_id"/> <field name="mail_stats_id" />
</field> </field>
</field> </field>
</record> </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 2016 Antonio Espinosa - <antonio.espinosa@tecnativa.com>
Copyright 2017 Vicent Cubells - <vicent.cubells@tecnativa.com> Copyright 2017 Vicent Cubells - <vicent.cubells@tecnativa.com>
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). --> 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"> <record model="ir.ui.view" id="view_mail_mass_mailing_contact_tree">
<field name="name">Add email score and stars</field> <field name="name">Add email score and stars</field>
<field name="model">mailing.contact</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="arch" type="xml">
<field name="is_blacklisted" position="after"> <field name="is_blacklisted" position="after">
<field name="email_bounced"/> <field name="email_bounced" />
<field name="email_score" widget="progressbar"/> <field name="email_score" widget="progressbar" />
</field> </field>
</field> </field>
</record> </record>
@ -19,11 +19,14 @@
<record model="ir.ui.view" id="view_mail_mass_mailing_contact_search"> <record model="ir.ui.view" id="view_mail_mass_mailing_contact_search">
<field name="name">Filter bounced contacts</field> <field name="name">Filter bounced contacts</field>
<field name="model">mailing.contact</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"> <field name="arch" type="xml">
<filter name="filter_not_email_bl" position="after"> <filter name="filter_not_email_bl" position="after">
<filter string="Email bounced" name="email_bounced" <filter
domain="[('email_bounced', '=', True)]"/> string="Email bounced"
name="email_bounced"
domain="[('email_bounced', '=', True)]"
/>
</filter> </filter>
</field> </field>
</record> </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> <!-- © 2016 Jairo Llopis <jairo.llopis@tecnativa.com>
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). --> License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -->
<data noupdate="1"> <data noupdate="1">
<record id="reason_not_interested" <record
id="reason_not_interested"
model="mail.unsubscription.reason" model="mail.unsubscription.reason"
forcecreate="False"> forcecreate="False"
>
<field name="name">I'm not interested</field> <field name="name">I'm not interested</field>
<field name="sequence">10</field> <field name="sequence">10</field>
<field name="details_required" eval="False"/> <field name="details_required" eval="False" />
</record> </record>
<record id="reason_not_requested" <record
id="reason_not_requested"
model="mail.unsubscription.reason" model="mail.unsubscription.reason"
forcecreate="False"> forcecreate="False"
>
<field name="name">I did not request this</field> <field name="name">I did not request this</field>
<field name="sequence">20</field> <field name="sequence">20</field>
<field name="details_required" eval="False"/> <field name="details_required" eval="False" />
</record> </record>
<record id="reason_too_many" <record id="reason_too_many" model="mail.unsubscription.reason" forcecreate="False">
model="mail.unsubscription.reason"
forcecreate="False">
<field name="name">I get too many emails</field> <field name="name">I get too many emails</field>
<field name="sequence">30</field> <field name="sequence">30</field>
<field name="details_required" eval="False"/> <field name="details_required" eval="False" />
</record> </record>
<record id="reason_other" <record id="reason_other" model="mail.unsubscription.reason" forcecreate="False">
model="mail.unsubscription.reason"
forcecreate="False">
<field name="name">Other reason</field> <field name="name">Other reason</field>
<field name="sequence">100</field> <field name="sequence">100</field>
<field name="details_required" eval="True"/> <field name="details_required" eval="True" />
</record> </record>
</data> </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> <!-- Copyright 2016 Jairo Llopis <jairo.llopis@tecnativa.com>
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). --> License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -->
<odoo> <odoo>
<template id="assets_frontend_demo" <template id="assets_frontend_demo" inherit_id="web.assets_frontend">
inherit_id="web.assets_frontend">
<xpath expr="."> <xpath expr=".">
<script type="text/javascript" <script
src="/mass_mailing_custom_unsubscribe/static/src/js/contact.tour.js"/> type="text/javascript"
<script type="text/javascript" src="/mass_mailing_custom_unsubscribe/static/src/js/contact.tour.js"
src="/mass_mailing_custom_unsubscribe/static/src/js/partner.tour.js"/> />
<script
type="text/javascript"
src="/mass_mailing_custom_unsubscribe/static/src/js/partner.tour.js"
/>
</xpath> </xpath>
</template> </template>
<!--This is set here to make tours work--> <!--This is set here to make tours work-->
<template id="mass_mailing_custom_unsubscribe.layout" <template id="mass_mailing_custom_unsubscribe.layout" inherit_id="mass_mailing.layout">
inherit_id="mass_mailing.layout">
<xpath expr="//t[@t-set='head']" position="inside"> <xpath expr="//t[@t-set='head']" position="inside">
<t t-call-assets="web_editor.assets_wysiwyg"/> <t t-call-assets="web_editor.assets_wysiwyg" />
<t t-call-assets="web.assets_frontend"/> <t t-call-assets="web.assets_frontend" />
</xpath> </xpath>
</template> </template>

View File

@ -1,14 +1,13 @@
/* Copyright 2016 Jairo Llopis <jairo.llopis@tecnativa.com> /* Copyright 2016 Jairo Llopis <jairo.llopis@tecnativa.com>
* License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). */ * License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). */
odoo.define("mass_mailing_custom_unsubscribe.contact_tour", odoo.define("mass_mailing_custom_unsubscribe.contact_tour", function(require) {
function (require) {
"use strict"; "use strict";
var base = require("web_editor.base"); var base = require("web_editor.base");
var tour = require("web_tour.tour"); var tour = require("web_tour.tour");
// Allow to know if an element is required // Allow to know if an element is required
$.extend($.expr[':'], { $.extend($.expr[":"], {
propRequired: function (element) { propRequired: function(element) {
return $(element).prop("required"); return $(element).prop("required");
}, },
}); });
@ -27,8 +26,7 @@ odoo.define("mass_mailing_custom_unsubscribe.contact_tour",
}, },
{ {
content: "Switch to not interested reason", content: "Switch to not interested reason",
trigger: trigger: '.radio:contains("I\'m not interested") :radio:not(:checked)',
".radio:contains(\"I'm not interested\") :radio:not(:checked)",
extra_trigger: "[name='details']:propRequired", extra_trigger: "[name='details']:propRequired",
}, },
{ {
@ -38,7 +36,8 @@ odoo.define("mass_mailing_custom_unsubscribe.contact_tour",
}, },
{ {
content: "Successfully unsubscribed", content: "Successfully unsubscribed",
trigger: "body:not(:has(#reason_form)) #subscription_info " + trigger:
"body:not(:has(#reason_form)) #subscription_info " +
":contains('successfully unsubscribed from')", ":contains('successfully unsubscribed from')",
}, },
{ {
@ -60,8 +59,8 @@ odoo.define("mass_mailing_custom_unsubscribe.contact_tour",
{ {
content: "Choose other reason", content: "Choose other reason",
trigger: ".radio:contains('Other reason') :radio", trigger: ".radio:contains('Other reason') :radio",
extra_trigger: ".radio:contains('Other reason') " + extra_trigger:
":radio:not(:checked)", ".radio:contains('Other reason') " + ":radio:not(:checked)",
}, },
{ {
content: "Add details to reason", content: "Add details to reason",
@ -75,28 +74,27 @@ odoo.define("mass_mailing_custom_unsubscribe.contact_tour",
}, },
{ {
content: "Successfully unsubscribed", content: "Successfully unsubscribed",
trigger: trigger: "#subscription_info:contains('Your changes have been saved.')",
"#subscription_info:contains('Your changes have been saved.')",
}, },
{ {
content: "Subscribe again to list 0", content: "Subscribe again to list 0",
// eslint-disable-next-line no-multi-str // eslint-disable-next-line no-multi-str
trigger:"body:not(:has(#unsubscribe_form #custom_div_feedback\ trigger:
"body:not(:has(#unsubscribe_form #custom_div_feedback\
:visible)):has(.alert-success) li:contains('test list 0') \ :visible)):has(.alert-success) li:contains('test list 0') \
input:not(:checked)", input:not(:checked)",
}, },
{ {
content: "Update subscriptions 3nd time", content: "Update subscriptions 3nd time",
// eslint-disable-next-line no-multi-str // eslint-disable-next-line no-multi-str
trigger:"#unsubscribe_form:not(\ trigger:
"#unsubscribe_form:not(\
:has(.js_unsubscription_reason:visible)) :submit", :has(.js_unsubscription_reason:visible)) :submit",
}, },
{ {
content: "Successfully subscribed", content: "Successfully subscribed",
trigger: trigger: "#subscription_info:contains('Your changes have been saved.')",
"#subscription_info:contains('Your changes have been saved.')",
}, },
] ]
); );
} });
);

View File

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

View File

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

View File

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

View File

@ -1,31 +1,36 @@
<?xml version="1.0" encoding="utf-8" ?> <?xml version="1.0" encoding="utf-8" ?>
<!-- Copyright 2016 Jairo Llopis <jairo.llopis@tecnativa.com> <!-- Copyright 2016 Jairo Llopis <jairo.llopis@tecnativa.com>
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). --> License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -->
<odoo> <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 --> <!-- Hide original feedback textarea to put another one after mailing lists checkboxes -->
<xpath expr="//div[@id='div_feedback']" position="attributes"> <xpath expr="//div[@id='div_feedback']" position="attributes">
<attribute name="class" add="d-none" separator=" "/> <attribute name="class" add="d-none" separator=" " />
</xpath> </xpath>
<!-- Add reasons to mass mailing list manager --> <!-- Add reasons to mass mailing list manager -->
<xpath expr="//ul[hasclass('list-group')]" position="after"> <xpath expr="//ul[hasclass('list-group')]" position="after">
<t t-call="mass_mailing_custom_unsubscribe.reason"> <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> </t>
</xpath> </xpath>
</template> </template>
<template id="unsubscribed" <template
id="unsubscribed"
inherit_id="mass_mailing.unsubscribed" inherit_id="mass_mailing.unsubscribed"
name="Add Reasons to Blacklist Management Form"> name="Add Reasons to Blacklist Management Form"
>
<!-- Add reasons to blacklist manager --> <!-- Add reasons to blacklist manager -->
<xpath expr="//div[@id='button_add_blacklist']" position="before"> <xpath expr="//div[@id='button_add_blacklist']" position="before">
<form id="unsubscribe_form"> <form id="unsubscribe_form">
<t t-call="mass_mailing_custom_unsubscribe.reason"> <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> </t>
</form> </form>
</xpath> </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> <!-- Copyright 2016 Jairo Llopis <jairo.llopis@tecnativa.com>
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). --> License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -->
<odoo> <odoo>
<template id="assets_backend" <template id="assets_backend" inherit_id="mass_mailing.assets_backend">
inherit_id="mass_mailing.assets_backend">
<!-- Need to replace the asset because upstream is impossible to extend --> <!-- Need to replace the asset because upstream is impossible to extend -->
<xpath expr="//script[@src='/mass_mailing/static/src/js/unsubscribe.js']" position="replace"> <xpath
<script type="text/javascript" expr="//script[@src='/mass_mailing/static/src/js/unsubscribe.js']"
src="/mass_mailing_custom_unsubscribe/static/src/js/unsubscribe.js"/> position="replace"
>
<script
type="text/javascript"
src="/mass_mailing_custom_unsubscribe/static/src/js/unsubscribe.js"
/>
</xpath> </xpath>
</template> </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> <!-- Copyright 2016 Pedro M. Baeza <pedro.baeza@tecnativa.com>
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). --> License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -->
<odoo> <odoo>
<record id="view_mail_mass_mailing_list_form" model="ir.ui.view"> <record id="view_mail_mass_mailing_list_form" model="ir.ui.view">
<field name="model">mailing.list</field> <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="arch" type="xml">
<field name="is_public" position="after"> <field name="is_public" position="after">
<field name="not_cross_unsubscriptable" <field
attrs="{'invisible': [('is_public', '=', False)]}"/> name="not_cross_unsubscriptable"
attrs="{'invisible': [('is_public', '=', False)]}"
/>
</field> </field>
</field> </field>
</record> </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> <!-- Copyright 2016 Jairo Llopis <jairo.llopis@tecnativa.com>
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). --> License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -->
<odoo> <odoo>
<record id="mail_unsubscription_reason_view_form" model="ir.ui.view"> <record id="mail_unsubscription_reason_view_form" model="ir.ui.view">
@ -11,11 +10,11 @@
<form> <form>
<sheet> <sheet>
<group> <group>
<field name="name"/> <field name="name" />
<field name="details_required"/> <field name="details_required" />
<field name="sequence"/> <field name="sequence" />
</group> </group>
<div class="oe_chatter"/> <div class="oe_chatter" />
</sheet> </sheet>
</form> </form>
</field> </field>
@ -26,9 +25,9 @@
<field name="model">mail.unsubscription.reason</field> <field name="model">mail.unsubscription.reason</field>
<field name="arch" type="xml"> <field name="arch" type="xml">
<tree> <tree>
<field name="sequence" widget="handle"/> <field name="sequence" widget="handle" />
<field name="name"/> <field name="name" />
<field name="details_required"/> <field name="details_required" />
</tree> </tree>
</field> </field>
</record> </record>
@ -38,8 +37,8 @@
<field name="model">mail.unsubscription.reason</field> <field name="model">mail.unsubscription.reason</field>
<field name="arch" type="xml"> <field name="arch" type="xml">
<search> <search>
<field name="name"/> <field name="name" />
<field name="details_required"/> <field name="details_required" />
</search> </search>
</field> </field>
</record> </record>
@ -47,11 +46,13 @@
<act_window <act_window
id="mail_unsubscription_reason_action" id="mail_unsubscription_reason_action"
name="Unsubscription Reasons" name="Unsubscription Reasons"
res_model="mail.unsubscription.reason"/> res_model="mail.unsubscription.reason"
/>
<menuitem <menuitem
id="mail_unsubscription_reason_menu" id="mail_unsubscription_reason_menu"
parent="mass_mailing.mass_mailing_configuration" parent="mass_mailing.mass_mailing_configuration"
action="mail_unsubscription_reason_action"/> action="mail_unsubscription_reason_action"
/>
</odoo> </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> <!-- Copyright 2016 Jairo Llopis <jairo.llopis@tecnativa.com>
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). --> License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -->
<odoo> <odoo>
<record id="mail_unsubscription_view_form" model="ir.ui.view"> <record id="mail_unsubscription_view_form" model="ir.ui.view">
@ -11,26 +10,31 @@
<form> <form>
<sheet> <sheet>
<group> <group>
<field name="date"/> <field name="date" />
<field name="mass_mailing_id"/> <field name="mass_mailing_id" />
<field name="unsubscriber_id"/> <field name="unsubscriber_id" />
<field name="mailing_list_ids" widget="many2many_tags"/> <field name="mailing_list_ids" widget="many2many_tags" />
<field name="email"/> <field name="email" />
<field name="action"/> <field name="action" />
<field name="reason_id" <field
attrs="{'required': [('action', '=', 'unsubscription')]}"/> name="reason_id"
<field name="details" attrs="{'required': [('action', '=', 'unsubscription')]}"
attrs="{'required': [('details_required', '=', True)]}"/> />
<field name="details_required" invisible="True"/> <field
<field name="metadata"/> name="details"
attrs="{'required': [('details_required', '=', True)]}"
/>
<field name="details_required" invisible="True" />
<field name="metadata" />
</group> </group>
</sheet> </sheet>
<div class="oe_chatter"> <div class="oe_chatter">
<field name="message_follower_ids" <field
name="message_follower_ids"
widget="mail_followers" widget="mail_followers"
groups="base.group_user"/> groups="base.group_user"
<field name="message_ids" />
widget="mail_thread"/> <field name="message_ids" widget="mail_thread" />
</div> </div>
</form> </form>
</field> </field>
@ -41,14 +45,14 @@
<field name="model">mail.unsubscription</field> <field name="model">mail.unsubscription</field>
<field name="arch" type="xml"> <field name="arch" type="xml">
<tree decoration-warning="action in ['unsubscription', 'blacklist_add']"> <tree decoration-warning="action in ['unsubscription', 'blacklist_add']">
<field name="date"/> <field name="date" />
<field name="mass_mailing_id"/> <field name="mass_mailing_id" />
<field name="unsubscriber_id"/> <field name="unsubscriber_id" />
<field name="mailing_list_ids" widget="many2many_tags"/> <field name="mailing_list_ids" widget="many2many_tags" />
<field name="email" invisible="True"/> <field name="email" invisible="True" />
<field name="action"/> <field name="action" />
<field name="reason_id"/> <field name="reason_id" />
<field name="details" invisible="True"/> <field name="details" invisible="True" />
</tree> </tree>
</field> </field>
</record> </record>
@ -58,32 +62,44 @@
<field name="model">mail.unsubscription</field> <field name="model">mail.unsubscription</field>
<field name="arch" type="xml"> <field name="arch" type="xml">
<search> <search>
<field name="mass_mailing_id"/> <field name="mass_mailing_id" />
<field name="unsubscriber_id"/> <field name="unsubscriber_id" />
<field name="mailing_list_ids"/> <field name="mailing_list_ids" />
<field name="email"/> <field name="email" />
<field name="reason_id"/> <field name="reason_id" />
<field name="details"/> <field name="details" />
<separator/> <separator />
<group string="Group by"> <group string="Group by">
<filter name="group_by_month" <filter
name="group_by_month"
string="Month" string="Month"
context="{'group_by': 'date:month'}"/> context="{'group_by': 'date:month'}"
<filter name="group_by_year" />
<filter
name="group_by_year"
string="Year" string="Year"
context="{'group_by': 'date:year'}"/> context="{'group_by': 'date:year'}"
<filter name="group_by_action" />
<filter
name="group_by_action"
string="Action" string="Action"
context="{'group_by': 'action'}"/> context="{'group_by': 'action'}"
<filter name="group_by_email" />
<filter
name="group_by_email"
string="Email" string="Email"
context="{'group_by': 'email'}"/> context="{'group_by': 'email'}"
<filter name="group_by_reason" />
<filter
name="group_by_reason"
string="Reason" string="Reason"
context="{'group_by': 'reason_id'}"/> context="{'group_by': 'reason_id'}"
<filter name="group_by_mass_mailing" />
<filter
name="group_by_mass_mailing"
string="Mass mailing" string="Mass mailing"
context="{'group_by': 'mass_mailing_id'}"/> context="{'group_by': 'mass_mailing_id'}"
/>
</group> </group>
</search> </search>
</field> </field>
@ -94,9 +110,9 @@
<field name="model">mail.unsubscription</field> <field name="model">mail.unsubscription</field>
<field name="arch" type="xml"> <field name="arch" type="xml">
<pivot string="(Un)subscriptions"> <pivot string="(Un)subscriptions">
<field name="reason_id" type="row"/> <field name="reason_id" type="row" />
<field name="mailing_list_ids" type="row"/> <field name="mailing_list_ids" type="row" />
<field name="action" type="col"/> <field name="action" type="col" />
</pivot> </pivot>
</field> </field>
</record> </record>
@ -106,18 +122,23 @@
<field name="model">mail.unsubscription</field> <field name="model">mail.unsubscription</field>
<field name="arch" type="xml"> <field name="arch" type="xml">
<graph string="(Un)subscriptions"> <graph string="(Un)subscriptions">
<field name="date" type="row"/> <field name="date" type="row" />
<field name="action" type="col"/> <field name="action" type="col" />
</graph> </graph>
</field> </field>
</record> </record>
<act_window id="mail_unsubscription_action" <act_window
id="mail_unsubscription_action"
name="(Un)subscriptions" name="(Un)subscriptions"
view_mode="tree,form,pivot,graph" view_mode="tree,form,pivot,graph"
res_model="mail.unsubscription"/> res_model="mail.unsubscription"
/>
<menuitem id="mail_unsubscription_menu" parent="mass_mailing.mass_mailing_menu_root" <menuitem
action="mail_unsubscription_action"/> id="mail_unsubscription_menu"
parent="mass_mailing.mass_mailing_menu_root"
action="mail_unsubscription_action"
/>
</odoo> </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 <!-- Copyright 2020 Tecnativa - João Marques
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). --> License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -->
<odoo> <odoo>
<template id="assets_frontend_demo" <template id="assets_frontend_demo" inherit_id="web.assets_frontend">
inherit_id="web.assets_frontend">
<xpath expr="."> <xpath expr=".">
<script type="text/javascript" <script
src="/mass_mailing_custom_unsubscribe_event/static/src/js/tour.js"/> type="text/javascript"
src="/mass_mailing_custom_unsubscribe_event/static/src/js/tour.js"
/>
</xpath> </xpath>
</template> </template>

View File

@ -1,14 +1,13 @@
/* Copyright 2020 Tecnativa - João Marques /* Copyright 2020 Tecnativa - João Marques
* License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). */ * License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). */
odoo.define("mass_mailing_custom_unsubscribe_event.tour", odoo.define("mass_mailing_custom_unsubscribe_event.tour", function(require) {
function (require) {
"use strict"; "use strict";
var base = require("web_editor.base"); var base = require("web_editor.base");
var tour = require("web_tour.tour"); var tour = require("web_tour.tour");
// Allow to know if an element is required // Allow to know if an element is required
$.extend($.expr[':'], { $.extend($.expr[":"], {
propRequired: function (element) { propRequired: function(element) {
return $(element).prop("required"); return $(element).prop("required");
}, },
}); });
@ -27,8 +26,7 @@ odoo.define("mass_mailing_custom_unsubscribe_event.tour",
}, },
{ {
content: "Switch to not interested reason", content: "Switch to not interested reason",
trigger: trigger: '.radio:contains("I\'m not interested") :radio:not(:checked)',
".radio:contains(\"I'm not interested\") :radio:not(:checked)",
extra_trigger: "[name='details']:propRequired", extra_trigger: "[name='details']:propRequired",
}, },
{ {
@ -38,10 +36,10 @@ odoo.define("mass_mailing_custom_unsubscribe_event.tour",
}, },
{ {
content: "Successfully unsubscribed", content: "Successfully unsubscribed",
trigger: "body:not(:has(#reason_form)) #subscription_info " + trigger:
"body:not(:has(#reason_form)) #subscription_info " +
":contains('successfully unsubscribed')", ":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 <!-- Copyright 2020 Tecnativa - Pedro M. Baeza
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). --> License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -->
<odoo> <odoo>
<record id="view_event_registration_form" model="ir.ui.view"> <record id="view_event_registration_form" model="ir.ui.view">
<field name="name">event.registration.form - Add opt_out</field> <field name="name">event.registration.form - Add opt_out</field>
<field name="model">event.registration</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="arch" type="xml">
<field name="email" position="after"> <field name="email" position="after">
<field name="opt_out" /> <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> <!-- Copyright 2016 Antonio Espinosa <antonio.espinosa@tecnativa.com>
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). --> License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -->
<odoo noupdate="1"> <odoo noupdate="1">
<record id="event_draft" model="event.registration.state"> <record id="event_draft" model="event.registration.state">

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