Problem/Motivation

The module uses one Document Server address for two different clients: the browser loads the editor from it, and the Drupal server downloads saved documents from it. The README says the address must be reachable from both. In many setups only the browser can use the public address, while the Drupal server reaches Document Server under another one, for example:

  • the public host name resolves differently, or not at all, on the Drupal host;
  • the public address sits behind a reverse proxy or firewall that does not let the Drupal host back in;
  • the public address uses a TLS certificate the Drupal host does not trust;
  • both servers share an internal network, and sending every save out through the public address and back in is a detour.

Saving then fails without any visible error at that moment. When the editor is closed, Document Server sends the callback with status 2 and a url pointing to the saved file. Document Server builds that URL from the address the editor was opened under, i.e. the public one. OnlyofficeCallbackController::proccessSave() downloads it with file_get_contents($body->url), the download fails, and the callback answers 400 ("Error download file from …" in the onlyoffice log channel). Document Server keeps the changes itself. The next time the same document is opened, the editor shows "This file is opened from a server backup copy and is not saved to storage. Use the 'Save as...' option to save it to a drive." The changes never reach the media entity.

ONLYOFFICE's own Nextcloud connector already solves this, and the proposed change follows it: the app has a setting "ONLYOFFICE Docs address for internal requests from the server", and its callback passes the file URL through replaceDocumentServerUrlToInternal(), which replaces the public address with the internal one (case-insensitively, str_ireplace()) before downloading.

Steps to reproduce

  1. Run Document Server under an address the browser can reach but the Drupal server cannot (for example a host name that resolves only outside the Drupal host). Document Server itself must be able to reach Drupal.
  2. Enter that address and the secret key on the ONLYOFFICE Connector settings page.
  3. Open an office document from the media library in the editor, change it, close the editor.
  4. Drupal's log shows "Error download file from https://<documentserver>/cache/files/…"; Document Server's log shows the callback answered with status code 400.
  5. Open the document again: the editor shows the "server backup copy" warning, and the media entity still holds the old file.

Proposed resolution

An optional setting Document Editing Service address for internal requests from the server (doc_server_url_internal in onlyoffice.settings). When it is set, the save callbacks of the module and of the ONLYOFFICE Form submodule replace the configured public address at the start of the file URL with the internal one before downloading:

https://docs.example.com/cache/files/data/…/output.docx?md5=…&expires=…
→ http://documentserver.internal/cache/files/data/…/output.docx?md5=…&expires=…

Document Server signs these links over path and expiry, not the host, so the rewritten link stays valid. A URL that does not start with the configured public address is left as it is, and with the setting empty nothing changes. The browser keeps using the public address for the editor.

The merge request implements this against 3.0.x; its diff applies to 2.0.x unchanged:

  • config/schema/onlyoffice.schema.yml: the new key.
  • SettingsForm: an optional field, validated as a URL when filled, trailing slash removed on save.
  • OnlyofficeUrlHelper::replaceDocServerUrlToInternal(): the rewrite; the comparison ignores case, as host names do and as the Nextcloud connector does.
  • OnlyofficeCallbackController and OnlyofficeFormCallbackController: download through it.
  • README.md: the setting under Configuration.

Since the value is ordinary configuration, it can also be set per environment in settings.php:

$config['onlyoffice.settings']['doc_server_url_internal'] = 'http://documentserver.internal';

Tested on 2.0.0 with ONLYOFFICE Docs 9.3: with the internal address set, the save callback answers 200 and the media entity gets a new revision with the edited file; without it, the behaviour is unchanged. phpcs --standard=Drupal,DrupalPractice reports nothing for the changed files.

Remaining tasks

  • Review.

User interface changes

One optional text field on the ONLYOFFICE Connector settings page, below the Document Editing Service address.

API changes

New static method OnlyofficeUrlHelper::replaceDocServerUrlToInternal(string $url).

Data model changes

New optional key doc_server_url_internal in onlyoffice.settings. No update hook: a missing key means the setting is off.

Issue fork onlyoffice-3629152

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Comments

daniel.pernold created an issue. See original summary.

daniel.pernold’s picture

Issue summary: View changes