Provisioning now restores all GUI customizations on reset+reprovision: - create_pod_espocrm.sh: deploy the version-controlled espocrm-custom/ tree (CTag entity, layouts, i18n, clientDefs, custom views, custom CSS) into the pod, then chown www-data and rebuild. Replaces the earlier inline CSS-only step. Adds a live-phase cache rebuild so customizations and the client cacheTimestamp are refreshed on every run. - espocrm-custom/: snapshot of custom/ and client/custom/ (source of truth). - snapshot_espocrm_custom.sh: refresh the snapshot from a running pod. - readme.md: usage, first-time host setup, image-update and reset workflows. - Include the task/instruction notes and plan.md for reference. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
6.0 KiB
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",[<codeview>,"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_podon this server. Web container is expected to beespocrm_ctr(confirm withpodman ps). - App root inside the web container:
/var/www/html, runs as userwww-data. - Customizations live on the persisted
/var/www/htmlbind-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 thecustom:prefix).
- Backend metadata:
Mechanism (from 9.3.8 source — confirm against this instance before relying on it)
- The signature field view
client/src/views/preferences/fields/signature.jsextends the wysiwyg field and overridessetupToolbar(). Itsthis.toolbarcontains a group['table', ['espoLink']]— the link button but notespoImage. - The generic wysiwyg field
client/src/views/fields/wysiwyg.jsregisters the upload callback inenableWysiwygMode():onImageUpload→uploadInlineAttachment(file)→ creates anAttachment(role: 'Inline Attachment') and inserts<img src="?entryPoint=attachment&id=…">. This callback is present regardless of the toolbar, so as soon as theespoImagebutton is in the toolbar, its upload path produces an attachment. espoImageis a registered summernote plugin (client/src/helpers/misc/summernote-custom.js): its button invokesespoImage.show→ modalviews/wysiwyg/modals/insert-image, whose upload path runs throughinsertImagesOrCallback→ theonImageUploadcallback above. (The modal's URL path inserts a plain remote<img>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
-
Confirm container.
podman ps→ identify the web container (expectedespocrm_ctr). -
Create the custom signature field view on the persisted mount:
/var/www/html/client/custom/src/views/preferences/fields/signature.jsdefine('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
grepin 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). -
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{ "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'stype(it stayswysiwyg; metadata is merged on top of core). Produce valid JSON. -
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 -
Clear cache / rebuild (as the web user):
podman exec -u www-data espocrm_ctr php command.php rebuild(If
command.phpis missing in this build, usebin/command rebuild; or Administration → Rebuild in the UI as admin.) -
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
- Delete
/var/www/html/client/custom/src/views/preferences/fields/signature.js. - Remove the
fields.signature.viewkey from…/metadata/entityDefs/Preferences.json(or delete the file if it now only contains that key). - Rebuild (step 5) and hard-reload the browser.