Search by

hamaka / silverstripe-pasted-base64-images

carelevers

Converts images pasted or dropped into the HTML editor (TinyMCE paste_data_images, stored as base64 data URIs) into regular files in the asset admin with an [image] shortcode, on save and for existing content including version history.

Package info

github.com/hamaka/silverstripe-pasted-base64-images

Type:silverstripe-vendormodule

pkg:composer/hamaka/silverstripe-pasted-base64-images

Statistics

Installs: 6

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

2.0.3 2026-11-10 15:56 UTC

This package is auto-updated.

Last update: 2026-11-10 15:56:35 UTC


README

Converts images that were pasted or dropped into the HTML editor, and stored as base64 data URIs, into regular files in Files with a standard [image] shortcode. It does this on save, and with a task for existing content, including the version history.

The problem

TinyMCE has paste_data_images enabled by default in Silverstripe 5 and 6. A pasted or dropped image ends up in the HTML field as:

<img src="data:image/png;base64,iVBORw0KGgo...">
  • The database keeps growing. Every save or publish copies the full image into SiteTree_Versions again.
  • The image isn't in Files. No management, no reuse, no resizing, no caching as a separate file.

Pasting keeps working for editors: this module leaves paste_data_images on and fixes it on the server.

What it does

  • On write, an extension on DataObject converts every HTMLText field that contains a base64 image. That covers pages, elemental blocks and every other object. By default only for users with CMS access.
  • Each image (png, jpg, gif or webp) is validated, saved as an Image in Uploads/pasted-images and published. The same image is stored once.
  • The <img> becomes an [image] shortcode. alt, class, width, height and title are kept.
  • On an error the original tag is left as it is, so no content is lost. The error is logged.
  • A task converts existing content, see Cleaning up existing content.

Requirements

Silverstripe PHP Version Branch
5.3+ 8.1+ ^1 1
6 8.3+ ^2 2 (this branch)

Installation

composer require hamaka/silverstripe-pasted-base64-images

Then flush: ?flush=1 in the browser, or vendor/bin/sake flush. The module adds no tables and works without configuration.

Configuration

All settings are optional. These are the defaults:

Hamaka\Base64Images\Base64ImageConverter:
  folder: 'Uploads/pasted-images'          # folder in Files
  filename_prefix: 'pasted-'               # followed by the first 12 characters of the sha1
  image_title: 'Pasted image'
  default_class: 'leftAlone ss-htmleditorfield-file image'  # used when the <img> has no class
  generate_thumbnails: true                # thumbnails for the file list in the CMS

  write_permission: 'CMS_ACCESS'           # who triggers the conversion on write; false: everyone
  max_bytes: 20971520                      # largest image that is converted (20 MB); 0: no limit
  max_pixels: 50000000                     # largest width x height that is converted; 0: no limit
  max_images: 100                          # per field of one record; 0: no limit

  exclude_classes: []                      # classes that are never converted, with their subclasses
  exclude_fields: []                       # fields that are never converted, as 'Class.Field'

  notify_email: null                       # null: Email.admin_email; false: no mail
  notify_throttle_minutes: 60              # at most one mail per this many minutes; 0: no throttling
  • write_permission: the conversion creates and publishes files, so HTML submitted by visitors without CMS access is left as it is. Writes from the command line and the task are always converted.
  • exclude_classes and exclude_fields: for fields that are not rendered through the shortcode parser, such as a newsletter body or content served through an API. They apply on write and in the task.
  • notify_email: Email.admin_email is empty by default, so no mail is sent until you set one of the two.

To disable the conversion on write altogether and only keep the task:

SilverStripe\ORM\DataObject:
  extensions:
    base64Images: null

Cleaning up existing content

ConvertBase64ImagesTask converts the base table, _Live and _Versions of every class, and the _Localised tables of Fluent.

Make a database backup first.

vendor/bin/sake tasks:ConvertBase64ImagesTask                          # dry run: count and report
vendor/bin/sake tasks:ConvertBase64ImagesTask --details                # dry run, per record
vendor/bin/sake tasks:ConvertBase64ImagesTask --apply                  # convert + OPTIMIZE TABLE
vendor/bin/sake tasks:ConvertBase64ImagesTask --apply --no-optimize    # without OPTIMIZE TABLE
  • It writes with direct SQL: no new versions are created, and pages don't show up as "Modified".
  • Old versions point to the same file, so the History tab keeps showing the image.
  • It runs OPTIMIZE TABLE on the changed tables, because InnoDB doesn't release the freed space otherwise.
  • It exits with a non-zero code when an image could not be converted.
  • It generates the thumbnails for the file list in the CMS for converted files that don't have them yet.
  • It is safe to run again.

The output of an apply run, shortened:

MODE: APPLY
SiteTree.Content: 3 record(s) with data:image/ ...
SiteTree_Versions.Content: 120 record(s) with data:image/ ...
--- Per table ---
SiteTree.Content: 3 records with data:image/, 3 updated, 4 images, 310 KB base64
SiteTree_Versions.Content: 120 records with data:image/, 119 updated, 121 images, 9.2 MB base64, 1 skipped
--- Total ---
125 images converted (9.5 MB base64 removed from the HTML)
4 new files in Files (Uploads/pasted-images), an existing file was reused 121 times
--- Skipped (not converted by this module, left as they are) ---
1 x Unsupported data URI (data:image/svg+xml;base64)
--- Table sizes ---
SiteTree_Versions: 14.1 MB -> 4.3 MB

Skipped data URIs are ones this module does not convert, see below. Errors are images that should have been converted but could not be; replace those in the CMS.

Limitations

  • Only png, jpg, gif and webp in the src of an <img>. Svg, avif, bmp, srcset and CSS backgrounds are left as they are.
  • Only HTMLText fields. HTMLFragment and HTMLVarchar don't render shortcodes, so they are left alone.
  • Files are published immediately, also when the page they are on is still a draft.
  • The task sets LastEdited on the records it changes. Flush static caches yourself.
  • OPTIMIZE TABLE rebuilds the table and needs free disk space of about its size. It and the table sizes are MySQL and MariaDB only.
  • The dry run leaves your content alone, but runs ANALYZE TABLE to get current table sizes.

Contributing

Report bugs and ideas in the issues. Pull requests are welcome; code that is the same for Silverstripe 5 and 6 goes to branch 1.

License

BSD-3-Clause, see LICENSE.md.