2.4.8
The fs-tpp-api/snap.js must be used in the markup of the third party app. There is no specific position defined, but if you want to use the API of window.TPP_SNAP, your own script must be loaded after the inclusion fs-tpp-api/snap.js, of course. You could define the optional [data-firstspirit-origin] attribute to ensure that the first(!) postMessage (aka. TPP handshake) is only committed to the real ContentCreator frame.
<script src="path/to/fs-tpp-api/snap.js" data-firstspirit-origin="http://firstspirit:8000"></script>
All you have to do is use the [data-preview-id] in your markup. This library automatically decorates those containers with the known borders and buttons. Content changes will force a refresh of the current page, so there is no need to implement in JS!
But of course you are able to do this by using the framework! That's what the docs are for...
As an example, a FirstSpirit Template delivers some HTML Markup, which you would like to update in-place in case of changes. You could use the [data-on-tpp-change] attribute which is a representation of the onContentChange~Handler. Or you use it directly, like this:
TPP_SNAP.onContentChange(($node, previewId, content) => {
if ($node.matches('.content') && content !== null) {
$node.innerHTML = content;
return false;
}
})
You can register multiple handlers. Further processing of the event is stopped after the first handler returns a value !== undefined.
<div class="content"
data-preview-id="<previewId>"
data-on-tpp-change="if (content !== null) { this.innerHTML = content; return false; }">
</div>
To change the current preview element, you should use the method TPP_SNAP.setPreviewElement. This also ensures that the ContentCreator keeps track of the route change and the shown workflows are those of the current element:
TPP_SNAP.setPreviewElement(pageRefPreviewId);
Note that for navigating to another page, you should use the preview ID for its PageRef instead for the page itself.
For being able to open pages from within the ContentCreator (e.g. to use the search function) you have to
specify your own listener to handle this request. The method to use for this purpose is TPP_SNAP.onRequestPreviewElement.
A handler registered with onRequestPreviewElement
should implement the frontend specific way to route to the
requested page using the given preview id.
TPP_SNAP.onRequestPreviewElement(async (previewId) => {
// The following previewIdToPath function is just a placeholder! The mapping from previewId to route has
// to be defined project-dependent!
let path = previewIdToPath(previewId);
// The routing mechanism depends on the frontend as well, thus the "route" function also has to
// be understood as a placeholder!
if (path) return route(path);
// ... error handling (e.g. 404) ...
}
Nested components of input components such as FS_CATALOG
or FS_INDEX can be edited as well.
To enable the editing of such a component make sure to provide a custom preview id for both the nested component as well as
the parent component using the syntax #PARENT_COMPONENT_NAME
and #NESTED_COMPONENT_INDEX
as the following example shows.
<div data-preview-id="8326527a-60ff-49a0-bc66-91671d660249">
<ul data-preview-id="#pt_catalog">
<li data-preview-id="#0">This is content from the first entry.</li>
<li data-preview-id="#1">The second component does have different content.</li>
<li data-preview-id="#2">And of course, this is also different.</li>
</ul>
</div>
Notice that the presence of the preview id of the data provider that contains the FS_CATALOG component is needed. As long as the HTML complies with this structure the individual nested components will automatically provide buttons for interaction.
NOTE: This feature also works for multiple nested components (e.g. a Catalog$Card that is part of of a Index$Record). It must be noted that in such a scenario, the path to a nested component must always alternate between a component name and an index. Additionally the last part of the path must always be an index of a nested component.
NOTE: Currently only FS_INDEX components that use the DatasetDataAccessPlugin are supported.
Some input components support inline editing when enabled. All input components named in the FirstSpirit Online Documentation are supported.
To enable inline editing for such components a custom preview id needs to be provided as a data attribute on the related DOM node
using the syntax #INPUT_COMPONENT_NAME
.
The following example includes a DOM node that corresponds to a CMS_INPUT_TEXT
component with the name pt_headline
.
<div data-preview-id="8326527a-60ff-49a0-bc66-91671d660249">
<span data-preview-id="#pt_headline">
Our various services for sales, marketing and installation branches.
</span>
</div>
Notice that the presence of the preview id of a data provider that contains the editable input component is needed. As long as the HTML complies with this structure the component will automatically become inline editable.
The CaaS mode is mainly useful for scenarios where your web app uses the CaaS as the source for FirstSpirit content and your app can't render changed content dynamically (e.g. using the respective event handlers).
Whenever a re-rendering of the current page or the preview of an element was requested, this mode waits for the changed content to be present in the CaaS and then triggers the related handlers (onRerenderView~Handler or onRequestPreviewElement~Handler).
To enable the CaaS mode use the TPP_SNAP.enableCaasMode function.
TPP_SNAP.onInit(async (success) => {
if(success) {
const { previewCollectionUrl, apiKey } = {
previewCollectionUrl: "https://caas-host/my-tenant-id/f948bb48-4f6b-4a8a-b521-338c9d352f2b.preview.content",
apiKey: "9afa9e21-d02f-4836-9e55-111fcf6521a3"
}
TPP_SNAP.enableCaasMode(previewCollectionUrl, apiKey)
}
})
NOTE: When using the CaaS mode in combination with dynamically rendering changed content (e.g. using the onContentChange~Handler) make sure to avoid handling any content changes that should lead to a re-rendering of the current page.
NOTE: The CaaS mode requires a minimum version for the CaaS platform and CaaS Connect module, see TPP_SNAP.enableCaasMode for more information.
Button Name | Visibility | Requirement | Functionality |
---|---|---|---|
edit | Visible on elements of type PageRef , Page , Section , GCAPage , GCASection , Dataset and SectionReference . |
The current user has the permission to change the element's content. | This button opens a dialog which enables the user to edit the element's content. |
translate | Visible on elements of type PageRef , Page , Section , GCAPage , GCASection , Dataset and SectionReference being defined as multi-language. |
The current user has the permission to change the element's content. | The user may select a translation from the current to another possible language, which will bring up the respective translation dialog. |
metadata | Not visible by default; for this button to be shown, it should be overridden by the developer. | The current user has the permission to change the element's meta data. | On click, this buttons opens the meta data dialog for the current element. |
add-sibling-section | Only visible on sections. | The current user has the permission to change the page. | Opens a dialog for creating a new sibling section. |
add-child-section | Only visible on elements of type Page with one Body element as a child. |
The current user has the permission to add a new leaf to the current page. | Opens a dialog for creating a new section as a child of the page's Body element. |
add-child-section-body | Only visible on elements of type Body . |
The current user has the permission to add a new leaf to the current page. | Opens a dialog for creating a new section as a child of the body. |
workflows | Only visible on elements supporting a release whilst not using a custom workflow; also, if no workflow has already been started for the element, at least one must be allowed. | If visible, this button is always enabled. | The user has to select either a new workflow to start, if none is already, or the next transition to take. |
tpp-icon-delete | Not visible on elements of type Body or Media . |
If a workflow is enabled, only usable if the element is neither of type Section nor SectionReference and a deletion workflow is defined. If no workflow is used, the user has to be granted the deletion permission. |
The button does either start a delete workflow if workflows are used, otherwise it deletes the selected element directly. |
tpp-icon-crop-image | Only visible on elements of type Media . |
Only applicable if the user has the permission to change this element. | On click, opens a dialog which allows the user to crop the selected image. The default resolution used for this action is "ORIGINAL", but can be specified by the developer via setting the attribute data-tpp-context-image-resolution to res1, res2 in the DOM. |
bookmark | Only visible on elements of type Page , PageRef , Section , Dataset or SectionReference . |
If visible, this button is always enabled. | On click, the selected element is added to (or removed from) the project bookmarks which can be viewed in the ContentCreator toolbar. |
edit-component | Visible on components lying inside a "nested component" structure. | Usable if the current user has the permission to change the element's content. | On click, this button opens a dialog which enables the user to edit the selected component. |
create-component | Visible on catalog or index components inside a "nested component" structure. | Usable if the current user has the permission to add a new leaf to the page. | The user may select an applicable template for a subordinate component to be created; on click, the Create dialog is shown. |
delete-component | Visible on components lying inside a "nested component" structure. | Usable if the current user has the permission to delete the element. | On click, this button deletes the selected element after confirmation. |
Will be true, if the TPP handshake was successful and the connected ContentCreator does not use the new design.
(boolean)
Enables the CaaS mode for OCM. When a re-rendering of the current page or a preview of an element is requested, OCM automatically waits for the presence of the new content in the CaaS before triggering the respective handlers (onRerenderView~Handler or onRequestPreviewElement~Handler). Use this mode when the rendering of your app depends on fetching changed content from the CaaS. See CaaS mode description for more information.
It is recommended to enable the CaaS mode after the OCM initialization finished successfully by using the onInit~Handler. See CaaS mode description for an example.
NOTE: The CaaS mode depends on the current preview element. Make sure to set this element via TPP_SNAP.setPreviewElement.
NOTE: This feature uses the Change Stream API of the CaaS, thus requiring the platform version 3.0.3 (or later) and CaaS Connect module version 3.4.0 (or later).
Will be called after internal initialization (even if the handler is set after initialization).
(onInit~Handler)
TPP_SNAP.onInit(async (success) => console.log(`FirstSpirit Preview is ${success ? 'now' : 'NOT'} available!`))
Is triggered any time the content changes (or is deleted), on each Node that
was associated by the [data-preview-id]
attribute in the markup.
(onContentChange~Handler)
TPP_SNAP.onContentChange(($node, previewId, content) => $node.innerHTML = content))
Sometimes it's easier to rerender the complete view instead of implementing event handlers for anything that could happen on the FristSpirit side. Additional this is the common fallback, if the onContentChange~Handler is not handled by the frontend application.
If there is no listener defined, there is a fallback to location.reload()
!
NOTE: When using the CaaS mode the triggering of the provided handler is delayed to ensure that the related CaaS document is up-to-date. See CaaS mode description for more information.
(onRerenderView~Handler)
TPP_SNAP.onRerenderView(() => app.rerender());
Is triggered when the site's structure has been changed, including the deletion of elements that could possibly be contained in the navigation. In the last case, furthermore the Status of the deleted element is passed on to the event handler as an argument; this enables the handler to ignore events for specific elements (e.g. dataset deletion if datasets may not appear in the current project's navigation).
Note: Keep in mind that for all navigation changes caused by creating a page the onRequestPreviewElement~Handler will also be triggered meaning that routing or navigation logic is better placed within a onRequestPreviewElement~Handler to avoid duplication and multiple executions of that logic.
See also WE_API.Common.NavigationChangeListener.
(onNavigationChange~Handler)
Is triggered when the preview of an element was requested (e.g. by clicking on a search result or by creating a new page). The FrontEnd application should now translate the PreviewId to a route, and follow it. After setting the new route always inform FirstSpirit by calling #setPreviewElement to set the context e.g. for workflow actions.
See also WE_API.Common.PreviewRequestHandler.
NOTE: When using the CaaS mode the triggering of the provided handler is delayed to ensure that the related CaaS document is up-to-date. See CaaS mode description for more information.
(onRequestPreviewElement~Handler)
TPP_SNAP.onRequestPreviewElement(async (previewId) => {
// error handling omitted for brevity
const json = await TPP_SNAP.renderElement(previewId);
window.history.pushState(json.uid, json.displayName, json.uid);
await TPP_SNAP.setPreviewElement(previewId);
})
Define a custom button on the element decoration.
(Button)
(int
= -1
)
the button index, used as rendering order;
-1
means at the end
// register a debug button, as the first button, on any decorated element
TPP_SNAP.registerButton({
css: 'tpp-icon-debug',
execute: async (scope) => console.log(scope),
}, 0);
Override a default button on the element decoration.
(string)
available button names:
edit
,
metadata
,
add-sibling-section
,
add-child-section
,
add-child-section-body
,
workflows
,
delete
,
crop
,
translate
,
move
, and
bookmark
// override what the default 'crop' button does
TPP_SNAP.overrideDefaultButton('crop', {
execute: async ({ previewId }) => actions.cropImage(previewId, 'my-cropped-resolution'),
});
Find DOM nodes by PreviewId.
(string)
Promise<[...HTMLElement]>
:
any DOM node which is associated with the given PreviewId
Executes a project script or an executable.
See also WE_API.Common.execute.
(string)
script ("script:script_uid") or executable ("class:full.qualified.executable.ClassName")
(object
= {}
)
parameters (e.g. { param1: 42, param2: 'text' })
(boolean
= true
)
should wait for an result
Promise<any>
:
the result
// https://docs.e-spirit.com/odfs/template-develo/contentcreator/functional-scop/index.html#klassen_navigationbearbeiten
TPP_SNAP.execute('class:EditMenu', { node: 43 }).then(() => location.reload());
Returns the PreviewId of the ContentCreator scope (see TPP_SNAP.setPreviewElement).
Promise<string>
:
the PreviewId
Returns the current language abbreviation, set by TPP_SNAP.setPreviewElement.
Fallback is always EN
!
Promise<string>
:
the language abbreviation
Opens the Edit Dialog of a FirstSpirit StoreElement associated with the PreviewId.
Triggers onContentChange~Handler.
(string)
the associated PreviewId
Opens the Meta Data Dialog of an FirtSpirit StoreElement associated with the PreviewId. MetaData providing elements must be allowed in the ContentCreator settings, see https://docs.e-spirit.com/odfs/edocs/admi/firstspirit-ser/project-propert/contentcreator/index.html#text_bild_14.
Triggers onContentChange~Handler.
(string)
the associated PreviewId
// display the default button
TPP_SNAP.overrideDefaultButton('metadata', {
isVisible: ({ status }) => !status.custom && (['PageRef', 'Page', 'Section', 'Media'].includes(status.elementType))
})
Renders the given PreviewId.
(string
= null
)
the associated PreviewId; if not set, the
StartNode
will be rendered
Promise<(string | object)>
:
the rendering result of the FirstSpirit template; if the result is a JSON, the
JSON will automatically parsed. For FirstSpirit projects based on CaaS v3 the standard FirstSpirit json format is returned
Tries to delete a FirstSpirit StoreElement based on the PreviewId.
Triggers onContentChange~Handler.
You can also delete elements by using a specific Workflow - see TPP_SNAP.startWorkflow.
Creates a new Page (and PageRef) in FirstSpirit.
Triggers onRerenderView~Handler and onNavigationChange~Handler.
(string)
the (uid-)path in FirstSpirit's PageStore/SiteStore (separated by '/')
(string)
the name/uid for the new page
(string)
the PageTemplate uid
(object?)
Name | Description |
---|---|
options.language string
(default null )
|
a specific language,
null
means the current language
|
options.result boolean
(default false )
|
if
true
, the
onRerenderView~Handler
will
not
be triggered and the
TPP_SNAP.renderElement
result will be returned.
Be careful when preventing the change event! The affected PreviewId could appear several times in the DOM! |
options.showFormDialog boolean
(default true )
|
if
true
, the page form will be shown
|
options.forceUid boolean
(default false )
|
if
true
, the
Page
and
PageRef
will be created with the exact
uid that was specified. If the uid is already being used the process will be aborted and a
DuplicatePageError
will be thrown. If
false
, a unique uid will be generated (based on the original uid)
in case of a duplicate uid.
|
Adds a new Section.
Triggers onContentChange~Handler if PreviewId fulfills a Page or PageRef, otherwise onRerenderView~Handler would directly be triggered.
(string)
the PreviewId of a parent or sibling element
(object?)
Name | Description |
---|---|
options.body string
(default null )
|
name of the body in which the section is created; if not given the parent's or sibling's body is used. |
options.template string
(default null )
|
a SectionTemplate uid; if not set a selection overlay will displayed |
options.name string
(default null )
|
the name of the Section |
options.index int
(default null )
|
the index where the section should be inserted; default is last position |
options.result boolean
(default false )
|
if
true
, the
onContentChange~Handler
/
onRerenderView~Handler
will
not
be triggered and the
TPP_SNAP.renderElement
result will be returned.
Be careful when preventing the change event! The affected PreviewId could appear several times in the DOM! |
Move a Section before or after another Section.
Triggers onRerenderView~Handler.
(string)
the PreviewId of a the source
Section
(string)
the PreviewId of a the target
Section
,
Body
or
Page
with a single
Body
(object?)
Name | Description |
---|---|
options.before boolean
(default false )
|
(only relevant if target is a Section ) move the source Section before target Section , otherwise it would be moved after |
options.copy boolean
(default false )
|
create a copy of the source Section |
options.skipRerenderEvent boolean
(default false )
|
if
true
, the
onRerenderView~Handler
will
not
be triggered
Be careful when preventing the change event! The affected PreviewId could appear several times in the DOM! |
any
:
true, if the operation was successful, false otherwise
Creates a new Dataset.
Triggers onRerenderView~Handler.
(string)
the TableTemplate uid
(object?)
Name | Description |
---|---|
options.language string
(default null )
|
a specific language,
null
means the current language
|
options.result boolean
(default false )
|
if
true
, the
onRerenderView~Handler
will
not
be triggered and the
TPP_SNAP.renderElement
result will be returned.
Be careful when preventing the change event! The affected PreviewId could appear several times in the DOM! |
Toggles an element to be bookmarked (flagged as master) or not. Bookmarked elements will appear in creation dialogs, like TPP_SNAP.createSection.
(string)
Shows the Crop Dialog for an image.
Triggers onContentChange~Handler, the content
parameter contains the URL in this case.
(string)
a PreviewId of
Media
(type
Picture
)
(boolean
= false
)
if
true
, the
onContentChange~Handler
will
not
be triggered and the URL will be returned.
Be careful when preventing the change event! The affected PreviewId could appear several times in the DOM!
Note: When using the OCM crop button, the resolution can also be specified in the DOM by defining the
data-tpp-context-image-resolution
attribute using the format res1, res2
.
Shows the translation help dialogue.
Triggers onContentChange~Handler if the current language is the same
as the given target
language.
Shows a message in the ContentCreator, either an info or an error messagebox.
Trigger onContentChange~Handler. Can be used if a Custom Button changes the content.
(string)
the target PreviewId
((string | object)
= null
)
the updated content, if
null
TPP_SNAP.renderElement
is called internally
Trigger TPP_SNAP.triggerRerenderView.
Wrapper for MPP_API.addParameterizedListener.
(MPPWebControl.ParameterizedListener)
Wrapper for MPP_API.addParameterListener.
(MPPWebControl.ParameterListener)
Wrapper for MPP_API.addTimeParameterListener.
(MPPWebControl.TimeParameterListener)
Async wrapper for MPP_API.getTimeParameter.
Promise<Date>
:
Async wrapper for MPP_API.isParameterized.
Promise<boolean>
:
Async wrapper for MPP_API.setParameter.
Async wrapper for MPP_API.setTimeParameter.
(Date)
Type: Function
Type: Function
(HTMLElement)
a DOM node which contains the
[data-preview-id]
attribute
(string)
the current PreviewId
(any)
the changed content, could be a string,
or an object (if the FirstSpirit Template generates JSON) or null, if
this PreviewElement was deleted.
any
:
if the handler doesn't return anything,
onRerenderView~Handler
will be triggered.
Type: Function
Type: Function
((string | null))
of a newly created page, otherwise null
Type: Function
(string)
the current PreviewId
(object)
Name | Description |
---|---|
button.label string
(default '' )
|
simple labeling, used by the default of Button#getLabel~Callback |
button.css string?
|
simple css class definition, used by the default of Button#getIcon~Callback |
button.icon string?
|
simple icon url, used by the default of Button#getIcon~Callback |
button.supportsComponentPath boolean?
|
whether the button is applied to elements specifying no actual, but a component path preview ID prefixed with "#" |
button.supportsInedit boolean?
|
whether the button is applied to elements that can be edited in-place |
button.isVisible Button#isVisible~Callback?
|
whether this button should be rendered or not; the default is
(scope) => true
|
button.isEnabled Button#isEnabled~Callback?
|
whether this button should be enabled or not; the default is
(scope) => false
|
button.getIcon Button#getIcon~Callback?
|
use
scope.$button
to define the appearance of the button
|
button.getLabel Button#getLabel~Callback?
|
the tooltip (
[title]
) for this button
|
button.getItems Button#getItems~Callback?
|
if this is not an empty list, a dropdown will be rendered; the default is
(scope) => []
|
button.beforeExecute Button#beforeExecute~Callback?
|
will be called before Button#execute~Callback |
button.execute Button#execute~Callback?
|
will be called, when the button (or an item) is clicked |
button.afterExecute Button#afterExecute~Callback?
|
will be called after Button#execute~Callback |
The ButtonScope is an object which is used by all button callbacks.
(HTMLElement)
the DOM node where the decoration appears
(string)
the PreviewId
(string)
the current language
Be careful with your Promise here: the rendering of all buttons only happens after all Button#isVisible
calls.
That's why ButtonScope.$button
doesn't exists in ButtonScope this time.
Type: Function
(ButtonScope)
Promise<boolean>
:
Type: Function
(ButtonScope)
Promise<boolean>
:
Type: Function
(ButtonScope)
// the default callback is defined as:
TPP_SNAP.registerButton({
icon = null,
css = null,
getIcon = async ({ $button }) =>
(css !== null && !$button.classList.add(css))
|| (icon !== null && ($button.style.backgroundImage = `url(${icon})`))
|| $button.classList.add('tpp-icon-action'),
...
});
Type: Function
(ButtonScope)
// the default callback is defined as:
TPP_SNAP.registerButton({
label = '',
getLabel = () => label,
...
});
// localize
TPP_SNAP.registerButton({
getLabel: ({ language }) => language.toLowerCase() === 'de' ? 'Deutsche Bezeichnung' : 'English Label',
...
});
An Item could be anything, but it needs a property called label
, which appears in the dropdown.
Type: Function
(ButtonScope)
TPP_SNAP.registerButton({
label = '',
getItems = () => {
return [
{ label: 'Option 1', value: 1 },
{ label: 'Option 2', value: 2 },
{ label: 'Option 3', value: 3 },
];
},
execute = (scope, item) => console.log(item.value),
...
});
Type: Function
(ButtonScope)
Type: Function
(ButtonScope)
Promise<void>
:
Type: Function
An error that represents a duplication of a page (e.g. thrown by createPage in case a duplicate UID was detected). It encapsulates the preview ID of the duplicated element.
Type: Error
Extends Error
(string)
: the preview ID of the already existing page
This error can be handled by applying .catch((e) => {...})
on the returned promise.
Via DuplicatePageError~getPreviewId method one can also retrieve the preview ID of the element that was
duplicated.
TPP_SNAP.createPage("path/to/homepage", "homepage", "homepage", {
forceUid: true
}).catch(e => {
console.log(e.message);
console.log(e.previewId);
});
A custom PreviewId starts with custom:
and can be used in a URN Schema way to create custom buttons.
Extends Status
// <div data-preview-id="custom:create-page:my/firstspirit/path:myPageName"></div>
TPP_SNAP.registerButton({
css: 'tpp-icon-create-page',
label: 'addMyPageName',
isVisible: ({ status }) => status.custom !== null && status.parts[0] === 'create-page',
execute: ({ status }) => {
const [, path, name] = status.parts;
TPP_SNAP.createPage(path, name, 'page_template_uid');
},
})
The status is not a stable API, yet.
Performs a request targeting a specific nested component in the context of a previewId, while avoiding duplicate parallel requests targeting the same component at the same time.
(any)
Performs the request for the specified nested component.
(any)
{string} The previewId of the surrounding IDProvider.
(any)
{string[]|null} The path to the nested component.
Promise
:
A promise receiving the result of the request.
The procedure making the request in the context of a specific nested component.
Type: Function
(any)
{string} The previewId of the parent element.
(any)
{string[]} The path to the nested component.
Promise
:
A promise receiving the result of the request.
Manual polyfill. The elements provided by MutationObserver do not seem to be enhanced by babel-polyfills.