mirror of
https://github.com/bringout/euro-office.git
synced 2026-04-18 04:22:01 +02:00
init: Euro-Office Odoo 16.0 modules
Based on onlyoffice_odoo by Ascensio System SIA (ONLYOFFICE, LGPL-3). Rebranded and adapted for Euro-Office by bring.out d.o.o. Modules: - eurooffice_odoo: base integration - eurooffice_odoo_templates: document templates - eurooffice_odoo_oca_dms: OCA DMS integration (replaces Enterprise documents) All references renamed: onlyoffice -> eurooffice, ONLYOFFICE -> Euro-Office. Original copyright notices preserved.
This commit is contained in:
commit
b59a9dc6bb
347 changed files with 16699 additions and 0 deletions
|
|
@ -0,0 +1,15 @@
|
|||
#
|
||||
# (c) Copyright Ascensio System SIA 2024
|
||||
#
|
||||
|
||||
DOC_SERVER_PUBLIC_URL = "eurooffice_connector.doc_server_public_url"
|
||||
DOC_SERVER_ODOO_URL = "eurooffice_connector.doc_server_odoo_url"
|
||||
DOC_SERVER_INNER_URL = "eurooffice_connector.doc_server_inner_url"
|
||||
DOC_SERVER_JWT_SECRET = "eurooffice_connector.doc_server_jwt_secret"
|
||||
DOC_SERVER_JWT_HEADER = "eurooffice_connector.doc_server_jwt_header"
|
||||
DOC_SERVER_DEMO = "eurooffice_connector.doc_server_demo"
|
||||
DOC_SERVER_DEMO_DATE = "eurooffice_connector.doc_server_demo_date"
|
||||
DOC_SERVER_DISABLE_CERTIFICATE = "eurooffice_connector.doc_server_disable_certificate"
|
||||
SAME_TAB = "eurooffice_connector.same_tab"
|
||||
|
||||
INTERNAL_JWT_SECRET = "eurooffice_connector.internal_jwt_secret"
|
||||
|
|
@ -0,0 +1,130 @@
|
|||
#
|
||||
# (c) Copyright Ascensio System SIA 2024
|
||||
#
|
||||
|
||||
import uuid
|
||||
from datetime import date
|
||||
|
||||
from odoo.addons.eurooffice_odoo.utils import config_constants
|
||||
|
||||
|
||||
def get_base_or_odoo_url(env):
|
||||
url = env["ir.config_parameter"].sudo().get_param(config_constants.DOC_SERVER_ODOO_URL)
|
||||
return fix_url(url or env["ir.config_parameter"].sudo().get_param("web.base.url"))
|
||||
|
||||
|
||||
def get_doc_server_public_url(env):
|
||||
url = env["ir.config_parameter"].sudo().get_param(config_constants.DOC_SERVER_PUBLIC_URL)
|
||||
if not url:
|
||||
url = "http://documentserver/"
|
||||
return fix_url(url)
|
||||
|
||||
|
||||
def get_doc_server_inner_url(env):
|
||||
url = env["ir.config_parameter"].sudo().get_param(config_constants.DOC_SERVER_INNER_URL)
|
||||
return fix_url(url or get_doc_server_public_url(env))
|
||||
|
||||
|
||||
def get_jwt_header(env):
|
||||
header = env["ir.config_parameter"].sudo().get_param(config_constants.DOC_SERVER_JWT_HEADER)
|
||||
if not header:
|
||||
header = "Authorization"
|
||||
return header
|
||||
|
||||
|
||||
def get_jwt_secret(env):
|
||||
return env["ir.config_parameter"].sudo().get_param(config_constants.DOC_SERVER_JWT_SECRET)
|
||||
|
||||
|
||||
def get_internal_jwt_secret(env):
|
||||
secret = env["ir.config_parameter"].sudo().get_param(config_constants.INTERNAL_JWT_SECRET)
|
||||
if not secret:
|
||||
secret = uuid.uuid4().hex
|
||||
env["ir.config_parameter"].sudo().set_param(config_constants.INTERNAL_JWT_SECRET, secret)
|
||||
env.cr.commit()
|
||||
|
||||
return secret
|
||||
|
||||
|
||||
def get_demo(env):
|
||||
return env["ir.config_parameter"].sudo().get_param(config_constants.DOC_SERVER_DEMO)
|
||||
|
||||
|
||||
def get_demo_date(env):
|
||||
return env["ir.config_parameter"].sudo().get_param(config_constants.DOC_SERVER_DEMO_DATE)
|
||||
|
||||
|
||||
def get_same_tab(env):
|
||||
return env["ir.config_parameter"].sudo().get_param(config_constants.SAME_TAB)
|
||||
|
||||
|
||||
def get_certificate_verify_disabled(env):
|
||||
return env["ir.config_parameter"].sudo().get_param(config_constants.DOC_SERVER_DISABLE_CERTIFICATE)
|
||||
|
||||
|
||||
def set_doc_server_public_url(env, url):
|
||||
if not url:
|
||||
url = "http://documentserver/"
|
||||
env["ir.config_parameter"].sudo().set_param(config_constants.DOC_SERVER_PUBLIC_URL, fix_url(url))
|
||||
|
||||
|
||||
def set_doc_server_odoo_url(env, url):
|
||||
env["ir.config_parameter"].sudo().set_param(config_constants.DOC_SERVER_ODOO_URL, fix_url(url))
|
||||
|
||||
|
||||
def set_doc_server_inner_url(env, url):
|
||||
env["ir.config_parameter"].sudo().set_param(config_constants.DOC_SERVER_INNER_URL, fix_url(url))
|
||||
|
||||
|
||||
def set_jwt_header(env, header):
|
||||
env["ir.config_parameter"].sudo().set_param(config_constants.DOC_SERVER_JWT_HEADER, header)
|
||||
|
||||
|
||||
def set_jwt_secret(env, secret):
|
||||
env["ir.config_parameter"].sudo().set_param(config_constants.DOC_SERVER_JWT_SECRET, secret)
|
||||
|
||||
|
||||
def set_demo(env, param):
|
||||
demo = get_demo(env)
|
||||
demo_date = get_demo_date(env)
|
||||
if not demo_date:
|
||||
set_demo_date(env)
|
||||
if param:
|
||||
set_doc_server_public_url(env, "https://onlinedocs.docs.eurooffice.com/")
|
||||
set_doc_server_odoo_url(env, "")
|
||||
set_doc_server_inner_url(env, "")
|
||||
set_jwt_header(env, "AuthorizationJWT")
|
||||
set_jwt_secret(env, "sn2puSUF7muF5Jas")
|
||||
elif demo and not param:
|
||||
set_doc_server_public_url(env, "http://documentserver/")
|
||||
set_doc_server_odoo_url(env, "")
|
||||
set_doc_server_inner_url(env, "")
|
||||
set_jwt_header(env, "Authorization")
|
||||
set_jwt_secret(env, "")
|
||||
env["ir.config_parameter"].sudo().set_param(config_constants.DOC_SERVER_DEMO, param)
|
||||
|
||||
|
||||
def set_demo_date(env):
|
||||
demo_date = date.today()
|
||||
env["ir.config_parameter"].sudo().set_param(config_constants.DOC_SERVER_DEMO_DATE, demo_date)
|
||||
|
||||
|
||||
def set_same_tab(env, param):
|
||||
env["ir.config_parameter"].sudo().set_param(config_constants.SAME_TAB, param)
|
||||
|
||||
|
||||
def set_certificate_verify_disabled(env, param):
|
||||
env["ir.config_parameter"].sudo().set_param(config_constants.DOC_SERVER_DISABLE_CERTIFICATE, param)
|
||||
|
||||
|
||||
def fix_url(url):
|
||||
if url:
|
||||
return fix_end_slash(fix_proto(url))
|
||||
|
||||
|
||||
def fix_proto(url):
|
||||
return url if url.startswith("http") else ("http://" + url)
|
||||
|
||||
|
||||
def fix_end_slash(url):
|
||||
return url if url.endswith("/") else (url + "/")
|
||||
|
|
@ -0,0 +1,138 @@
|
|||
#
|
||||
# (c) Copyright Ascensio System SIA 2024
|
||||
#
|
||||
|
||||
import os
|
||||
|
||||
from odoo.addons.eurooffice_odoo.utils import format_utils
|
||||
|
||||
|
||||
def get_file_title_without_ext(name):
|
||||
ind = name.rfind(".")
|
||||
return name[:ind]
|
||||
|
||||
|
||||
def get_file_name_without_ext(name):
|
||||
ind = name.rfind(".")
|
||||
return name[:ind]
|
||||
|
||||
|
||||
def get_file_ext(name):
|
||||
return name[name.rfind(".") + 1 :].lower()
|
||||
|
||||
|
||||
def get_file_type(context):
|
||||
for supported_format in format_utils.get_supported_formats():
|
||||
if supported_format.name == get_file_ext(context):
|
||||
return supported_format.type
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def can_view(context):
|
||||
for supported_format in format_utils.get_supported_formats():
|
||||
if supported_format.name == get_file_ext(context):
|
||||
if "view" in supported_format.actions:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def can_edit(context):
|
||||
for supported_format in format_utils.get_supported_formats():
|
||||
if supported_format.name == get_file_ext(context):
|
||||
if "edit" in supported_format.actions:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def can_fill_form(context):
|
||||
for supported_format in format_utils.get_supported_formats():
|
||||
if supported_format.name == get_file_ext(context):
|
||||
if "fill" in supported_format.actions:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def get_mime_by_ext(ext):
|
||||
if ext == "docx":
|
||||
return "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
|
||||
if ext == "xlsx":
|
||||
return "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
|
||||
if ext == "pptx":
|
||||
return "application/vnd.openxmlformats-officedocument.presentationml.presentation"
|
||||
if ext == "pdf":
|
||||
return "application/pdf"
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def get_default_file_template(lang, ext):
|
||||
locale_path = {
|
||||
"az": "az-Latn-AZ",
|
||||
"bg": "bg-BG",
|
||||
"cs": "cs-CZ",
|
||||
"de": "de-DE",
|
||||
"default": "default",
|
||||
"el": "el-GR",
|
||||
"en-gb": "en-GB",
|
||||
"en": "en-US",
|
||||
"es": "es-ES",
|
||||
"eu": "eu-ES",
|
||||
"fi": "fi-FI",
|
||||
"fr": "fr-FR",
|
||||
"gl": "gl-ES",
|
||||
"he": "he-IL",
|
||||
"it": "it-IT",
|
||||
"ja": "ja-JP",
|
||||
"ko": "ko-KR",
|
||||
"lv": "lv-LV",
|
||||
"nb": "nb-NO",
|
||||
"nl": "nl-NL",
|
||||
"pl": "pl-PL",
|
||||
"pt-br": "pt-BR",
|
||||
"pt": "pt-PT",
|
||||
"ru": "ru-RU",
|
||||
"sk": "sk-SK",
|
||||
"sv": "sv-SE",
|
||||
"tr": "tr-TR",
|
||||
"uk": "uk-UA",
|
||||
"vi": "vi-VN",
|
||||
"zh-CN": "zh-CN",
|
||||
"zh-TW": "zh-TW",
|
||||
"ca": "ca-ES",
|
||||
"da": "da-DK",
|
||||
"hu": "hu-HU",
|
||||
"id": "id-ID",
|
||||
"ro": "ro-RO",
|
||||
}
|
||||
|
||||
lang = lang.replace("_", "-")
|
||||
|
||||
locale = locale_path.get(lang)
|
||||
if locale is None:
|
||||
lang = lang.split("-")[0]
|
||||
locale = locale_path.get(lang)
|
||||
if locale is None:
|
||||
locale = locale_path.get("default")
|
||||
|
||||
file = open(
|
||||
os.path.join(
|
||||
os.path.abspath(os.path.dirname(__file__)),
|
||||
"..",
|
||||
"static",
|
||||
"assets",
|
||||
"document_templates",
|
||||
locale,
|
||||
"new." + ext,
|
||||
),
|
||||
"rb",
|
||||
)
|
||||
|
||||
try:
|
||||
file_data = file.read()
|
||||
return file_data
|
||||
finally:
|
||||
file.close()
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
#
|
||||
# (c) Copyright Ascensio System SIA 2024
|
||||
#
|
||||
|
||||
import json
|
||||
import os
|
||||
|
||||
|
||||
class Format:
|
||||
def __init__(self, name, type, actions=None, convert=None, mime=None):
|
||||
if actions is None:
|
||||
actions = []
|
||||
if convert is None:
|
||||
convert = []
|
||||
if mime is None:
|
||||
mime = []
|
||||
self.name = name
|
||||
self.type = type
|
||||
self.actions = actions
|
||||
self.convert = convert
|
||||
self.mime = mime
|
||||
|
||||
|
||||
def get_supported_formats():
|
||||
file_path = os.path.join(
|
||||
os.path.dirname(__file__), "..", "static", "assets", "document_formats", "eurooffice-docs-formats.json"
|
||||
)
|
||||
|
||||
with open(file_path, encoding="utf-8") as f:
|
||||
data = json.load(f)
|
||||
|
||||
formats = []
|
||||
for item in data:
|
||||
n = item["name"]
|
||||
t = item["type"]
|
||||
a = item.get("actions", [])
|
||||
c = item.get("convert", [])
|
||||
m = item.get("mime", [])
|
||||
|
||||
formats.append(Format(n, t, a, c, m))
|
||||
|
||||
return formats
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
#
|
||||
# (c) Copyright Ascensio System SIA 2024
|
||||
#
|
||||
|
||||
import datetime
|
||||
|
||||
import jwt
|
||||
|
||||
from odoo.addons.eurooffice_odoo.utils import config_utils
|
||||
|
||||
|
||||
def is_jwt_enabled(env):
|
||||
return bool(config_utils.get_jwt_secret(env))
|
||||
|
||||
|
||||
def encode_payload(env, payload, secret=None):
|
||||
if secret is None:
|
||||
secret = config_utils.get_jwt_secret(env)
|
||||
now = datetime.datetime.utcnow()
|
||||
exp = now + datetime.timedelta(hours=24)
|
||||
payload["iat"] = int(now.timestamp())
|
||||
payload["exp"] = int(exp.timestamp())
|
||||
return jwt.encode(payload, secret, algorithm="HS256")
|
||||
|
||||
|
||||
def decode_token(env, token, secret=None):
|
||||
if secret is None:
|
||||
secret = config_utils.get_jwt_secret(env)
|
||||
return jwt.decode(token, secret, algorithms=["HS256"])
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
import logging
|
||||
|
||||
from odoo.addons.eurooffice_odoo.utils import config_utils
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def replace_public_url_to_internal(env, url):
|
||||
public_url = config_utils.get_doc_server_public_url(env)
|
||||
inner_url = config_utils.get_doc_server_inner_url(env)
|
||||
if inner_url and inner_url != public_url:
|
||||
_logger.info("Replace public url %s to internal url %s", public_url, inner_url)
|
||||
url = url.replace(public_url, inner_url)
|
||||
return url
|
||||
|
|
@ -0,0 +1,183 @@
|
|||
import json
|
||||
import os
|
||||
import re
|
||||
import time
|
||||
from urllib.request import urlopen
|
||||
|
||||
import requests
|
||||
|
||||
from odoo.exceptions import ValidationError
|
||||
|
||||
from odoo.addons.eurooffice_odoo.utils import jwt_utils
|
||||
|
||||
|
||||
def valid_url(url):
|
||||
if not url:
|
||||
return True
|
||||
# pylint: disable=anomalous-backslash-in-string
|
||||
pattern = "^(https?:\/\/)?[\w-]{1,32}(\.[\w-]{1,32})*[\/\w-]*(:[\d]{1,5}\/?)?$"
|
||||
# pylint: enable=anomalous-backslash-in-string
|
||||
if re.findall(pattern, url):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def settings_validation(self):
|
||||
base_url = self.doc_server_odoo_url
|
||||
public_url = self.doc_server_public_url
|
||||
inner_url = self.doc_server_inner_url
|
||||
jwt_secret = self.doc_server_jwt_secret
|
||||
jwt_header = self.doc_server_jwt_header
|
||||
disable_certificate = self.doc_server_disable_certificate
|
||||
demo = self.doc_server_demo
|
||||
|
||||
url = public_url
|
||||
if inner_url and inner_url != public_url:
|
||||
url = inner_url
|
||||
|
||||
check_mixed_content(base_url, url, demo)
|
||||
check_doc_serv_url(url, demo, disable_certificate)
|
||||
check_doc_serv_command_service(self.env, url, jwt_secret, jwt_header, disable_certificate, demo)
|
||||
check_doc_serv_convert_service(self.env, url, base_url, jwt_secret, jwt_header, disable_certificate, demo)
|
||||
|
||||
|
||||
def check_mixed_content(base_url, url, demo):
|
||||
if base_url.startswith("https") and not url.startswith("https"):
|
||||
get_message_error("Mixed Active Content is not allowed. HTTPS address for Document Server is required.", demo)
|
||||
|
||||
|
||||
def check_doc_serv_url(url, demo, disable_certificate):
|
||||
try:
|
||||
url = os.path.join(url, "healthcheck")
|
||||
|
||||
context = None
|
||||
if disable_certificate and url.startswith("https://"):
|
||||
import ssl
|
||||
|
||||
context = ssl._create_unverified_context()
|
||||
|
||||
response = urlopen(url, timeout=30, context=context)
|
||||
|
||||
healthcheck = response.read()
|
||||
|
||||
if not healthcheck:
|
||||
get_message_error(os.path.join(url, "healthcheck") + " returned false.", demo)
|
||||
|
||||
except ValidationError as e:
|
||||
get_message_error(str(e), demo)
|
||||
except Exception:
|
||||
get_message_error("Euro-Office cannot be reached", demo)
|
||||
|
||||
|
||||
def check_doc_serv_command_service(env, url, jwt_secret, jwt_header, disable_certificate, demo):
|
||||
try:
|
||||
headers = {"Content-Type": "application/json"}
|
||||
body_json = {"c": "version"}
|
||||
|
||||
if jwt_secret is not None and jwt_secret is not False and jwt_secret != "":
|
||||
payload = {"payload": body_json}
|
||||
|
||||
header_token = jwt_utils.encode_payload(env, payload, jwt_secret)
|
||||
headers[jwt_header] = "Bearer " + header_token
|
||||
|
||||
token = jwt_utils.encode_payload(env, body_json, jwt_secret)
|
||||
body_json["token"] = token
|
||||
|
||||
response = requests.post(
|
||||
os.path.join(url, "coauthoring/CommandService.ashx"),
|
||||
verify=not disable_certificate,
|
||||
timeout=60,
|
||||
data=json.dumps(body_json),
|
||||
headers=headers,
|
||||
)
|
||||
|
||||
if response.json()["error"] == 6:
|
||||
get_message_error("Authorization error", demo)
|
||||
|
||||
if response.json()["error"] != 0:
|
||||
get_message_error(
|
||||
os.path.join(url, "coauthoring/CommandService.ashx")
|
||||
+ " returned error: "
|
||||
+ str(response.json()["error"]),
|
||||
demo,
|
||||
)
|
||||
|
||||
except ValidationError as e:
|
||||
get_message_error(str(e), demo)
|
||||
except Exception:
|
||||
get_message_error("Error when trying to check CommandService", demo)
|
||||
|
||||
|
||||
def check_doc_serv_convert_service(env, url, base_url, jwt_secret, jwt_header, disable_certificate, demo):
|
||||
file_url = os.path.join(base_url, "eurooffice/file/content/test.txt")
|
||||
|
||||
result = convert(env, file_url, url, jwt_secret, jwt_header, disable_certificate)
|
||||
|
||||
if isinstance(result, str):
|
||||
return get_message_error(result, demo)
|
||||
|
||||
|
||||
def convert(env, file_url, url, jwt_secret, jwt_header, disable_certificate):
|
||||
body_json = {
|
||||
"key": int(time.time()),
|
||||
"url": file_url,
|
||||
"filetype": "txt",
|
||||
"outputtype": "txt",
|
||||
}
|
||||
|
||||
headers = {
|
||||
"Content-Type": "application/json",
|
||||
"Accept": "application/json",
|
||||
}
|
||||
|
||||
if bool(jwt_secret):
|
||||
payload = {"payload": body_json}
|
||||
header_token = jwt_utils.encode_payload(env, payload, jwt_secret)
|
||||
headers[jwt_header] = "Bearer " + header_token
|
||||
token = jwt_utils.encode_payload(env, body_json, jwt_secret)
|
||||
body_json["token"] = token
|
||||
|
||||
try:
|
||||
response = requests.post(
|
||||
os.path.join(url, "ConvertService.ashx"),
|
||||
verify=not disable_certificate,
|
||||
timeout=60,
|
||||
data=json.dumps(body_json),
|
||||
headers=headers,
|
||||
)
|
||||
|
||||
if response.status_code == 200:
|
||||
response_json = response.json()
|
||||
if "error" in response_json:
|
||||
return get_conversion_error_message(response_json.get("error"))
|
||||
else:
|
||||
return f"Document conversion service returned status {response.status_code}"
|
||||
|
||||
except Exception:
|
||||
return "Document conversion service cannot be reached"
|
||||
|
||||
|
||||
def get_message_error(message, demo):
|
||||
if demo:
|
||||
raise ValidationError(f"Error connecting to demo server: {message}")
|
||||
else:
|
||||
raise ValidationError(message)
|
||||
|
||||
|
||||
def get_conversion_error_message(errorCode):
|
||||
errorDictionary = {
|
||||
-1: "Unknown error",
|
||||
-2: "Conversion timeout error",
|
||||
-3: "Conversion error",
|
||||
-4: "Error while downloading the document file to be converted",
|
||||
-5: "Incorrect password",
|
||||
-6: "Error while accessing the conversion result database",
|
||||
-7: "Input error",
|
||||
-8: "Invalid token",
|
||||
}
|
||||
|
||||
try:
|
||||
return errorDictionary[errorCode]
|
||||
|
||||
except Exception:
|
||||
return "Undefined error code"
|
||||
Loading…
Add table
Add a link
Reference in a new issue