Skip to content

feat!: replace Abiword with LibreOffice and add DOCX export#7539

Open
JohnMcLear wants to merge 3 commits intodevelopfrom
drop-abiword-add-docx
Open

feat!: replace Abiword with LibreOffice and add DOCX export#7539
JohnMcLear wants to merge 3 commits intodevelopfrom
drop-abiword-add-docx

Conversation

@JohnMcLear
Copy link
Copy Markdown
Member

Summary

  • Replaces the Abiword converter backend with LibreOffice (soffice) as the sole advanced import/export converter
  • Adds .docx as an export format; the "Microsoft Word" UI button now downloads DOCX
  • Legacy /export/doc still works for API consumers

Closes #4805.

Rationale

Abiword's DOCX output is experimental and the project is effectively Linux-only (Windows 2.8.6 / macOS 2.4 are abandoned). Most deployments already use soffice via INSTALL_SOFFICE. Dropping Abiword collapses a forked code path, removes an apt dependency, simplifies exportAvailable() / ExportHandler / ImportHandler, and lets us cleanly add DOCX — which soffice produces directly from HTML.

What changed

Removed:

  • src/node/utils/Abiword.ts
  • settings.abiword config + abiwordAvailable() + the Settings.ts fs.exists check
  • INSTALL_ABIWORD arg + abiword abiword-plugin-command apk install in Dockerfile
  • abiword entry in settings.json.template and settings.json.docker
  • abiwordAvailable clientVar in PadMessageHandler and SocketIOMessage
  • #importmessageabiword element in pad.html / ui/pad.html, matching CSS in popup_import_export.css and colibris/.../import-export.css
  • pad.importExport.abiword.innerHTML locale key across 83 locale files
  • Abiword-conditional skip + per-test wording in importexportGetPost.ts
  • /src/bin/abiword.exe from .gitignore
  • Abiword sections in doc/docker.md and doc/docker.adoc

Added:

  • 'docx' in both the export allowlist and the converter-required list in importexport.ts
  • DOCX export tests in src/tests/backend/specs/api/importexportGetPost.ts (exports from scratch, and from imported DOCX)
  • Word button now points at /export/docx

Kept:

  • .doc as a valid export type on the backend (API-compatible)
  • .doc and .docx as valid import types (unchanged)

Breaking change

Deployments currently using "abiword": "/usr/bin/abiword" must switch to "soffice": "/usr/bin/soffice". The INSTALL_ABIWORD=true Docker build arg is removed — use INSTALL_SOFFICE=true.

Test plan

  • pnpm run ts-check passes
  • mocha tests/backend/specs/api/importexportGetPost.ts — 56 passing, 10 pending (soffice-dependent, skipped in local env without LibreOffice), 2 failing (pre-existing on develop: rev test1 is 403 / rev test1 results in 500, unrelated to this change — error body serialization issue in checkValidRev path)
  • mocha tests/backend/specs/export.ts — 1 passing
  • CI with soffice installed should exercise the new DOCX export paths
  • Manual: verify Word button downloads a valid .docx that opens in LibreOffice / Word / Google Docs

🤖 Generated with Claude Code

The Abiword converter is dropped. Abiword's DOCX export is weak and the
project is niche on modern platforms; LibreOffice (soffice) is the
common deployment path and now serves as the sole converter backend.

DOCX is added as an export format and becomes the new target for the
"Microsoft Word" UI button. The /export/doc URL still works for legacy
API consumers.

BREAKING CHANGE: The 'abiword' setting, the INSTALL_ABIWORD Dockerfile
build arg, the abiwordAvailable clientVar, and the
#importmessageabiword UI element (with locale key
pad.importExport.abiword.innerHTML) are removed. Deployments relying on
Abiword must configure 'soffice' instead.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@qodo-free-for-open-source-projects
Copy link
Copy Markdown

Review Summary by Qodo

Replace Abiword with LibreOffice and add DOCX export support

✨ Enhancement 🐞 Bug fix

Grey Divider

Walkthroughs

