September 2021 Release

Attention: If you skipped one or more releases, please also check the release-notes of the skipped ones.





System Requirements


Name Version
Node 16
Postgres 13
Elasticsearch 7
Redis 6
Livingdocs Server Docker Image livingdocs/server-base:16
Livingdocs Editor Docker Image livingdocs/editor-base:16
Browser Support Edge >= 80, Firefox >= 74, Chrome >= 80, Safari >= 13.1, iOS Safari >= 13.4, Opera >= 67


Name Version
Node 14
Postgres 9.6 (Deprecated Postgres 9 and 10)
Elasticsearch 6.x
Redis 5
Livingdocs Server Docker Image livingdocs/server-base:14.3
Livingdocs Editor Docker Image livingdocs/editor-base:14.3
Browser Support Edge >= 80, Firefox >= 74, Chrome >= 80, Safari >= 13.1, iOS Safari >= 13.4, Opera >= 67


Revoke Media

The new “revoke media” functionality in the media library allows users to delete media files from storage instead of archiving them. This can be useful in situations such as handling requests to completely removing media for legal reasons. Customers have different requirements for what should happen after revoking an image, like

  • remove revoked images from documents
  • clearing caches
  • sending an email notification Therefore we provide a server event and a webhook, both named mediaLibraryEntry.revoke, which the downstream server can subscribe to.

Dashboard Cards Configuration for the Media Library 🎉

This feature allows you to register your own dashboard card in the Media Library. The default card liMediaLibraryCard, allows to be extended with additional metadata fields.

Dashboard Cards Configuration for the Document List 🎉

This feature allows you to register your own dashboard card in the Document List.

List Teasers

We now support list teasers as includes without registering your own plugin.

Responsive Editing Toolbar 🎉

With the introduction of more an more actions in the Document Editing Toolbar, space was getting rare, especially on smaller screens. Therefore a responsive editing toolbar has been introduced which collapse actions to groups when there is not enough space.

Screenshot 2021-07-06 at 12 51 19 Screenshot 2021-07-06 at 12 51 38 Screenshot 2021-07-06 at 12 51 57 Screenshot 2021-07-06 at 12 53 02

Restricted Users 🎉

It’s now possible to define a user group with “Restricted Users”. Restricted users can only see their own documents and assets. This can be useful if you have external editors (freelancer).

Improve Keyboard Navigation and Viewport Focus in Editor (more Word like behavior) 🎉

You can now use the arrow keys on your keyboard to navigate between different text components. The cursor position is maintained, so that the editor can always get back to where she started after navigating around. The viewport scrolls automatically if the cursor hits positions outside of the viewport. The same applies to navigation between comments in a document. The behavior is now much more in line with common text processors such as Microsoft Word.

Breaking Changes 🔥

Migrate the database

It’s a simple/fast migration with no expected data losses.

# run grunt migrate to update to the newest database scheme
# migration - 166-add-media-library-state.js
#   add row state to the table media_library_entries
livingdocs-server migrate up

Remove support for callbacks in multiple server API’s 🔥

We removed Callback support in several server API’s as we noticed many bugs originating from mixing callbacks and promises. So we continue to phase out callbacks in all APIs. In the next releases we will continue with the removal of callbacks in server API’s. All server API’s already support Promises, therefore you can prepare the downstream migration from Callbacks to Promises.

  • 🔥 remove callback support for designLoaderApi (server.features.api('li-design-loader')) functions. Only promise based calls are supported
  • 🔥 remove callback support for registrationApi (server.features.api('li-registration')) functions. Only promise based calls are supported
  • 🔥 remove callback support for documentRelationApi (server.features.api('li-document-relations')) functions. Only promise based calls are supported
  • 🔥 remove callback support for desknetIntegrationApi (server.features.api('li-desknet-integration')) functions. Only promise based calls are supported
  • 🔥 remove callback support for documentCopyApi (server.features.api('li-document-copy')) functions. Only promise based calls are supported
  • 🔥 remove callback support for designStatsApi (server.features.api('li-design-stats')) functions. Only promise based calls are supported
  • 🔥 remove callback support for tasksApi (server.features.api('li-tasks')) functions. Only promise based calls are supported
  • 🔥 remove callback support for designsApi (server.features.api('li-designs')) functions. Only promise based calls are supported
  • 🔥 remove callback support for cacheApi (server.features.api('li-cache')) functions. Only promise based calls are supported
  • 🔥 remove callback support for migrationApi (server.features.api('li-migrations')) functions. Only promise based calls are supported
  • 🔥 remove callback support for emailApi (server.features.api('li-emails')) functions. Only promise based calls are supported
  • 🔥 remove callback support for routingApi (server.features.api('li-routing')) functions. Only promise based calls are supported

Example how to migrate

