diff --git a/dms_libreoffice_preview/README.rst b/dms_libreoffice_preview/README.rst new file mode 100644 index 000000000..7ef815900 --- /dev/null +++ b/dms_libreoffice_preview/README.rst @@ -0,0 +1,138 @@ +.. image:: https://odoo-community.org/readme-banner-image + :target: https://odoo-community.org/get-involved?utm_source=readme + :alt: Odoo Community Association + +========================= +DMS — LibreOffice Preview +========================= + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:81baba4c980b32e56a3e208ba841264571d907763fa987650db3d700e2592292 + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png + :target: https://odoo-community.org/page/development-status + :alt: Beta +.. |badge2| image:: https://img.shields.io/badge/license-LGPL--3-blue.png + :target: http://www.gnu.org/licenses/lgpl-3.0-standalone.html + :alt: License: LGPL-3 +.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fdms-lightgray.png?logo=github + :target: https://github.com/OCA/dms/tree/19.0/dms_libreoffice_preview + :alt: OCA/dms +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/dms-19-0/dms-19-0-dms_libreoffice_preview + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png + :target: https://runboat.odoo-community.org/builds?repo=OCA/dms&target_branch=19.0 + :alt: Try me on Runboat + +|badge1| |badge2| |badge3| |badge4| |badge5| + +Adds in-browser preview of office file formats (Word, Excel, PowerPoint, +OpenDocument, RTF) to OCA DMS by converting them to PDF on the server +with a headless LibreOffice subprocess. The converted PDF is cached as a +child ``ir.attachment`` of the source ``dms.file`` and served via the +existing side-pane preview registry (``dms.preview_handlers``), so the +browser's native PDF viewer renders the result with no new UI code. + +Without this module, office files in DMS fall back to a download-only +card. Install this module on any deployment where LibreOffice can be +installed system-wide and you don't need full in-browser editing (use +``dms_onlyoffice`` for that). + +**Table of contents** + +.. contents:: + :local: + +Installation +============ + +System dependencies +------------------- + +LibreOffice must be installed on the Odoo server. On Debian / Ubuntu: + +:: + + apt-get install libreoffice fonts-noto fonts-liberation + +The ``fonts-noto`` and ``fonts-liberation`` packages are recommended +even if your container already has a font set — headless LibreOffice +falls back to ugly substitutes when common fonts are missing, which +produces unreadable PDFs for typical office documents. + +Once LibreOffice is on the ``PATH``, install this module as usual. The +preview is lazy: nothing converts until the first time a user opens an +office file in the DMS side-pane. Subsequent opens of the same file +(same ``write_date``) hit the ``ir.attachment`` cache. + +Usage +===== + +No configuration required. With LibreOffice on the server and this +module installed, opening any office file (``.doc{x}``, ``.odt``, +``.xls{x}``, ``.ods``, ``.ppt{x}``, ``.odp``, ``.rtf``) from the DMS +kanban or list view shows the rendered PDF in the side-pane preview. + +The first open takes 1–5 seconds (LibreOffice conversion); subsequent +opens hit the cached attachment and feel instant. If the source file +changes (new content, new name, etc.), the cache invalidates and the +next open triggers a fresh conversion. + +If LibreOffice fails to convert a file (corrupt source, unusual format +variation), the side-pane shows a brief error message. The original file +is always downloadable via the side-pane toolbar's Download button +regardless of preview success. + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues `_. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us to smash it by providing a detailed and welcomed +`feedback `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +------- + +* ledoent + +Contributors +------------ + +- Don Kendall + +Maintainers +----------- + +This module is maintained by the OCA. + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use. + +.. |maintainer-dnplkndll| image:: https://github.com/dnplkndll.png?size=40px + :target: https://github.com/dnplkndll + :alt: dnplkndll + +Current `maintainer `__: + +|maintainer-dnplkndll| + +This module is part of the `OCA/dms `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/dms_libreoffice_preview/__init__.py b/dms_libreoffice_preview/__init__.py new file mode 100644 index 000000000..91c5580fe --- /dev/null +++ b/dms_libreoffice_preview/__init__.py @@ -0,0 +1,2 @@ +from . import controllers +from . import models diff --git a/dms_libreoffice_preview/__manifest__.py b/dms_libreoffice_preview/__manifest__.py new file mode 100644 index 000000000..b64ecbdde --- /dev/null +++ b/dms_libreoffice_preview/__manifest__.py @@ -0,0 +1,30 @@ +# Copyright 2026 ledoent — Don Kendall +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). + +{ + "name": "DMS — LibreOffice Preview", + "summary": ( + "In-browser preview of office files in DMS via " + "server-side LibreOffice→PDF conversion" + ), + "version": "19.0.1.0.0", + "category": "Document Management", + "license": "LGPL-3", + "website": "https://github.com/OCA/dms", + "author": "ledoent, Odoo Community Association (OCA)", + "maintainers": ["dnplkndll"], + "depends": ["dms", "dms_preview_pane"], + "external_dependencies": { + "deb": ["libreoffice", "fonts-noto", "fonts-liberation"], + }, + "assets": { + "web.assets_backend": [ + "dms_libreoffice_preview/static/src/js/libreoffice_preview.esm.js", + "dms_libreoffice_preview/static/src/js/libreoffice_preview.xml", + ], + "web.assets_unit_tests": [ + "dms_libreoffice_preview/static/tests/**/*.test.js", + ], + }, + "installable": True, +} diff --git a/dms_libreoffice_preview/controllers/__init__.py b/dms_libreoffice_preview/controllers/__init__.py new file mode 100644 index 000000000..12a7e529b --- /dev/null +++ b/dms_libreoffice_preview/controllers/__init__.py @@ -0,0 +1 @@ +from . import main diff --git a/dms_libreoffice_preview/controllers/main.py b/dms_libreoffice_preview/controllers/main.py new file mode 100644 index 000000000..3d64dd3c7 --- /dev/null +++ b/dms_libreoffice_preview/controllers/main.py @@ -0,0 +1,51 @@ +# Copyright 2026 ledoent — Don Kendall +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). + +from odoo import http +from odoo.exceptions import AccessError, UserError +from odoo.http import request + + +class DmsLibreofficePreviewController(http.Controller): + @http.route( + "/dms/file//libreoffice_preview", + type="http", + auth="user", + methods=["GET"], + # Not readonly: cache-miss path writes a new ir.attachment via + # _ensure_libreoffice_preview(). Marking readonly=True triggers + # Odoo 19's "retry with r/w cursor" warning which checklog-odoo + # promotes to a build error. + ) + def libreoffice_preview(self, file_id, **kwargs): + """Serve a cached or freshly-converted PDF preview inline. + + The `?v=` query parameter is not validated server-side + — it exists purely as a cache-buster for the browser (the iframe + URL changes when `dms.file.write_date` changes, forcing a refetch + instead of pulling a stale render from the HTTP cache). Cache + validity on the server is owned by `_libreoffice_preview_attachment`. + """ + dms_file = request.env["dms.file"].browse(file_id).exists() + if not dms_file: + raise request.not_found() + try: + dms_file.check_access("read") + except AccessError as e: + raise request.not_found() from e + if not dms_file._libreoffice_preview_supported(): + raise request.not_found() + try: + attachment = dms_file.sudo()._ensure_libreoffice_preview() + except UserError as e: + # Conversion errors are surfaced as 502 with the user-facing + # message — the side-pane handler can render this as an empty + # state via the existing error path. + return request.make_response( + str(e), status=502, headers=[("Content-Type", "text/plain")] + ) + return ( + request.env["ir.binary"] + ._get_stream_from(attachment, "raw") + .get_response(as_attachment=False) + ) diff --git a/dms_libreoffice_preview/models/__init__.py b/dms_libreoffice_preview/models/__init__.py new file mode 100644 index 000000000..e1448f4ab --- /dev/null +++ b/dms_libreoffice_preview/models/__init__.py @@ -0,0 +1 @@ +from . import dms_file diff --git a/dms_libreoffice_preview/models/dms_file.py b/dms_libreoffice_preview/models/dms_file.py new file mode 100644 index 000000000..4f14be281 --- /dev/null +++ b/dms_libreoffice_preview/models/dms_file.py @@ -0,0 +1,200 @@ +# Copyright 2026 ledoent — Don Kendall +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). + +import base64 +import logging +import os +import subprocess +import tempfile + +from odoo import models +from odoo.exceptions import UserError + +_logger = logging.getLogger(__name__) + +# Mimetypes this module knows how to convert. Mirrors the OFFICE_MIMETYPES +# set in dms/static/src/js/components/preview/handlers.esm.js so the JS +# handler's match() predicate stays in lockstep with what the server can +# actually deliver. If you add a mimetype here, add it there too. +OFFICE_MIMETYPES = frozenset( + { + "application/msword", + "application/vnd.openxmlformats-officedocument.wordprocessingml.document", + "application/vnd.oasis.opendocument.text", + "application/rtf", + # libmagic detects RTF as text/rtf, not application/rtf — without + # this, RTF files match TextPreview (text/*) at score 0 and render + # as raw RTF source markup instead of routing through soffice. + "text/rtf", + "application/vnd.ms-excel", + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", + "application/vnd.oasis.opendocument.spreadsheet", + "application/vnd.ms-powerpoint", + "application/vnd.openxmlformats-officedocument.presentationml.presentation", + "application/vnd.oasis.opendocument.presentation", + } +) + +# Office extensions, used as a fallback when the stored mimetype is generic. +# libmagic records several office docs (notably the OCA demo's Document_04.rtf) +# as text/plain or application/octet-stream; the base dms preview pane already +# remaps those to an office mimetype off the extension before routing here, so +# the support check has to honour the extension too — otherwise the JS handler +# points the iframe at our route and the server 404s it. Keep aligned with +# OFFICE_MIMETYPES above. +_OFFICE_EXTENSIONS = frozenset( + {"doc", "docx", "odt", "rtf", "xls", "xlsx", "ods", "ppt", "pptx", "odp"} +) + +# Prefix on ir.attachment.description that we use as a cache key. The value +# after the colon is the dms.file.write_date at conversion time; a mismatch +# between the cached value and the current write_date means the source has +# been updated and we need to re-convert. +_CACHE_DESCRIPTION_PREFIX = "libreoffice_preview:" + +# Hard cap on soffice runtime. Most real documents convert in <5s; a 60s +# ceiling stops a single hung file from blocking an Odoo worker indefinitely. +_CONVERT_TIMEOUT = 60 + + +class DmsFile(models.Model): + _inherit = "dms.file" + + def _libreoffice_preview_supported(self): + """True when we can hand this file to soffice for a PDF preview. + + Stored mimetype first, then an extension fallback: a generic stored + type (libmagic logs some office docs as text/plain) would 404 the + route even though the JS handler routed here off the extension. The + fallback keeps the two in lockstep so mis-typed files still preview. + """ + self.ensure_one() + if self.mimetype in OFFICE_MIMETYPES: + return True + return (self.extension or "").lstrip(".").lower() in _OFFICE_EXTENSIONS + + def _libreoffice_preview_attachment(self): + """Return the cached preview ir.attachment, or an empty recordset. + + Cache validity is keyed on `dms.file.checksum` (SHA1 of content, + already maintained by base dms). This is intentionally NOT keyed + on `write_date` — renaming a file should not force re-conversion, + only content edits should. + """ + self.ensure_one() + if not self.checksum: + return self.env["ir.attachment"] + expected = _CACHE_DESCRIPTION_PREFIX + self.checksum + return self.env["ir.attachment"].search( + [ + ("res_model", "=", "dms.file"), + ("res_id", "=", self.id), + ("description", "=", expected), + ("mimetype", "=", "application/pdf"), + ], + limit=1, + ) + + def _ensure_libreoffice_preview(self): + """Return a cached or freshly-converted PDF ir.attachment. + + Raises `UserError` if the source mimetype isn't in our convertible + set — callers are expected to gate on `_libreoffice_preview_supported` + before invoking this. Bypasses ACL checks intentionally: it is the + controller's job to enforce access, this method just does the work. + """ + self.ensure_one() + if not self._libreoffice_preview_supported(): + raise UserError( + self.env._( + "%s is not an office format this module can convert.", self.name + ) + ) + + cached = self._libreoffice_preview_attachment() + if cached: + return cached + + pdf_bytes = self._convert_with_libreoffice() + return self.env["ir.attachment"].create( + { + "name": (self.name or "file") + ".preview.pdf", + "res_model": "dms.file", + "res_id": self.id, + "mimetype": "application/pdf", + "datas": base64.b64encode(pdf_bytes), + "description": _CACHE_DESCRIPTION_PREFIX + (self.checksum or ""), + } + ) + + def _convert_with_libreoffice(self): + """Run `soffice --headless --convert-to pdf` and return PDF bytes. + + Each call gets its own temp directory so concurrent conversions + don't collide on `.~lock.#` markers. Source extension is + preserved on the temp file because LibreOffice infers the input + format from the extension (not from sniffing the content). + """ + self.ensure_one() + source_bytes = base64.b64decode(self.content or b"") + if not source_bytes: + raise UserError(self.env._("%s has no content to convert.", self.name)) + + suffix = "." + (self.extension or "").lstrip(".") if self.extension else "" + with tempfile.TemporaryDirectory(prefix="dms_libreoffice_") as workdir: + src_path = os.path.join(workdir, "source" + suffix) + with open(src_path, "wb") as fh: + fh.write(source_bytes) + try: + subprocess.run( + [ + "soffice", + "--headless", + "--convert-to", + "pdf", + "--outdir", + workdir, + src_path, + ], + capture_output=True, + check=True, + timeout=_CONVERT_TIMEOUT, + ) + except FileNotFoundError as e: + # `soffice` not on $PATH — apt packages were not installed. + raise UserError( + self.env._( + "LibreOffice (soffice) is not installed on the server. " + "Install libreoffice + fonts-noto + fonts-liberation, or " + "remove the dms_libreoffice_preview module." + ) + ) from e + except subprocess.TimeoutExpired as e: + raise UserError( + self.env._( + "LibreOffice conversion of %(name)s timed out after " + "%(timeout)ss. Source may be malformed or unusually large.", + name=self.name, + timeout=_CONVERT_TIMEOUT, + ) + ) from e + except subprocess.CalledProcessError as e: + _logger.warning( + "soffice failed for dms.file id=%s: stderr=%s", + self.id, + (e.stderr or b"").decode("utf-8", errors="replace")[:500], + ) + raise UserError( + self.env._( + "LibreOffice could not convert %s. Source may be corrupted.", + self.name, + ) + ) from e + + pdf_path = os.path.join(workdir, "source.pdf") + if not os.path.exists(pdf_path): + raise UserError( + self.env._("LibreOffice produced no output for %s.", self.name) + ) + with open(pdf_path, "rb") as fh: + return fh.read() diff --git a/dms_libreoffice_preview/pyproject.toml b/dms_libreoffice_preview/pyproject.toml new file mode 100644 index 000000000..4231d0ccc --- /dev/null +++ b/dms_libreoffice_preview/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["whool"] +build-backend = "whool.buildapi" diff --git a/dms_libreoffice_preview/readme/CONTRIBUTORS.md b/dms_libreoffice_preview/readme/CONTRIBUTORS.md new file mode 100644 index 000000000..eb61da848 --- /dev/null +++ b/dms_libreoffice_preview/readme/CONTRIBUTORS.md @@ -0,0 +1 @@ +- Don Kendall \<\> diff --git a/dms_libreoffice_preview/readme/DESCRIPTION.md b/dms_libreoffice_preview/readme/DESCRIPTION.md new file mode 100644 index 000000000..b240cf89b --- /dev/null +++ b/dms_libreoffice_preview/readme/DESCRIPTION.md @@ -0,0 +1,11 @@ +Adds in-browser preview of office file formats (Word, Excel, PowerPoint, +OpenDocument, RTF) to OCA DMS by converting them to PDF on the server +with a headless LibreOffice subprocess. The converted PDF is cached as +a child `ir.attachment` of the source `dms.file` and served via the +existing side-pane preview registry (`dms.preview_handlers`), so the +browser's native PDF viewer renders the result with no new UI code. + +Without this module, office files in DMS fall back to a download-only +card. Install this module on any deployment where LibreOffice can be +installed system-wide and you don't need full in-browser editing (use +`dms_onlyoffice` for that). diff --git a/dms_libreoffice_preview/readme/INSTALL.md b/dms_libreoffice_preview/readme/INSTALL.md new file mode 100644 index 000000000..a0db72044 --- /dev/null +++ b/dms_libreoffice_preview/readme/INSTALL.md @@ -0,0 +1,17 @@ +## System dependencies + +LibreOffice must be installed on the Odoo server. On Debian / Ubuntu: + +``` +apt-get install libreoffice fonts-noto fonts-liberation +``` + +The `fonts-noto` and `fonts-liberation` packages are recommended even if +your container already has a font set — headless LibreOffice falls back +to ugly substitutes when common fonts are missing, which produces +unreadable PDFs for typical office documents. + +Once LibreOffice is on the `PATH`, install this module as usual. The +preview is lazy: nothing converts until the first time a user opens an +office file in the DMS side-pane. Subsequent opens of the same file +(same `write_date`) hit the `ir.attachment` cache. diff --git a/dms_libreoffice_preview/readme/USAGE.md b/dms_libreoffice_preview/readme/USAGE.md new file mode 100644 index 000000000..4994f43ab --- /dev/null +++ b/dms_libreoffice_preview/readme/USAGE.md @@ -0,0 +1,14 @@ +No configuration required. With LibreOffice on the server and this +module installed, opening any office file (`.doc{x}`, `.odt`, `.xls{x}`, +`.ods`, `.ppt{x}`, `.odp`, `.rtf`) from the DMS kanban or list view +shows the rendered PDF in the side-pane preview. + +The first open takes 1–5 seconds (LibreOffice conversion); subsequent +opens hit the cached attachment and feel instant. If the source file +changes (new content, new name, etc.), the cache invalidates and the +next open triggers a fresh conversion. + +If LibreOffice fails to convert a file (corrupt source, unusual format +variation), the side-pane shows a brief error message. The original +file is always downloadable via the side-pane toolbar's Download button +regardless of preview success. diff --git a/dms_libreoffice_preview/static/description/index.html b/dms_libreoffice_preview/static/description/index.html new file mode 100644 index 000000000..1221c1c50 --- /dev/null +++ b/dms_libreoffice_preview/static/description/index.html @@ -0,0 +1,478 @@ + + + + + +README.rst + + + +
+ + + +Odoo Community Association + +
+