Description
• Replace Abiword converter with LibreOffice as sole backend
• Add DOCX export format; Word button now downloads DOCX
• Remove Abiword configuration, settings, and UI elements
• Simplify export/import handler logic by eliminating dual-converter paths
• Update documentation and Docker configuration for LibreOffice only
Diagram
flowchart LR
  A["Abiword Converter"] -->|removed| B["LibreOffice Only"]
  C["Export Formats<br/>pdf, doc, txt, html, odt"] -->|add docx| D["Export Formats<br/>pdf, doc, docx, txt, html, odt"]
  E["Word Button<br/>/export/doc"] -->|changed to| F["Word Button<br/>/export/docx"]
  G["Settings Config<br/>abiword + soffice"] -->|simplify to| H["Settings Config<br/>soffice only"]
Loading

Grey Divider

File Changes

1. src/tests/backend/specs/api/importexportGetPost.ts 🧪 Tests +23/-8

Update tests to reference LibreOffice only

src/tests/backend/specs/api/importexportGetPost.ts


2. src/node/utils/Settings.ts ⚙️ Configuration changes +2/-42

Remove abiword setting and abiwordAvailable function

src/node/utils/Settings.ts


3. src/node/hooks/express/importexport.ts ✨ Enhancement +5/-5

Add docx to export types and update error messages

src/node/hooks/express/importexport.ts


View more (101)
4. src/static/js/pad_impexp.ts ✨ Enhancement +3/-5

Update Word export link to docx and remove abiword UI

src/static/js/pad_impexp.ts


5. src/node/handler/ExportHandler.ts 🐞 Bug fix +2/-5

Remove abiword converter logic, use LibreOffice only

src/node/handler/ExportHandler.ts


6. src/node/handler/ImportHandler.ts 🐞 Bug fix +1/-6

Remove abiword converter initialization code

src/node/handler/ImportHandler.ts


7. src/node/utils/ExportTxt.ts 📝 Documentation +1/-2

Update comment about form feed character handling

src/node/utils/ExportTxt.ts


8. src/node/utils/ExportHtml.ts 📝 Documentation +1/-2

Clarify form feed character as legacy control char

src/node/utils/ExportHtml.ts


9. src/static/css/pad/popup_import_export.css Formatting +1/-7

Remove abiword message styling and display rules

src/static/css/pad/popup_import_export.css


10. src/tests/settings.json ⚙️ Configuration changes +1/-1

Remove abiword setting from test configuration

src/tests/settings.json


11. doc/docker.md 📝 Documentation +5/-19

Remove Abiword installation section, update to LibreOffice only

doc/docker.md


12. doc/docker.adoc 📝 Documentation +5/-22

Remove Abiword installation section, update to LibreOffice only

doc/docker.adoc


13. settings.json.docker ⚙️ Configuration changes +4/-12

Remove abiword setting, update soffice documentation

settings.json.docker


14. settings.json.template ⚙️ Configuration changes +4/-12

Remove abiword setting, update soffice documentation

settings.json.template


15. Dockerfile ⚙️ Configuration changes +1/-10

Remove INSTALL_ABIWORD build argument and logic

Dockerfile


16. src/locales/ar.json Additional files +0/-1

...

src/locales/ar.json


17. src/locales/ast.json Additional files +0/-1

...

src/locales/ast.json


18. src/locales/az.json Additional files +0/-1

...

src/locales/az.json


19. src/locales/bcc.json Additional files +0/-1

...

src/locales/bcc.json


20. src/locales/be-tarask.json Additional files +0/-1

...

src/locales/be-tarask.json


21. src/locales/bgn.json Additional files +0/-1

...

src/locales/bgn.json


22. src/locales/br.json Additional files +0/-1

...

src/locales/br.json


23. src/locales/ca.json Additional files +0/-1

...

src/locales/ca.json


24. src/locales/cs.json Additional files +0/-1

...

src/locales/cs.json


25. src/locales/da.json Additional files +0/-1

...

src/locales/da.json


26. src/locales/de.json Additional files +0/-1

...

src/locales/de.json


27. src/locales/diq.json Additional files +0/-1

...

src/locales/diq.json


28. src/locales/dsb.json Additional files +0/-1

...

src/locales/dsb.json


29. src/locales/dty.json Additional files +0/-1

...

src/locales/dty.json


30. src/locales/el.json Additional files +0/-1

...

src/locales/el.json


31. src/locales/en-gb.json Additional files +0/-1

...

src/locales/en-gb.json


32. src/locales/en.json Additional files +0/-1

...

src/locales/en.json