Search e.g. for li-design-loader and replace all your code from a callback to a promise based approach (async/await). Please also consider your tests.

// from
const designLoaderApi = liServer.features.api('li-design-loader')
  version: req.params.version,
}, (err, designConfig) => {
  if (err) return callback(err)
  // ...

// to
const designLoaderApi = liServer.features.api('li-design-loader')
const designConfig = await designLoaderApi.loadConfig({
  version: req.params.version,

If you have a lot of code with callbacks and it’s difficult to migrate everything in one step, you can use Livingdocs callbackify function to migrate the functions step by step. If you have transformed one function e.g. fetchTargetChannel into a promise based function and have a lot of callback functions which are calling fetchTargetChannel, you can use callbackify. callbackify transforms a promise into a callback.

// original function with callback
function fetchTargetChannel (projectId, callback) {
  const projectApi = liServer.features.api('li-projects').project
  projectApi.getProject(projectId, (err, project) => {
    if (err) return callback(err)
    const targetChannel = project.channels.find(
      (channel) => channel.channelHandle === AUTHORS_CHANNEL_HANDLE)
    callback(null, targetChannel)

// step 1 - transform the function into a promise, but callbackify the function as long as it's called by other callback functions
const callbackify = require('@livingdocs/server/exports').callbackify

async function fetchTargetChannel (projectId, callback) {
  // add this line to callbackify the promise
  if (callback) return callbackify.method(fetchTargetChannel, callback, [projectId])

  const projectApi = liServer.features.api('li-projects').project
  const project = await projectApi.getProject(projectId)
  const targetChannel = project.channels.find(
    (channel) => channel.channelHandle === AUTHORS_CHANNEL_HANDLE)
  return targetChannel

// step 2 - remove callbackify when everything is transformed into promises properly
async function fetchTargetChannel (projectId) {
  const projectApi = liServer.features.api('li-projects').project
  const project = await projectApi.getProject(projectId)
  const targetChannel = project.channels.find(
    (channel) => channel.channelHandle === AUTHORS_CHANNEL_HANDLE)
  return targetChannel


Do not support callbacks in includes anymore (removed rendering.function) 🔥

🔥 removed rendering.function (callback) support in includes. Use rendering.render (promise) instead. (Tip: search for rendering: { to find potentially affected functions)

References: Server PR

Remove support for worker-farm and worker-nodes strategies 🔥

There is no worker strategy available and settable anymore for the migration and render pipeline feature. All migrations are computed in the same process (formerly known as in-process strategy)

🔥 You can’t set a worker strategy anymore. Remove all server config settings at migration.worker_strategy and render_pipeline.worker_strategy

References: Server PR

Upgrade from Webpack 4 to Webpack 5 🔥

In case you have a karma test setup (if karma.js file is present in your project root), you’ll need to update the configurations. istanbul-instrumenter-loader got replaced by babel-plugin-istanbul and karma-coverage.

Please remove all webpack dependencies and install the new versions for it:

npm uninstall webpack karma-webpack karma-coverage-istanbul-reporter
npm install --save-dev webpack@latest karma-webpack@latest karma-coverage@latest

Then change the coverage reporter in the karma.js configuration:

- const webpackRules = webpackConfig.module.rules
- webpackRules.unshift({
-   test: /\.(js)$/,
-   exclude: /(\/(node_modules|test|vendor|dist|bundle|coverage|config)\/|\.spec\.js$)/,
-   use: {
-    loader: 'istanbul-instrumenter-loader',
-     options: {
-       esModules: true
-     }
-   }
- })

     plugins: [
-       'karma-coverage-istanbul-reporter',
+       'karma-coverage',

-     coverageIstanbulReporter: {
-        dir: './coverage',
-        reports: ['lcov']
+     coverageReporter: {
+       dir: 'coverage',
+       reporters: [
+         {type: 'lcov', subdir: 'lcov'},
+         {type: 'text-summary', subdir: '.'}
+       ]

-     reporters: ['super-dots', 'mocha', 'coverage-istanbul']
+     reporters: ['super-dots', 'mocha', 'coverage']

References: Editor PR

Dashboard Card Configuration 🔥

🔥 Removed project config editorSettings.mediaLibrary.dashboard.displayFilters. Move editorSettings.mediaLibrary.dashboard.displayFilters config to mediaType.editor.dashboard.displayFilters on the relevant mediaType configs.

🔥 Media Type Dashboard configuration

If you configure both mediaType.editor.dashboard (the dashboard opened in modals and through the document editing toolbar) and mediaType.editor.managementDashboard (the dashboard opened through the main navigation), the config used for the managementDashboard is compiled from the dashboard config and the managementDashboard config by overwriting on a top-level property level. Before, if managementDashboard was configured, the dashboard configuration was completely ignored.


// behaviour before this change: management dashboard would not contain a displayFilter
// behaviour after this change: management dashboard contains the displayFilter configured in dashboard
mediaType.editor: {
  dashboard: {
    displayFilters: [{filterName: 'myFilter'}]
  managementDashboard: {
    baseFilters: [{filterName: 'myBaseFilter'}]

To get the old behavior, configure like this:

mediaType.editor: {
  dashboard: {
    displayFilters: [{filterName: 'myFilter'}]
  managementDashboard: {
    baseFilters: [{filterName: 'myBaseFilter'}],
    displayFilters: []

References: Editor PR

Remove File Feature 🔥

  • 🔥 Removed core feature li-files
  • 🔥 Removed server API liServer.features.api('li-files')
  • 🔥 Removed editing API /files/upload, use POST /media-library/upload-file instead

References: Server PR

Always activate copy component feature 🔥

🔥 Remove editor config copy.componentCopyEnabled. The setting has no effect anymore, component copy is always enabled.

References: Editor PR

Show insert component filter if there are more than 8 components 🔥

🔥 Remove editor config app.editor.insertPanel.useAdvancedComponentGroups. The setting has no effect anymore. The filter on the insert panel is always shown if there are more than 8 components.

References: Editor PR

Data-Migration: remove callback support 🔥

🔥 The callback-based function migrate has been removed for a file data-migration. Use the promise based function migrateAsync instead.

References: Server PR

Fix JSON patch operation of MediaLibraryEntries 🔥

We had some invalid data structures when an asset in the media library got replaced.

Required action for downstreams 🔥

In case you had the replaceAsset functionality enabled, you might want to fix invalid data structures. To check whether there are existing archived assets, please execute that query:

FROM media_library_entries
WHERE data->'archivedAssets' IS NOT NULL;

In case you have broken assets, please execute the following migration. We didn’t put it into a new migration as the migration could block for too long.

CREATE OR REPLACE FUNCTION li_jsonb_extract_nested_image_objects(val jsonb, obj jsonb)
LANGUAGE plpgsql
AS $$
  key text;
  IF jsonb_typeof(val) != 'object' THEN RETURN obj; END IF;
  IF val->'key' IS NOT NULL THEN RETURN obj || jsonb_build_object(val->>'key', val); END IF;
  FOR key IN SELECT * FROM jsonb_object_keys(val) LOOP
    obj := li_jsonb_extract_nested_image_objects(val->key, obj);
  RETURN obj;

UPDATE media_library_entries
SET data = data || jsonb_build_object('archivedAssets', li_jsonb_extract_nested_image_objects(data->'archivedAssets', '{}'))
WHERE data->'archivedAssets' IS NOT NULL;

References: Server PR

Typography Cleanup 🔥

If you have any custom UI in your downstream that makes use of any of the following you want to read this:


  • .alpha, .beta, .gamma, .delta, .epsilon, .zeta
  • .smallprint, .milli, .micro, .dense
  • .text--milli, .text--micro
  • .text--black

SCSS variabales / mixins:

  • $base-font-size, $base-line-height, $h1-size, $h2-size, $h3-size, $h4-size, $h5-size, $h6-size, $milli-size, $micro-size, $line-height-ratio
  • font-size($font-size, $line-height: true)
  • headings($from: 1, $to: 6)

You are highly encouraged to refactor your markup / custom stylesheets to not use these things anymore. In order to ease that process, there is a file you can @import in your custom SCSS to get support for the mentioned classes and variables: In the SCSS file you have configured as CUSTOM_STYLE_PATH_BEFORE or CUSTOM_STYLE_PATH_AFTER add this line at the top:

@import "~styles/backwards-compatibiliy/release-2021-09.scss";

This will define the removed classes, variables and mixins within your SCSS file tree. Your Sass files will compile again and your custom UI will most probably look just fine. From there on you can refactor your code and remove the @import "~styles/backwards-compatibiliy/release-2021-09.scss"; after you are done. We will keep this file around for some time, but it will eventually get removed.

If you have any questions about this, don’t hesitate to contact us. You can also consult the styleguide to read more about newly introduced styles and how they look.

References: Editor PR


Document List Card Extension

editor config are deprecated in favor of project config editorSettings.documentList.dashboard:

🔧 Deprecate editor config app.filters.documentListList and projectConfig settings.lists. Move config to server config editorSettings.documentLists.dashboard


APIs 🎁

Server API

Because we want to get rid of callbacks on the server, we provide helper functions for downstreams.

🎁 Provide promisify helper for downstreams -> require('@livingdocs/server/exports').promisify 🎁 Provide callbackify helper for downstreams -> require('@livingdocs/server/exports').callbackify


Server CLI

livingdocs-server add-design

  • renamed livingdocs-server add-design to livingdocs-server design-add (deprecate add-design)


livingdocs-server add-design

  • add task to set current/active design livingdocs-server design-set-active --project=<project handle> --design-version=<design-version>


Other Changes






Livingdocs Server Patches

  • v154.0.45: fix: Support a accessTokenCacheSize config to increase the token cache size on heavy used servers
  • v154.0.44: fix: return revision also in case metadata_id is null
  • v154.0.43: fix(elasticsearch): Fix opensearch compatibility
  • v154.0.42: fix: be able to start the server without hugo config
  • v154.0.41: fix: add more information to the remoteError from airship
  • v154.0.40: fix: trigger a new version for npm
  • v154.0.39: fix(print): Add hugo image URLs to export data
  • v154.0.38: fix: Fix backfilling of the content_type_id column on the document_publication_events table
  • v154.0.37: fix: Upgrade to pino v7
  • v154.0.36: chore: check type of assets
  • v154.0.35: fix: use the updated release-notes-patch repo
  • v154.0.34: fix(postgres): Fix support for postgres 14 that has some query logic change (maybe accidental)
  • v154.0.33: chore(build): Upgrade to @livingdocs/secure-password
  • v154.0.32: test: update tests for comments mentioning
  • v154.0.31: fix(imagemagick): Support imagemagick uploads again
  • v154.0.29: fix(publication-hooks): Pass the correct documentType, add the contentType additionally to the documentType
  • v154.0.28: fix(group-projection): do nothing on an insert conflict
  • v154.0.27: fix(telemetry): Fix opentelemetry span creation in the job queue
  • v154.0.26: chore: remove content-disposition and let the editor decide how to get the file
  • v154.0.25: test(push notifications): Add tests for airship
  • v154.0.24: fix(elasticsearch): Get rid "./*": "./*" deprecation message coming from a package.json require of elasticsearch
  • v154.0.23: test: adapt tests
  • v154.0.22: chore(blob-store): Isolate the computeKey tests more
  • v154.0.21: fix(project-config): Lower the minimum metadata handle length to two characters
  • v154.0.20: fix(pusher): Enforce pusher to use tls
  • v154.0.19: fix(blob-store): Fix Cloudinary storage
  • v154.0.18: perf(imports): Skip daily count request if there’s no import limit configured
  • v154.0.17: fix(tracing): Properly retrieve the traceId from a span of http requests
  • v154.0.16: fix(framework): update to 21.0.5
  • v154.0.15: openid-connect: Support HTTP_PROXY environment variable in call

Livingdocs Editor Patches

  • v72.13.49: fix(restore-metadata): when metadata not set log an error

  • v72.13.48: fix(comments): on page the comments can be disabled

  • v72.13.47: fix(imatrics-form): add margin-bottom

  • v72.13.46: fix: deduplicate concepts

  • v72.13.45: test: add the file to the dataTransfer object from hugo

  • v72.13.44: fix: upgrade framework to 21.0.7

  • v72.13.43: fix: show card actions in inbox and document search

  • v72.13.42: fix(top banner): Max width mobile

  • v72.13.41: fix: design improvements for document-list and soft-lock

  • v72.13.40: fix: Upgrade @livingdocs/framework to fix setting link attributes in editable.js

  • v72.13.39: fix(li-dialog): Close on click outside

  • v72.13.38: fix(includes): correctly update directive params when multiple services are used with vue components UIs

  • v72.13.37: fix: initial crop with setImage

  • v72.13.36: fix(metadata): Spacing

  • v72.13.35: fix: show correct placeholder on creating list

  • v72.13.34: fix: use the updated release-notes-patch repo

  • v72.13.33: fix(media-library): correctly select an additional language in multilanguage

  • v72.13.32: fix(media upload service): Remove component on failed/canceled upload

  • v72.13.31: fix(component card): Fix layout for images with height > width

  • v72.13.30: fix: send context to server

  • v72.13.29: fix(search filter bar): Update show/hide when items are added asynchronously

  • v72.13.28: fix: rename Ignore to Off

  • v72.13.26: fix(user): Escape user initials that would break the avatar svg

  • v72.13.25: fix: move file downloader to it’s own helper and improve logic

  • v72.13.24: fix(media-library): Hide “set” button for non-translatable assets

  • v72.13.23: fix: Trigger another release as the npm publish failed

  • v72.13.22: fix(feature-detection): Cancel script execution once we detect that a browser is not supported

  • v72.13.21: fix: Handle properties with default values in li-form-select

  • v72.13.20: fix(clipboard): Cancel drag event when clipboard is emptied

  • v72.13.19: fix: change toolbar max offset to fix overlapping action bar issue

  • v72.13.18: fix(framework): update to 21.0.5

  • v72.13.17: document lists: Compute data when adding documents

    Icon Legend

    • Breaking changes: 🔥
    • Feature: 🎁
    • Bugfix: 🪲
    • Chore: 🔧