DMS — LibreOffice Preview

+ +

Beta License: LGPL-3 OCA/dms Translate me on Weblate Try me on Runboat

+

Adds in-browser preview of office file formats (Word, Excel, PowerPoint, +OpenDocument, RTF) to OCA DMS by converting them to PDF on the server +with a headless LibreOffice subprocess. The converted PDF is cached as a +child ir.attachment of the source dms.file and served via the +existing side-pane preview registry (dms.preview_handlers), so the +browser’s native PDF viewer renders the result with no new UI code.

+

Without this module, office files in DMS fall back to a download-only +card. Install this module on any deployment where LibreOffice can be +installed system-wide and you don’t need full in-browser editing (use +dms_onlyoffice for that).

+

Table of contents

+ +
+

Installation

+
+

System dependencies

+

LibreOffice must be installed on the Odoo server. On Debian / Ubuntu:

+
+apt-get install libreoffice fonts-noto fonts-liberation
+
+

The fonts-noto and fonts-liberation packages are recommended +even if your container already has a font set — headless LibreOffice +falls back to ugly substitutes when common fonts are missing, which +produces unreadable PDFs for typical office documents.

+

Once LibreOffice is on the PATH, install this module as usual. The +preview is lazy: nothing converts until the first time a user opens an +office file in the DMS side-pane. Subsequent opens of the same file +(same write_date) hit the ir.attachment cache.

