Files
RenewCMS/docs/releases/v3.4.1-beta.md
T

6.5 KiB

This release adds a Media field type to custom content types, shows where each image is used, makes merging duplicate images safe against concurrent edits, and replaces or updates dependencies to close known security advisories.

New

Media field

  • A custom content type can now have a Media field that holds one image from the media library. The entry stores the image, not a copy, so the field always shows the current library image.
  • A Media field has a label, a key and a required switch. It has no default, unique, minimum, maximum or choices, and its type cannot change after the first save.
  • In the schema builder, Media is the seventh field type. Adding a required Media field to a type that already has entries is refused in the review step, because such a field cannot have a default.
  • In the entry editor, the field shows a thumbnail with Change image and Remove buttons, opens the library picker on click or keyboard, and can upload a new image from the picker's Upload tab. A required field has no "No image" choice.
  • When an image was deleted after an entry was saved, the entry keeps the link and the field shows "This image is no longer in the library". An optional field can still be saved, and a required one asks for a new image.
  • The entry list gets a Thumbnail column and a thumbnail on mobile cards. It can be added from the Columns menu or shown as the first column.

Image usage in the media library

  • Entries now count as uses of an image, together with articles, authors and sites. The usage badge, the Unused filter and the unused count all include them.
  • Hovering or focusing a usage badge on a card shows how many articles and entries use the image.
  • The image detail panel has a "Used in entries" section listing each entry with its content type and title, as a link to the entry.
  • Deleting, bulk deleting and merging images now warn with counts of uses, and say what happens to entries that point at the image. Deleting a used image is still allowed and leaves the entries' data untouched.

Improvements and fixes

  • Merging duplicate images is now all-or-nothing. If any step fails, articles, entries and the library are left exactly as they were.
  • Stored files are removed only after the merge is committed. A file that cannot be removed afterwards is logged and does not undo the merge.
  • A merge no longer overwrites an article or entry edited while it was running. Articles and entries are re-read before they are rewritten, and ones deleted in the meantime are skipped.
  • A duplicate purge scans entries once instead of once per group, commits one group at a time, so one failing group no longer undoes the others, and stops cleanly when the request is cancelled.
  • Sessions now expire. New sign-in tokens carry a 24 hour expiry, matching the cookie. Sessions created before this release keep working until you sign out.

API

  • Changed: an entry response now has eight keys, the seven existing ones plus media. It is an object keyed by Media field key, {} when the type has no Media field. Each value is { "id", "filename", "width", "height", "size", "thumbnail_url" } for an existing image, or { "id", "missing": true } when the image is gone. data keeps the image id, or null, as the field's value. It is on POST (201), PUT (200), GET one and every item of the list.
  • POST /v1/content-type and PUT /v1/content-type/{key} accept fields of type media. A Media field with unique, default, min, max or options answers 422 invalid_field.
  • POST and PUT on /v1/content-type/{key}/entry check that a new or changed image id exists in the library, otherwise 422 invalid_entry: <key>: type. A required Media field that is empty, or whose stored image is gone, answers 422 invalid_entry: <key>: required.
  • Changed: used_in on GET /v1/media/ and GET /v1/media/{id} now includes entries, and filter=unused and counts.unused treat an image used by an entry as used.
  • Changed: GET /v1/media/{id}/usage also returns items of type entry, with id, fields, content_type_key, content_type_name and first_field (key, type and the raw stored value). They follow the article, author and site items, ordered by content type name and then id. Existing item shapes are unchanged.
  • Merging duplicates, by hand or through the purge, now repoints entry Media values to the kept image.

Under the hood

  • Seven direct and twelve indirect Go modules are gone. Sign-in tokens, SMTP delivery, .env loading, CORS, request logging and the image-variant single-flight group are now small in-repo implementations on the standard library, with tests. The removed modules are jwtauth, lestrrat-go/jwx, gomail, httplog, godotenv, go-chi/cors and x/sync/singleflight, and the dependencies they pulled in.
  • Every remaining Go module is updated, and the Go toolchain moves to 1.27.3. govulncheck reports no vulnerability reachable from the code, where it found thirteen in the standard library and several in golang.org/x/net and chi before.
  • Web dependencies are updated, which clears all but three dev-only audit findings. npm-run-all2 is removed, and bun run build now runs the type check and then the build.
  • A transaction port in the domain lets use cases commit several writes together, with a GORM adapter that works on SQLite, MySQL, MariaDB and PostgreSQL.
  • Feature specs and QA procedures for each slice are in features/.

Upgrading

  • Replace the binary, or pull the new image. Back up the database first as usual.
  • No schema change and no new environment variable. A Media field is stored in the existing content type and entry tables.
  • .env files no longer expand $VARIABLES and do not support multi-line values. A malformed line now makes the file ignored, and the server logs the line number. Real environment variables still take precedence.
  • If you build the image yourself, the Go base image is now golang:1.27.3-alpine.
  • SMTP now fails with a clear error when a login is set but the server offers no authentication, where it used to continue without logging in. Credentials are never sent in clear text to a host other than localhost.
  • CORS_ALLOWED_ORIGINS also accepts one * per entry, for example https://*.preview.example.com, and a lone * for any origin. Credentials are never allowed cross-origin.

Known limitations

  • A Media field holds one image. Galleries are not supported.
  • Entries still cannot be filtered or searched by their field values, and there are no relations between types.
  • The editor preview of a Media field uses the original image, with no variant choice.