Settings

The settings contain general feature settings for your project, in particular:

  • language settings
  • copy configurations (e.g. copy web to print)
  • plugin configurations
  • webhooks
  • include rendering

An example:

settings: {
  handle: 'magazine',
  languages: {
    available: [
      {label: 'German', locale: 'de'},
      {label: 'English', locale: 'en'}
    ],
    defaultLanguage: {
      label: 'German',
      locale: 'de'
    },
    requiredOnCreation: true,
    translationWorkflow: true // shows "translate" icon on the toolbar
  },
  integrations: {
    // Kordiam Global Integration
    // For the legacy Kordiam Platform Integration see `settings.kordiam`
    // For further details see the "Kordiam Global Integration" guide
    kordiam: {
      enabled: true,
      credentials: {
        clientId: 'my-kordiam-client-id',
        clientSecret: {
          $secretRef: {
            name: 'my-kordiam-secret'
          }
        }
      },
      apiEndpoint: 'https://kordiam.app', // Optional, Defaults to 'https://kordiam.app'
      createDocumentFunction: 'myCreateDocumentFunction',
      createElementFunction: 'myCreateElementFunction',
      incomingElementToDocumentCommandsFunction: 'myIncomingElementToDocumentCommandsFunction',
      outgoingDocumentToElementFunction: 'myOutgoingDocumentToElementFunction',
      outgoingDocumentToElementEventSources: ['publish', 'unpublish', 'update'] // Default: ['publish', 'unpublish']
    },
    // NOTE: imatrics uses our secure $secretRef method that will be used by all plugins in future
    imatrics: {
      enabled: true,
      apiEndpoint: 'https://some.url.com',
      user: 'yourUser',
      key: {
        $secretRef: {
          name: 'imatrics-20211027'
        }
      },
      testEnvironment: false, // Added in: release-2024-09
      minchars: 20,
      language: {
        name: 'German',
        value: 'de'
      }
    },
    retresco: {
      enabled: true,
      apiEndpoint: 'https://<subdomain>.rtrsupport.de/',
      username: '<username>',
      password: {
        $secretRef: {
          name: '<secret-name>'
        }
      },
      enableLiveAnalysis: true,
      // The component.directive combinations used to extract specific strings.
      // The first match will be used, but multiple values can be provided so that many
      // content types can be supported.
      titleMatches: ['header.title'],
      supertitleMatches: ['header.catchline'],
      teaserMatches: ['header.lead', 'p.text'],
      maxTextLength: 100
    },
    comyan: {
      enabled: true,
      buttonLabel: 'open comyan',
      targetMediaType: 'image', // mandatory with release-2025-01
      mediaSystem: {
        additionalApiQueryString: 'foo=bar' // in case of proxy use, set `svcURLFormat=api`
        credentials: {
          username: 'user',
          password: {
            $secretRef: {
              name: '<secret-name>'
            }
          }
        },
        baseUrl: 'https://example.com/MediaSystem',
      },
      storageBaseUrl: 'https://storage.proxy.com/' // set this if comyan serves your images through a proxy
    },
    googleVision: {
      enabled: true,
      credentials: {
        type: 'service_account',
        project_id: 'your-project-123',
        private_key_id: '123',
        private_key: '-----BEGIN PRIVATEfoo bar-----END PRIVATE KEY-----\n',
        client_email: 'vision-api-dev@your-project-123.iam.gserviceaccount.com',
        client_id: '123',
        auth_uri: 'https://accounts.google.com/o/oauth2/auth',
        token_uri: 'https://oauth2.googleapis.com/token',
        auth_provider_x509_cert_url: 'https://www.googleapis.com/oauth2/v1/certs',
        client_x509_cert_url: 'https://www.googleapis.com/robot/v1/metadata/x509/vision-api-dev%40your-project-123.iam.gserviceaccount.com'
      },
      confidenceCliff: 0.7,
      language: {
        label: 'German',
        locale: 'de'
      },
      shouldDetectWeb: true
    },
    peiq: {
      enabled: true,
      targetMediaType: 'image',
      apiEndpoint: 'https://example.peiq.cloud',
      apiToken: {
        $secretRef: {
          name: '<secret-name>'
        }
      }
    }
  },

  // Kordiam Platform Integration (legacy)
  // For the new Kordiam Global Integration see `settings.integrations.kordiam`
  // For further details see the "Kordiam Platform Integration" guide
  kordiam: {
    enabled: true,
    credentials: {
      clientId: 'my-kordiam-client-id',
      clientSecret: {
        $secretRef: {
          name: 'my-kordiam-secret-ref'
        }
      }
    },
    titlePath: 'slug', // Default: "title"
    publicationStatus: {
      sync: false,
      publishedStatus: 5, // Deprecated
      unpublishedStatus: 1 // Deprecated
    },
    contentTypes: {
      standard: 'regular', // Required. The default content type to create.
      // Configuration of additional content types that can be created
      source: 'publication.type.name',
      mapping: [
        {
          source: 'article', // publication.type.name value
          target: 'regular' // Livingdocs content type handle
        }
      ]
    }
  },

  includeServices: [
    {
      handle: 'echo-service',
      rendering: {
        type: 'remote',
        url: 'http://localhost:3000',
        timeout: 3000
      },
      config: {
        foo: 'bar'
      },
      paramsSchema: [
        {
          type: 'li-text',
          handle: 'json',
          config: {
              maxLength: 200
          },
          ui: {
            config: {
              label: 'Json',
              placeholder: '{ "foo": "bar" }'
            }
          }
        }
      ],
      defaultParams: {
        title: 'defaultTitle'
      },
      blockEditorInteraction: 'initial'
    }
  ],

  webhooks: {
    active: true,
    configurations: [
      {
        active: true,
        handle: 'publish-hook',
        label: 'Publish Hook',
        description: '',
        url: 'https://foo.bar.com/my-webhook',
        secret: 'foo-bar',
        events: [{
          name: 'document.build',
          conditions: {
            deliveryHandles: ['web']
          }
        }]
      }
    ]
  },

  copy: [{
    source: {
      channelHandle: 'web',
      contentType: 'regular'
    },
    targets: [
      {
        channelHandle: 'web',
        contentType: 'kolumne',
        metadata: {
          map: [
            'title',
            'headline',
            'authors',
            'description',
            'teaserImage',
            'publishDate'
          ]
        },
        componentConversions: [
          {
            match: 'article-container',
            exclude: true
          },
          {
            match: 'head',
            result: [
              {
                component: 'catchline',
                directives: {title: {takeFrom: 'flag'}}
              },
              {
                component: 'headline',
                directives: {title: {takeFrom: 'title'}}
              }
            ]
          }
        ]
      }
    ]
  }],

  // This setting can be overwritten in the `contentType` config
  imageSourcePolicy: [
    {provider: 'upload', enabled: true},
    {provider: 'hugo', enabled: false},
    {
      provider: 'url',
      enabled: true,
      hosts: ['//pixabay.com']
    }
  ],

  // This setting can be overwritten in the `contentType` config
  videoSourcePolicy: [
    {provider: 'upload', enabled: false}
  ],

  // setup the push notification feature if applicable
  // for this to work you also must:
  // 1. configure firebase in the server-wide `pushNotifications` configuration
  // 2. have a metadata field with the (exact) name `pushNotifications`
  // IMPORTANT: value can not contain more than three comma separated entries
  // see firebase conditions for more details.
  pushNotifications: {
    topics: [
      {
        handle: 'breaking-news',
        label: 'Breaking News',
        value: 'ios_de_breakingnews, android_de_breakingnews'
      },
      {
        handle: 'sport',
        label: 'Sport',
        value: 'ios_de_sports, android_de_sports'
      }
    ]
  },
  
  tracking: {
    enabled: true
  },

  editMode: 'default'
}