33. src/locales/eo.json Additional files +0/-1

...

src/locales/eo.json


34. src/locales/es.json Additional files +0/-1

...

src/locales/es.json


35. src/locales/et.json Additional files +0/-1

...

src/locales/et.json


36. src/locales/eu.json Additional files +0/-1

...

src/locales/eu.json


37. src/locales/fa.json Additional files +0/-1

...

src/locales/fa.json


38. src/locales/ff.json Additional files +0/-1

...

src/locales/ff.json


39. src/locales/fi.json Additional files +0/-1

...

src/locales/fi.json


40. src/locales/fo.json Additional files +0/-1

...

src/locales/fo.json


41. src/locales/fr.json Additional files +0/-1

...

src/locales/fr.json


42. src/locales/ga.json Additional files +0/-1

...

src/locales/ga.json


43. src/locales/gl.json Additional files +0/-1

...

src/locales/gl.json


44. src/locales/gu.json Additional files +0/-1

...

src/locales/gu.json


45. src/locales/he.json Additional files +0/-1

...

src/locales/he.json


46. src/locales/hr.json Additional files +0/-1

...

src/locales/hr.json


47. src/locales/hrx.json Additional files +0/-1

...

src/locales/hrx.json


48. src/locales/hsb.json Additional files +0/-1

...

src/locales/hsb.json


49. src/locales/hu.json Additional files +0/-1

...

src/locales/hu.json


50. src/locales/ia.json Additional files +0/-1

...

src/locales/ia.json


51. src/locales/id.json Additional files +0/-1

...

src/locales/id.json


52. src/locales/is.json Additional files +0/-1

...

src/locales/is.json


53. src/locales/it.json Additional files +0/-1

...

src/locales/it.json


54. src/locales/ja.json Additional files +0/-1

...

src/locales/ja.json


55. src/locales/kab.json Additional files +0/-1

...

src/locales/kab.json


56. src/locales/ko.json Additional files +0/-1

...

src/locales/ko.json


57. src/locales/krc.json Additional files +0/-1

...

src/locales/krc.json


58. src/locales/ksh.json Additional files +0/-1

...

src/locales/ksh.json


59. src/locales/lki.json Additional files +0/-1

...

src/locales/lki.json


60. src/locales/lt.json Additional files +0/-1

...

src/locales/lt.json


61. src/locales/map-bms.json Additional files +0/-1

...

src/locales/map-bms.json


62. src/locales/mk.json Additional files +0/-1

...

src/locales/mk.json


63. src/locales/ml.json Additional files +0/-1

...

src/locales/ml.json


64. src/locales/ms.json Additional files +0/-1

...

src/locales/ms.json


65. src/locales/my.json Additional files +0/-1

...

src/locales/my.json


66. src/locales/nap.json Additional files +0/-1

...

src/locales/nap.json


67. src/locales/nb.json Additional files +0/-1

...

src/locales/nb.json


68. src/locales/nds.json Additional files +0/-1

...

src/locales/nds.json


69. src/locales/nl.json Additional files +0/-1

...

src/locales/nl.json


70. src/locales/nn.json Additional files +0/-1

...

src/locales/nn.json


71. src/locales/oc.json Additional files +0/-1

...

src/locales/oc.json


72. src/locales/os.json Additional files +0/-1

...

src/locales/os.json


73. src/locales/pa.json Additional files +0/-1

...

src/locales/pa.json


74. src/locales/pl.json Additional files +0/-1

...

src/locales/pl.json


75. src/locales/pms.json Additional files +0/-1

...

src/locales/pms.json


76. src/locales/pt-br.json Additional files +0/-1

...

src/locales/pt-br.json


77. src/locales/pt.json Additional files +0/-1

...

src/locales/pt.json


78. src/locales/qqq.json Additional files +0/-1

...

src/locales/qqq.json


79. src/locales/ro.json Additional files +0/-1

...

src/locales/ro.json


80. src/locales/ru.json Additional files +0/-1

...

src/locales/ru.json


81. src/locales/sc.json Additional files +0/-1

...

src/locales/sc.json


82. src/locales/sco.json Additional files +0/-1

...

src/locales/sco.json


83. src/locales/sh-latn.json Additional files +0/-1

...

src/locales/sh-latn.json


84. src/locales/shn.json Additional files +0/-1

