Server Hooks
allow you to
- Influence the publication process
- Influence the document rendering
- Get notified about list updates
Use Cases of a Server Hook
- Modify a document
- Abort the publish process and return an error to the server
- Notify other systems
Alternatives to Server Hooks
- Metadata Plugins which can modify single metadata fields
- Events (fire and forget)
2 Types of Server Hooks
- Project specific hooks: register as many as you need, executed in the order they got registered
- Server wide hooks: run on every project, but are only allowed for publication hooks
Publication Hooks
With publication hooks you can influence the Document Publication Lifecycle
. E.g. manipulate the content before a publish happens.
Process and Hooks Overview
Name | Supported in | Editor Feedback | Added in | Removed in |
---|---|---|---|---|
preparePublishHookAsync | Instant Publish | ✅ | release-2022-03 | |
prepublishHookAsync | Instant Publish | ✅ | release-2022-03 | |
publishHookAsync | Instant Publish | ✅ | release-2022-03 | |
postPublishHookAsync | Instant Publish | release-2022-03 | ||
unpublishHookAsync | Instant Publish | ✅ | release-2017-01 | release-2022-05 |
postUnpublishHookAsync | Instant Publish | ✅ | release-2022-05 |
Register a Publication Hook
Two ways of registering a Publication Hook
registerPublicationHooks
: Register as many hooks as you need, executed in the order they got registeredregisterPublicationServerHooks
: Hooks are executed on all projects. These hooks run before project specific ones.
API of Publication Hooks
preparePublishHookAsync
:({documentVersion}) {return}
postPublishHookAsync
:({documentVersion}) {return}
preUnpublishHookAsync
:({documentVersion}) {return}
postUnpublishHookAsync
:({documentVersion}) {return}
- Removed in:
release-2022-05
:unpublishHookAsync
:({documentType, documentVersion}) {return}
Example
const appConfig = require('./conf')
const liServer = require('@livingdocs/server')(appConfig)
liServer.registerInitializedHook(async () => {
liServer.features.api('li-documents').registerPublicationServerHooks({
async preparePublishHookAsync ({documentVersion}) { return }
})
liServer.features.api('li-documents').registerPublicationHooks({
projectHandle: 'your-awesome-project',
channelHandle: 'default',
// Added in: release-2022-03
async preparePublishHookAsync ({documentVersion}) { return },
// Added in: release-2022-03
async postPublishHookAsync ({documentVersion}) {
liServer.log.info(`postPublishHookAsync called for documentType: ${documentVersion.documentType}!`)
liServer.log.debug({documentVersion: documentVersion})
return
},
// Removed in: release-2022-05
async unpublishHookAsync ({documentVersion}) {
liServer.log.info(`unpublishHookAsync called for documentType: ${documentVersion.documentType}!`)
liServer.log.debug({documentVersion})
return
}
// Added in: release-2022-05
async preUnpublishHookAsync ({documentVersion}) {
liServer.log.info(`preUnpublishHookAsync called for documentType: ${documentVersion.documentType}!`)
liServer.log.debug({documentVersion})
return
},
// Added in: release-2022-05
async postUnpublishHookAsync ({documentVersion}) {
liServer.log.info(`postUnpublishHookAsync called for documentType: ${documentVersion.documentType}!`)
liServer.log.debug({documentVersion})
return
}
})
})
preparePublishHookAsync()
The preparePublishHookAsync
hook allows modifications of the DocumentVersion before a document will be published.
Use Cases
- Modify document (DocumentVersion)
- Error feedback with throwing an error (document will not be published)
Example
async preparePublishHookAsync ({documentVersion}) {
if (documentVersion.metadata.title === 'Let me pass') {
// modify the document here
return
} else {
// Example Validation Error for a metadata property
const err = new Error('Metadata Errors')
err.name = 'MetadataValidationErrors'
err.status = 400
err.invalidMetadata = [{
metadataProperty: 'title'
message: 'Invalid Title',
}]
throw err
}
}
postPublishHookAsync
The postPublishHookAsync
hook will be called after a document has been published. Any change to the DocumentVersion has no effect. A use case for this hook is to inform remote systems about the publication of a document.
Use Cases
- Notify other systems
Example
async postPublishHookAsync ({documentVersion}) {
axios.post(`https://my-remote-service.com/publish`,
{
projectId: documentVersion.projectId,
documentId: documentVersion.id,
publicationId: documentVersion.getLastPublicationId(),
})
}
unpublishHookAsync
Removed in: release-2022-05
The unpublishHookAsync
hook will be called after a document has been unpublished. Any change to the DocumentVersion has no effect. A use case for this hook is to inform remote systems about the unpublication of a document.
async unpublishHookAsync ({documentVersion}) {...}
postUnpublishHookAsync
The postUnpublishHookAsync
hook will be called after a document has been unpublished or a published document gets deleted. Any change to the DocumentVersion has no effect. A use case for this hook is to inform remote systems about the unpublish of a document.
Use Cases
- Notify other systems
Example
async postUnpublishHookAsync ({documentVersion}) {
axios.post(`https://my-remote-service.com/unpublish`,
{
projectId: documentVersion.projectId,
documentId: documentVersion.id
})
}
preUnpublishHookAsync
The preUnpublishHookAsync
hook allows modifications of the DocumentVersion before a document will be unpublished.
Use Cases
- Modify document (DocumentVersion)
- Error feedback with throwing an error (document will not be unpublished)
Example
async preUnpublishHookAsync ({documentVersion}) {
if (documentVersion.metadata.title === 'Let me pass') {
// modify the document here
return
} else {
// Example Validation Error for a metadata property
const err = new Error('Metadata Errors')
err.name = 'MetadataValidationErrors'
err.status = 400
err.invalidMetadata = [{
metadataProperty: 'title'
message: 'Document cannot be unpublished because ...',
}]
throw err
}
}
Render Hooks
The beforeRenderHookAsync
is called right before a document gets rendered.
API:
beforeRenderHookAsync
:({documentType, rendition, renditionNames})
Example:
const appConfig = require('./conf')
const liServer = require('@livingdocs/server')(appConfig)
liServer.registerInitializedHook(async () => {
liServer.features.api('li-render-pipeline').registerRenderHooks({
projectHandle: 'your-interesting-project',
channelHandle: 'some-channel',
async beforeRenderHookAsync ({contentType, rendition, renditionNames}) {
if (['interview', 'biography'].includes(contentType)) {
liServer.log.info("We're about to render something about somebody!")
// do something with the rendition:
const livingdoc = rendition.getLivingdoc()
const galleryTeasers = livingdoc.componentTree.find('gallery-teaser')
return extendGalleryTeasers(galleryTeasers, rendition)
}
}
})
})
List Hooks
registerListHooks()
There is one hook for the document-lists
feature. The hook can be registered through registerListHooks()
.
Example:
const appConfig = require('./conf')
const liServer = require('@livingdocs/server')(appConfig)
liServer.registerInitializedHook(async () => {
liServer.features.api('li-document-lists').registerListHooks({
projectHandle: 'your-interesting-project',
channelHandle: 'some-channel',
listUpdateHookAsync ({projectId, listId, remove, add}) {
console.info(
`The list with id '${listId}' in the project '${projectId}' has changes.`,
`removing ${remove.length} things, adding ${add.length} things.`
)
return
}
})
})
listUpdateHookAsync()
The payload described here has a custom format where it gives the added and
removed documentId
s. An example how to use that hook would be to have Elasticsearch
reindex the documents which got added/removed from a list.
listUpdateHookAsync: ({
trx,
eventSource, // which api method triggers the hook 'updateList' or `removeDocumentFromList`
projectId,
channelId,
listId,
remove: [30, 199],
add: [{id: 77, order: 12}]
})