### Goal Restore the image button (`espoImage`) in the EspoCRM **signature** editor toolbar. The signature field uses the core view `views/preferences/fields/signature`, whose `setupToolbar()` overrides the toolbar to `[["style",[…]],["color",["color"]],["height",["height"]],["table",["espoLink"]],["misc",[,"fullscreen"]]]` — i.e. it drops the `espoImage` button that the generic wysiwyg field has. We want the image button back **only for the signature field**, without changing anything else. Do **not** touch other fields, ACLs, the generic wysiwyg editor (email body / templates already have the image button), or any data. ### Environment - EspoCRM **9.3.8** (Apache variant) runs as a **rootless Podman pod** `espocrm_pod` on this server. Web container is expected to be `espocrm_ctr` (confirm with `podman ps`). - App root inside the web container: `/var/www/html`, runs as user **`www-data`**. - Customizations live on the persisted `/var/www/html` bind-mount, same locations as the S47 task: - Backend metadata: `/var/www/html/custom/Espo/Custom/Resources/metadata/…` - Frontend custom views: `/var/www/html/client/custom/src/…` (referenced with the `custom:` prefix). ### Mechanism (from 9.3.8 source — confirm against this instance before relying on it) - The signature field view `client/src/views/preferences/fields/signature.js` extends the wysiwyg field and **overrides** `setupToolbar()`. Its `this.toolbar` contains a group `['table', ['espoLink']]` — the link button but **not** `espoImage`. - The generic wysiwyg field `client/src/views/fields/wysiwyg.js` registers the upload callback in `enableWysiwygMode()`: `onImageUpload` → `uploadInlineAttachment(file)` → creates an `Attachment` (`role: 'Inline Attachment'`) and inserts ``. This callback is present regardless of the toolbar, so as soon as the `espoImage` button is in the toolbar, its upload path produces an attachment. - `espoImage` is a registered summernote plugin (`client/src/helpers/misc/summernote-custom.js`): its button invokes `espoImage.show` → modal `views/wysiwyg/modals/insert-image`, whose **upload** path runs through `insertImagesOrCallback` → the `onImageUpload` callback above. (The modal's **URL** path inserts a plain remote `` instead — not what we want.) Verify before relying on it: ``` podman exec espocrm_ctr sh -lc "grep -n 'setupToolbar\|espoLink\|espoImage\|table' /var/www/html/client/src/views/preferences/fields/signature.js" podman exec espocrm_ctr sh -lc "grep -n 'onImageUpload\|uploadInlineAttachment\|entryPoint=attachment' /var/www/html/client/src/views/fields/wysiwyg.js" ``` You should see the reduced toolbar with `['table', ['espoLink']]` and the `onImageUpload`→attachment path. If the structure differs in this build, adjust the toolbar-patch below to match the real group names. ### Steps 1. **Confirm container.** `podman ps` → identify the web container (expected `espocrm_ctr`). 2. **Create the custom signature field view** on the persisted mount: `/var/www/html/client/custom/src/views/preferences/fields/signature.js` ```js define('custom:views/preferences/fields/signature', ['views/preferences/fields/signature'], (SignatureFieldView) => { return class extends SignatureFieldView { setupToolbar() { // Build the reduced signature toolbar first … super.setupToolbar(); // … then re-add the image button next to the link button. this.toolbar = (this.toolbar || []).map(group => { if (Array.isArray(group) && group[0] === 'table') { const buttons = group[1] || []; if (!buttons.includes('espoImage')) { return ['table', [...buttons, 'espoImage']]; } } return group; }); } }; }); ``` Create the directory if needed: ``` podman exec espocrm_ctr sh -lc "mkdir -p /var/www/html/client/custom/src/views/preferences/fields" ``` > If the `grep` in the verify step shows no group whose first element is `'table'`, fall back to simply appending a new group instead of the `.map(...)`: `this.toolbar.push(['insert', ['espoImage']]);` (place it before the final `['misc', …]` group if you want it left of code-view/fullscreen). 3. **Point the signature field at the custom view** via an entityDefs override. Merge into (create if absent): `/var/www/html/custom/Espo/Custom/Resources/metadata/entityDefs/Preferences.json` ```json { "fields": { "signature": { "view": "custom:views/preferences/fields/signature" } } } ``` **MERGE** — if the file already exists, preserve all other keys and only add `fields.signature.view`. Do not drop the field's `type` (it stays `wysiwyg`; metadata is merged on top of core). Produce valid JSON. 4. **Fix ownership** (as root in the container): ``` podman exec espocrm_ctr chown -R www-data:www-data /var/www/html/client/custom /var/www/html/custom/Espo/Custom/Resources/metadata ``` 5. **Clear cache / rebuild** (as the web user): ``` podman exec -u www-data espocrm_ctr php command.php rebuild ``` (If `command.php` is missing in this build, use `bin/command rebuild`; or Administration → Rebuild in the UI as admin.) 6. **Verify (hard reload Ctrl+Shift+R first).** Open Benutzereinstellungen → the „E-Mail Signatur" editor. The toolbar must now show a **Bild/Image button** (picture icon) next to the link button. Click it → a „Bild einfügen" dialog opens with a **file-upload** option (and a URL field). Nothing else in EspoCRM should have changed. ### Rollback 1. Delete `/var/www/html/client/custom/src/views/preferences/fields/signature.js`. 2. Remove the `fields.signature.view` key from `…/metadata/entityDefs/Preferences.json` (or delete the file if it now only contains that key). 3. Rebuild (step 5) and hard-reload the browser.