...

src/locales/shn.json


85. src/locales/sk.json Additional files +0/-1

...

src/locales/sk.json


86. src/locales/sl.json Additional files +0/-1

...

src/locales/sl.json


87. src/locales/sq.json Additional files +0/-1

...

src/locales/sq.json


88. src/locales/sr-ec.json Additional files +0/-1

...

src/locales/sr-ec.json


89. src/locales/sr-el.json Additional files +0/-1

...

src/locales/sr-el.json


90. src/locales/sro.json Additional files +0/-1

...

src/locales/sro.json


91. src/locales/sv.json Additional files +0/-1

...

src/locales/sv.json


92. src/locales/sw.json Additional files +0/-1

...

src/locales/sw.json


93. src/locales/th.json Additional files +0/-1

...

src/locales/th.json


94. src/locales/tr.json Additional files +0/-1

...

src/locales/tr.json


95. src/locales/uk.json Additional files +0/-1

...

src/locales/uk.json


96. src/locales/vi.json Additional files +0/-1

...

src/locales/vi.json


97. src/locales/zh-hans.json Additional files +0/-1

...

src/locales/zh-hans.json


98. src/locales/zh-hant.json Additional files +0/-1

...

src/locales/zh-hant.json


99. src/node/handler/PadMessageHandler.ts Additional files +0/-2

...

src/node/handler/PadMessageHandler.ts


100. src/node/utils/Abiword.ts Additional files +0/-93

...

src/node/utils/Abiword.ts


101. src/static/js/types/SocketIOMessage.ts Additional files +0/-1

...

src/static/js/types/SocketIOMessage.ts


102. src/static/skins/colibris/src/components/import-export.css Additional files +0/-12

...

src/static/skins/colibris/src/components/import-export.css


103. src/templates/pad.html Additional files +0/-1

...

src/templates/pad.html


104. ui/pad.html Additional files +0/-1

...

ui/pad.html


Grey Divider

Qodo Logo

@qodo-free-for-open-source-projects
Copy link
Copy Markdown

qodo-free-for-open-source-projects bot commented Apr 17, 2026

Code Review by Qodo

🐞 Bugs (2) 📘 Rule violations (2) 📎 Requirement gaps (1)

Grey Divider


Action required

1. doc export still enabled 📎 Requirement gap ≡ Correctness
Description
The export route still allows doc, so the system continues to expose/produce legacy DOC output
instead of fully replacing it with DOCX. This violates the requirement to replace DOC with DOCX for
the supported converter path(s).
Code

src/node/hooks/express/importexport.ts[R31-39]

+      const types = ['pdf', 'doc', 'docx', 'txt', 'html', 'odt', 'etherpad'];
      // send a 404 if we don't support this filetype
      if (types.indexOf(req.params.type) === -1) {
        return next();
      }