Languages / Translations

Livingdocs allows you to define documents in multiple languages as well as integrating a translation workflow to translate a document from one language to the other.

  • languages defines which languages are available for documents.
  • defaultLanguage set a default language for new documents created.
  • requiredOnCreation: true flag, forces the user to choose a language before creating a document.
  • translationWorkflow: true flag, enables the translation tools in Livingdocs (shows “translate” icon on the toolbar). This feature is only available for documents and pages (but not for a data-record).

If you activate the translation feature, then the language metadata plugin on a document will contain a groupId. You can use this in the languageGroupId parameter of the Public API - Search Publications call to retrieve all translations of a document.

If you want to use a computer assisted translation (CAT) tool to translate livingdocs content you can install our li2xliff library to convert documents to XLIFF for translation and list to server events to update content. You can follow our in-depth guide here

Integrations

In general all integrations are under the integrations key. We still have some legacy markup where the integration is directly on the root (kordiam), but this will be moved in the future.

Available plugins are:

  • Kordiam (planning)
  • iMatrics (text auto-tagging)
  • Retresco (text auto-tagging)
  • Google Vision (image auto-tagging)
  • Comyan (external image storage)
  • PEIQ (external image storage)

Imatrics

Imatrics already uses our new secure secrets feature that will in future be used by all plugins. If you are using a seeding process, e.g. via the CLI then you need to manually generate a key and reference it in your project config:

// NOTE: you need to choose a unique name, an easy way is to append the current date to the string 'imatrics-' as done below
npx livingdocs-server secret-add --project=<handle> --name=imatrics-20211027 --value=secretvalue
// -> this adds a new secret 'imatrics-20211027' to our encrypted secret store

After you have created the secret in our system you need to reference it from your project config:

imatrics: {
  ...
  key: {
    $secretRef: {
      name: 'imatrics-20211027'
    }
  },
  ...

NOTE: for this to work, the name of the key needs to be unique for every update of the key. You can e.g. append the current date to ensure this or use any other form of unique string appending.

Retresco

As with iMatrics above, the secure secrets feature is used to store the password for the Basic authentication used by the Retresco API. Please note that the integration must also be enabled in the server config.

Please see the Retresco integration guide for details on how to setup the integration.

Comyan

As with iMatrics above, the secure secrets feature is used to store the password for the Basic authentication used by the Comyan API. Please note that the integration must also be enabled in the server config.

PEIQ

As with iMatrics above, the secure secrets feature is used to store the token for the bearer authentication used by the PEIQ API. Please note that the integration must also be enabled in the server config.

Includes

Includes allow you to render parts of your document from a remote location. For example you can create a component called highcharts that uses a service directive in the HTML lik <div doc-include="highcharts"></div>. Everything inside that div will be rendered from the remote service “highcharts”.

The service itself is configured in the config (see example at the top). The handle is the service name that is also used in the doc-include directive of the component. In the rendering object you define the URL of your micro-service where the rendering is done as well as a timeout to indicate when it should fail. blockEditorInteraction can be used to alter the interactivity of the include within the editor. Not setting this value, or selecting “No” in the UI, will result in the default experience of the include being rendered and interactive within the editor. Passing a value of 'always' will block all interaction with the include in the editor, and using 'initial' will block the first click only. The paramsSchema generates a form for the editor in the sidebar when a respective component is selected. Editors can then enter (dynamic) configs that are sent to your micro-service. As of now the only supported form types are li-boolean and li-text, i.e. a checkbox and a text input. The defaultParams object allows you to define defaults for the dynamic params of an include. Dynamic params are set by the user over a UI in the editor. The config parameter can contain arbitrary (fixed) data that are sent to your micro-service.

Webhooks

Webhooks allow you to receive notifications for events from within Livingdocs and react to them.

For details on the parameters see our Webhooks documentation.

Copy Configuration

The copy configuration allows you to define the mapping of what content-types can be copied to what other content-types and how the metadata is mapped. A common example is the copy of a web article to the print article.

It’s an array of copy definitions. Each definition first defines the source, i.e. the content-type that is copied (e.g. web article) and then the possible targets (e.g. print article), which metadata fields should be mapped from source to target and a set of rules on how to convert the components and directives (exlcude, simple copy or one to many).

For the enterprise version we also have a detailed guide for copy and transform workflows.

Product Usage Analytics

Added in: release-2025-01

In order to drive the continuous improvement of Livingdocs in a user-centered way, usage data and patterns can greatly improve our perspective and point us to pain points worth interviewing users about. Livingdocs tracks certain user interactions along with some context data and sends it to PostHog, a cloud service to store, process and visualize such data.

  • We do not transmit any user names, email addresses, IP addresses, location data, browser details
  • User Ids are transmitted in a hashed form and are never used to link back to an actual person
  • Only as much data as necessary is transmitted to gain valuable insights
  • User inputs (e.g. search terms) are not transmitted in clear text, it’s either hashed or a derived information
  • We do not gather heatmap data, nor are we using features that record the screen of a user

Tracked events (simplified)

  • dashboard screen opened (display filters, search info)
  • dashboard screen left (display filters, search info)
  • dashboard result selected (display filters, search info, result index, browser target)
  • added, changed, removed, reset display filters or search term (display filters, search info)

How to opt-in

Livingdocs has to add the corresponding customerId to the allow-list which also requires a patch version to be deployed afterward. Additionally, the tracking needs to be enabled per project:

{
  tracking: {
    enabled: true
  }
}