+
+
+
+

Usage

+

No configuration required. With LibreOffice on the server and this +module installed, opening any office file (.doc{x}, .odt, +.xls{x}, .ods, .ppt{x}, .odp, .rtf) from the DMS +kanban or list view shows the rendered PDF in the side-pane preview.

+

The first open takes 1–5 seconds (LibreOffice conversion); subsequent +opens hit the cached attachment and feel instant. If the source file +changes (new content, new name, etc.), the cache invalidates and the +next open triggers a fresh conversion.

+

If LibreOffice fails to convert a file (corrupt source, unusual format +variation), the side-pane shows a brief error message. The original file +is always downloadable via the side-pane toolbar’s Download button +regardless of preview success.

+
+
+

Bug Tracker

+

Bugs are tracked on GitHub Issues. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us to smash it by providing a detailed and welcomed +feedback.

+

Do not contact contributors directly about support or help with technical issues.

+
+
+

Credits

+
+

Authors

+
    +
  • ledoent
  • +
+
+
+

Contributors

+ +
+
+

Maintainers

+

This module is maintained by the OCA.

+ +Odoo Community Association + +

OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use.

+

Current maintainer:

+

dnplkndll

+

This module is part of the OCA/dms project on GitHub.

+

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

+
+
+
+
+ + diff --git a/dms_libreoffice_preview/static/src/js/libreoffice_preview.esm.js b/dms_libreoffice_preview/static/src/js/libreoffice_preview.esm.js new file mode 100644 index 000000000..4632536f7 --- /dev/null +++ b/dms_libreoffice_preview/static/src/js/libreoffice_preview.esm.js @@ -0,0 +1,55 @@ +// /** ******************************************************************************** +// Copyright 2026 ledoent — Don Kendall +// License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). +// +// Plugs an "office mimetypes → server-converted PDF" handler into the +// existing dms.preview_handlers registry. Score 10 beats the score-0 +// OfficeFallbackPreview (download card) shipped in base dms, while +// leaving room for a future dms_onlyoffice handler at score 20 to win +// when in-browser editing is the goal. +// +// The component reuses the existing PdfPreview iframe class +// (`o_dms_preview__iframe`) so no new SCSS is needed — once the server +// emits the converted PDF, the browser's native PDF viewer handles +// rendering exactly as it does for source-PDF files today. +// **********************************************************************************/ +import {Component} from "@odoo/owl"; +import {previewRegistry} from "@dms_preview_pane/js/components/preview/preview_registry.esm"; + +// Must stay in lockstep with OFFICE_MIMETYPES in +// dms_libreoffice_preview/models/dms_file.py — the JS predicate gates UI +// display, the Python set gates server-side conversion. Drift between +// them produces a clickable office card with a 404 preview. +const OFFICE_MIMETYPES = new Set([ + "application/msword", + "application/vnd.openxmlformats-officedocument.wordprocessingml.document", + "application/vnd.oasis.opendocument.text", + "application/rtf", + "text/rtf", + "application/vnd.ms-excel", + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", + "application/vnd.oasis.opendocument.spreadsheet", + "application/vnd.ms-powerpoint", + "application/vnd.openxmlformats-officedocument.presentationml.presentation", + "application/vnd.oasis.opendocument.presentation", +]); + +export class LibreofficePreview extends Component { + static template = "dms_libreoffice_preview.Preview"; + static props = {file: {type: Object}}; + + get src() { + // Write_date doubles as the cache-bust query param so the iframe + // refetches whenever the source file changes. Server caches on the + // same write_date so two requests for the same generation hit the + // attachment store, not soffice. + const ts = encodeURIComponent(this.props.file.write_date || ""); + return `/dms/file/${this.props.file.id}/libreoffice_preview?v=${ts}`; + } +} + +previewRegistry().add("dms_libreoffice_preview/office", { + component: LibreofficePreview, + match: (mt) => OFFICE_MIMETYPES.has(mt), + score: 10, +}); diff --git a/dms_libreoffice_preview/static/src/js/libreoffice_preview.xml b/dms_libreoffice_preview/static/src/js/libreoffice_preview.xml new file mode 100644 index 000000000..c8b08462f --- /dev/null +++ b/dms_libreoffice_preview/static/src/js/libreoffice_preview.xml @@ -0,0 +1,14 @@ + + + + +