-      // if abiword is disabled, and this is a format we only support with abiword, output a message
+      // if soffice is disabled, and this is a format we only support with soffice, output a message
      if (exportAvailable() === 'no' &&
-          ['odt', 'pdf', 'doc'].indexOf(req.params.type) !== -1) {
+          ['odt', 'pdf', 'doc', 'docx'].indexOf(req.params.type) !== -1) {
Evidence
PR Compliance ID 1 requires DOC to be replaced by DOCX, but the export allowlist still includes
doc and the converter output filename uses the requested type extension, enabling DOC output
generation.

DOC export is replaced with DOCX export for Abiword and LibreOffice
src/node/hooks/express/importexport.ts[31-39]
src/node/handler/ExportHandler.ts[98-108]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The backend still supports `GET .../export/doc`, which means legacy DOC output is still exposed/produced, conflicting with the requirement to replace DOC with DOCX.

## Issue Context
The UI now uses `/export/docx`, but the server route allowlist still includes `doc` and the export handler writes the output using `.${type}`.

## Fix Focus Areas
- src/node/hooks/express/importexport.ts[31-39]
- src/node/handler/ExportHandler.ts[98-108]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. docx export lacks flag 📘 Rule violation ☼ Reliability
Description
DOCX export is newly enabled in both the server allowlist and the UI without any feature flag or
disabled-by-default toggle. This violates the requirement that new features be behind a feature flag
with the old behavior preserved when disabled.
Code

src/static/js/pad_impexp.ts[R147-163]

+      // hide stuff thats not avaible if soffice is disabled
      if (clientVars.exportAvailable === 'no') {
        $('#exportworda').remove();
        $('#exportpdfa').remove();
        $('#exportopena').remove();
-
-        $('#importmessageabiword').show();
      } else if (clientVars.exportAvailable === 'withoutPDF') {
        $('#exportpdfa').remove();

-        $('#exportworda').attr('href', `${padRootPath}/export/doc`);
+        $('#exportworda').attr('href', `${padRootPath}/export/docx`);
        $('#exportopena').attr('href', `${padRootPath}/export/odt`);

        $('#importexport').css({height: '142px'});
        $('#importexportline').css({height: '142px'});
      } else {
-        $('#exportworda').attr('href', `${padRootPath}/export/doc`);
+        $('#exportworda').attr('href', `${padRootPath}/export/docx`);
        $('#exportpdfa').attr('href', `${padRootPath}/export/pdf`);
        $('#exportopena').attr('href', `${padRootPath}/export/odt`);
Evidence
PR Compliance ID 9 requires new features to be feature-flagged and disabled by default, but DOCX
export is wired into the UI and accepted by the server unconditionally (when export is available).

src/static/js/pad_impexp.ts[147-164]
src/node/hooks/express/importexport.ts[31-31]
Best Practice: Repository guidelines

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
DOCX export is introduced/enabled without a feature flag and changes existing behavior (the Word button now downloads DOCX).

## Issue Context
Compliance requires new features to be behind a flag, disabled by default, and to preserve the old execution path when disabled.

## Fix Focus Areas
- src/static/js/pad_impexp.ts[147-164]
- src/node/hooks/express/importexport.ts[31-46]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Abiword removed without WARN 📘 Rule violation ⚙ Maintainability
Description
Abiword import/export support is removed outright (config + runtime converter path), but there is no
explicit deprecation period behavior that emits a WARN explaining the upcoming removal. This
violates the feature-removal deprecation requirement and increases upgrade risk for existing
deployments.
Code

src/node/handler/ImportHandler.ts[R59-64]

let converter:any = null;
let exportExtension = 'htm';

-// load abiword only if it is enabled and if soffice is disabled
-if (settings.abiword != null && settings.soffice == null) {
-  converter = require('../utils/Abiword');
-}
-
// load soffice only if it is enabled
if (settings.soffice != null) {
  converter = require('../utils/LibreOffice');
Evidence
PR Compliance ID 8 requires removals to be deprecated first with a WARN; the current code and
templates now only support LibreOffice (soffice) and contain no Abiword compatibility path,
indicating immediate removal rather than deprecation.

src/node/handler/ImportHandler.ts[59-66]
settings.json.template[338-345]
Best Practice: Repository guidelines

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Abiword support was removed without an explicit deprecation mechanism and dedicated WARN messaging to guide users before removal.

## Issue Context
Compliance requires a deprecation phase that emits a WARN stating the feature will be removed in a future version. Consider temporarily accepting `abiword` config with a WARN and either continuing to support it for one release cycle or mapping it to the new supported path.

## Fix Focus Areas
- src/node/handler/ImportHandler.ts[59-66]
- settings.json.template[338-345]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

4. Import ignores settings reload 🐞 Bug ☼ Reliability
Description
ImportHandler decides whether to use LibreOffice once at module load (based on settings.soffice) and
never re-evaluates it, so reloadSettings() at runtime won’t actually enable/disable advanced
imports. This can cause imports to keep failing after enabling soffice, or to attempt conversion
when reloadSettings() has cleared settings.soffice.
Code

src/node/handler/ImportHandler.ts[R59-64]

let converter:any = null;
let exportExtension = 'htm';

-// load abiword only if it is enabled and if soffice is disabled
-if (settings.abiword != null && settings.soffice == null) {
-  converter = require('../utils/Abiword');
-}
-
// load soffice only if it is enabled
if (settings.soffice != null) {
  converter = require('../utils/LibreOffice');
Evidence
ImportHandler caches converter and exportExtension at module scope based on settings.soffice,
so later config changes won’t be reflected per request. The app calls reloadSettings() at runtime
(admin “restartServer” and after plugin install), and LibreOffice execution reads settings.soffice
at conversion time—so the cached converter != null flag can diverge from the current
settings.soffice value.

src/node/handler/ImportHandler.ts[59-66]
src/node/hooks/express/adminsettings.ts[307-313]
src/static/js/pluginfw/installer.ts[30-36]
src/node/utils/LibreOffice.ts[35-50]
src/node/utils/Settings.ts[992-1004]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`ImportHandler` snapshots whether LibreOffice is enabled (`converter` and `exportExtension`) at module load time. Because `reloadSettings()` is invoked at runtime (admin restartServer, plugin installer), imports do not reflect settings changes until a full Node process restart. Worse, if `reloadSettings()` clears `settings.soffice` (bad path), the cached `converter != null` can remain true and later conversions will attempt to run LibreOffice with a null/invalid command.

## Issue Context
Exports already gate behavior dynamically via `exportAvailable()`/`settings.soffice`. Imports should similarly evaluate current settings at request time.

## Fix Focus Areas
- src/node/handler/ImportHandler.ts[59-66]

## Suggested fix approach
- Remove the module-scope caching of `converter`/`exportExtension`.
- Inside `doImport()`, determine `useConverter` from the *current* `settings.soffice` (consider treating empty string as disabled as well), and only then `require('../utils/LibreOffice')` and set `exportExtension`.
- Ensure the non-converter path still uses the prior behavior (`.htm` output, ASCII check, etc.).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Advisory comments

5. Misleading SOFFICE null comment 🐞 Bug ⚙ Maintainability
Description
Settings.ts claims "${SOFFICE:null}" results in the literal string "null", but coerceValue()
explicitly converts the string "null" to JavaScript null. This contradiction can mislead
maintainers/admins reading the guidance.
Code

src/node/utils/Settings.ts[R744-745]

+ * short syntax "${SOFFICE}", and not "${SOFFICE:null}": the latter would result
 * in the literal string "null", instead.
Evidence
The comment explicitly says ${SOFFICE:null} produces a literal string, but the conversion code
returns null for the string 'null', so the comment is inaccurate relative to actual behavior.

src/node/utils/Settings.ts[739-746]
src/node/utils/Settings.ts[758-767]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
A Settings.ts comment states that using the default syntax `${SOFFICE:null}` would result in the literal string "null", but `coerceValue()` converts the string `"null"` to JavaScript `null`. This inconsistency can confuse future edits.

## Issue Context
This is a documentation/maintainability fix only; runtime behavior is already correct.

## Fix Focus Areas
- src/node/utils/Settings.ts[739-746]

## Suggested fix approach
- Update or remove the misleading sentence so it reflects actual behavior (that `:null` is coerced to `null`).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

ⓘ The new review experience is currently in Beta. Learn more

Grey Divider

Qodo Logo

Comment on lines +31 to +39
const types = ['pdf', 'doc', 'docx', 'txt', 'html', 'odt', 'etherpad'];
// send a 404 if we don't support this filetype
if (types.indexOf(req.params.type) === -1) {
return next();
}

// if abiword is disabled, and this is a format we only support with abiword, output a message
// if soffice is disabled, and this is a format we only support with soffice, output a message
if (exportAvailable() === 'no' &&
['odt', 'pdf', 'doc'].indexOf(req.params.type) !== -1) {
['odt', 'pdf', 'doc', 'docx'].indexOf(req.params.type) !== -1) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

1. doc export still enabled 📎 Requirement gap ≡ Correctness

The export route still allows doc, so the system continues to expose/produce legacy DOC output
instead of fully replacing it with DOCX. This violates the requirement to replace DOC with DOCX for
the supported converter path(s).
Agent Prompt
## Issue description
The backend still supports `GET .../export/doc`, which means legacy DOC output is still exposed/produced, conflicting with the requirement to replace DOC with DOCX.

## Issue Context
The UI now uses `/export/docx`, but the server route allowlist still includes `doc` and the export handler writes the output using `.${type}`.

## Fix Focus Areas
- src/node/hooks/express/importexport.ts[31-39]
- src/node/handler/ExportHandler.ts[98-108]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +147 to 163
// hide stuff thats not avaible if soffice is disabled
if (clientVars.exportAvailable === 'no') {
$('#exportworda').remove();
$('#exportpdfa').remove();
$('#exportopena').remove();

$('#importmessageabiword').show();
} else if (clientVars.exportAvailable === 'withoutPDF') {
$('#exportpdfa').remove();

$('#exportworda').attr('href', `${padRootPath}/export/doc`);
$('#exportworda').attr('href', `${padRootPath}/export/docx`);
$('#exportopena').attr('href', `${padRootPath}/export/odt`);

$('#importexport').css({height: '142px'});
$('#importexportline').css({height: '142px'});
} else {
$('#exportworda').attr('href', `${padRootPath}/export/doc`);
$('#exportworda').attr('href', `${padRootPath}/export/docx`);
$('#exportpdfa').attr('href', `${padRootPath}/export/pdf`);
$('#exportopena').attr('href', `${padRootPath}/export/odt`);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

2. docx export lacks flag 📘 Rule violation ☼ Reliability

DOCX export is newly enabled in both the server allowlist and the UI without any feature flag or
disabled-by-default toggle. This violates the requirement that new features be behind a feature flag
with the old behavior preserved when disabled.
Agent Prompt
## Issue description
DOCX export is introduced/enabled without a feature flag and changes existing behavior (the Word button now downloads DOCX).

## Issue Context
Compliance requires new features to be behind a flag, disabled by default, and to preserve the old execution path when disabled.

## Fix Focus Areas
- src/static/js/pad_impexp.ts[147-164]
- src/node/hooks/express/importexport.ts[31-46]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines 59 to 64
let converter:any = null;
let exportExtension = 'htm';

// load abiword only if it is enabled and if soffice is disabled
if (settings.abiword != null && settings.soffice == null) {
converter = require('../utils/Abiword');
}

// load soffice only if it is enabled
if (settings.soffice != null) {
converter = require('../utils/LibreOffice');
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

3. abiword removed without warn 📘 Rule violation ⚙ Maintainability

Abiword import/export support is removed outright (config + runtime converter path), but there is no
explicit deprecation period behavior that emits a WARN explaining the upcoming removal. This
violates the feature-removal deprecation requirement and increases upgrade risk for existing
deployments.
Agent Prompt
## Issue description
Abiword support was removed without an explicit deprecation mechanism and dedicated WARN messaging to guide users before removal.

## Issue Context
Compliance requires a deprecation phase that emits a WARN stating the feature will be removed in a future version. Consider temporarily accepting `abiword` config with a WARN and either continuing to support it for one release cycle or mapping it to the new supported path.

## Fix Focus Areas
- src/node/handler/ImportHandler.ts[59-66]
- settings.json.template[338-345]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

@JohnMcLear JohnMcLear assigned JohnMcLear and Copilot and unassigned JohnMcLear Apr 17, 2026
@JohnMcLear
Copy link
Copy Markdown
Member Author

@copilot address qodos concerns and fix the PR

Copilot AI and others added 2 commits April 17, 2026 16:50
- Add `docxExport: true` setting to opt out of DOCX (use legacy DOC)
- Pass `docxExport` to client via clientVars
- Use `docxExport` flag in pad_impexp.ts for Word button format
- Emit a specific WARN when deprecated `abiword` config is detected
- Update settings.json.template and settings.json.docker with docxExport
- Add docxExport to ClientVarPayload type in SocketIOMessage.ts

Agent-Logs-Url: https://github.com/ether/etherpad/sessions/9afc5291-73b2-4b66-b028-feed39e7056f

Co-authored-by: JohnMcLear <220864+JohnMcLear@users.noreply.github.com>
Copy link
Copy Markdown

Copilot AI commented Apr 17, 2026

@copilot address qodos concerns and fix the PR

Addressed all three Qodos concerns in commits d012f90 and 226c476:

  1. doc export still enabled – Kept intentionally for backward API compatibility (documented in PR description). Removing it would break existing API consumers calling /export/doc.

  2. docx export lacks feature flag – Added docxExport: true setting. When set to false, the Word button falls back to /export/doc. Exposed via clientVars.docxExport, documented in both settings.json.template and settings.json.docker (with DOCX_EXPORT env variable support).

  3. abiword removed without WARN – Added an explicit startup WARN in reloadSettings(): when an abiword key is detected in the parsed config, a targeted message is logged with migration instructions pointing users to soffice.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

replace doc export with docx format

2 participants