Content Editor
Content editor specific CSS
| CX 1.2.46 |
Sometimes it is useful to apply special CSS rules to the content when the page is in edit or preview mode. There are two special CSS classes set on the document’s <html> tag to address such use cases:
-
bsi-ce-edit-mode is set when the document is shown in the content editor.
-
bsi-ce-preview-mode is set when the document is shown in preview mode inside the content editor.
The preview mode outside the content editor or any content that is delivered by a story will not have any of the above listed classes.
<html><head><!-- head omitted --></head>
<body>
<h1 class="hide show-if-edit">this is edit mode</h1>
<h1 class="hide show-if-preview">this is preview mode</h1>
<!-- rest of body omitted -->
</body>
.hide { display: none; }
.bsi-ce-edit-mode .show-if-edit { display: block; color:green; }
.bsi-ce-preview-mode .show-if-preview { display: block; color:darkorange; }
| Edit mode |
|---|
Preview Mode |
|
|
Predefined Story outlets (Bracket Links)
One way to define a link out of a design is to use a data-bsi-element-part="link" annotation on a link (<a>) element.
Doing so will add a link editor to enter a URL and a link text to the content editor.
<a data-bsi-element-part="link" href="https://www.bsi-software.com/">BSI Website</a>
Another way to define a link directly out of a design is by using the CX bracket link syntax for links. This mechanism in built into the content editor itself to create a link anywhere in a text.
<span data-bsi-element-part="plain-text" >If you do not wish to receive further promotions, you can [unsubscribe] now.</span>
-
Definition of links using the CX bracket syntax.
-
Each link is converted into a story outlet. Each outlet can then be linked to an individual action in the story.
-
The content that is delivered to the user.
While rendering the above content, CX will convert the [unsubscribe] term to a link tag internally, create a unique hyperlink and will add a new outlet to the step to which the current content belongs.
When using bracket links, the story designer will see the provided link as a story outlet by default.
This allows the Story Designer to link the click action on the link with other steps (e.g. a subsequent landing page step).
In the example above, the Email Step will already have an outlet named unsubscribe.
The bracket technique itself is usable everywhere in the content editor and is not limited to the usage in a design.
The convenience format of a bracket link for CX is [link text|ID].
The full syntax is [ID|text=link text|url=https://example.com|newWindow].
You may use the following optional parameters:
-
urlcan be used to explicitly specify an absolute URL. -
newWindowwill force to open the target in a new tab or window. -
followremoves the nofollow value from the html rel attribute. Without nofollow, bots are allowed to crawl your page (not recommended for personalised links). -
logincreates a link having the query parameter ?login, which redirects to the login page in a website. -
logoutcreates a link having the query parameter ?logout, which removes the authenticated user from a CX HTTP session in a website.
If a bracket itself should be part of a link, it can be escaped with a backslash (e.g. [\[click me\]|id] will then be visualized as [click me]).
You can also place a bracket link anywhere in the design.
An example of doing so is to add it to an href of a link.
<a href="[Facebook]"><img src="img/facebook.png" /></a>
| Be reminded that all CX will replace everything that looks like a bracket link. It is your responsibility as a developer to use escaping where brackets have a different meaning. |
<!-- CX will convert this to a bracket link -->
<input pattern="[0-9]">
<!-- escaping is mandatory for the pattern mechanism to work as desired -->
<input pattern="\[0-9]">
Bracket links in iterators
When you display data in a table, you will use the iterator feature of BSI CX. In this example we display a list of blog articles.
In the content editor you use a table content-element. You activate the 'Dynamic table' checkbox in the editor form. Now you define the columns. Let’s assume we have 3 columns:
-
{blog.title}
-
{blog.releaseDate}
-
[blog.details] [blog.edit]
The third column defines two iterator bracket links. The link ID must start with the entity prefix, which is followed by an arbitrary name that defines the function of the link.
When you use this content in a media step in the storyboard, the step will provide a GUI, to pick an entity and the values for the placeholders 'title' and 'releaseDate'. For the two links the step will generate two outlets. A consequent step connected to these outlets can pick from the iterator property which is labeled with 'Chosen via link' and can access the properties of that distinct iterator item.
Styling of bracket links
When CX converts a [placeholder] term internally into a link tag, it adds the CSS class placeholder-link to the a tag. This allows bracket links to be selected and styled separately from other link elements.
placeholder-linka.placeholder-link {color: #fe9915;}
.footer-div a.placeholder-link {color: #ffffff;}
It should be noted that the CSS class is not set until the live content is rendered. For this reason, you won’t find the styling, referring to the CSS class placeholder-link, in the content editor, but only - in the case of email content - in the sent email.
Using anchor links inside a design
First of all, it is possible to use anchor links in the form of #top inside a CX design.
Anchor links are allowed as targets of link parts.
However, it is not possible to use anchor links as url of a bracket link.
This is due to the fact, that bracket links meant to be trackable by CX and this requires a complete roundtrip to the server.
<section id="news">
<!-- some news -->
</section>
<!-- some more html code -->
<div data-bsi-element="cta">
<a href="#news" data-bsi-element-part="link">Jump to news</a>
</div>
Content Elements and Parts
This chapter describes the structure of the content-elements.html file and the available content elements.
The content-elements.html file contains HTML snippets for the content elements that can be selected in BSI CX.
To do so, BSI CX searches for elements with the data-bsi-element attribute in the HTML code.
The recommendation is to define all content elements at the same level as the DOM, for example, as <div> directly below the <body> element.
However, for better visualization during development, the developer can also create additional elements (e.g., with CSS borders or margins) around the elements marked with data-bsi-element.
BSI CX will ignore those, though.
It is not necessary for the content-elements.html file to have a <html>, <head> or <body> tag.
But the containing HTML must be valid.
|
The following listing illustrates a very simple content element:
<div data-bsi-element="logo">
<img src="logo.png" alt="My Company"/>
</div>
As we can see in the listing, data-bsi-element defines the identifier of the content element.
It must not contain any spaces.
The identifier corresponds to the value that can be used in the attribute called data-bsi-dropzone-allowed-elements in the dropzone.
The element marked with the identifier, when used, is inserted into the relevant dropzone (not only the child elements).
| The content editor specifically highlights the content elements. To make sure this works properly, all content elements must be block elements rather than inline elements. |
A content element can define one or more internal dropzones.
The definition is the same as for design.html.
For example, use it with a content element for a 3-column layout with three internal dropzones.
While the dropzones may be nested arbitrarily, please pay attention to usability.
Grouping
Complex designs can consist of many elements.
For better organization, it is mandatory to group the elements.
To do so, the data-bsi-group attribute can be used.
The following listing illustrates such a grouping:
<div data-bsi-group="texts">
<div data-bsi-element="default-text" data-bsi-element-part="formatted-text">
<p>Lorem ipsum</p>
<ul>
<li>lorem</li>
<li>ipsum</li>
</ul>
</div>
<div data-bsi-element="raw-text" data-bsi-element-part="plain-text">Lorem ipsum</div>
</div>
<div data-bsi-group="images">
<div data-bsi-element="full-size-image">
<img src="example.jpg" data-bsi-element-part="image"/>
</div>
</div>
Types
There are two distinct styles of content elements:
-
HTML-Elements: HTML-Elements only consist of HTML Element Parts. Element Parts specify a fixed structure of a DOM-subtree that is directly manipulated inside the content editor. This type has been supported ever since the release of CX.
-
Handlebars-Elements: Handlebars-Elements use Handlebars as template engine. The element consists of Template Parts that specify parts of the element editor. The HTML of the element is created by rendering the Handlebars-Template with the variables exported by the Template Parts. Handlebars-Elements cannot include traditional HTML Element Parts.
Element Parts
A content element part references, inside a content element, an area (such as text or image) that can be edited directly with the BSI CX content editor.
The content element "text with image" knows, for example, the two areas image and plain-text.
The identifiers must be adopted exactly as they are.
The following listing illustrates the example:
<figure class="element text-with-image" data-bsi-element="text-with-image"> (1)
<img class="image" data-bsi-element-part="image" src="example.jpg"/> (2)
<figcaption class="text" data-bsi-element-part="plain-text">Lorem ipsum</figcaption> (3)
</figure>
| 1 | This is the content element definition. |
| 2 | The image part defines an editable image. |
| 3 | The plain-text part defines an editable plain text. |
The individual parts are likely to contain sample content already.
This content must fit in terms of the structure (e.g., only plain text with plain-text, and a correct table definition with table, etc.)
|
Plain Text
The part plain-text can be applied to any HTML element that can contain text.
The text is inserted within the element.
The element must not have children with other content element parts (content is removed).
A single-line text field without formatting is available as the editor.
When using the optional attribute data-bsi-multiline, the text field becomes multiline.
Boolean attributes like data-bsi-multiline require an empty value ="" or sample value like ="true".
|
<h1 data-bsi-element="title-h1" data-bsi-element-part="plain-text" class="element title-h1">Lorem ipsum</h1>
<p data-bsi-element="paragraph" data-bsi-element-part="plain-text" data-bsi-multiline="">Lorem ipsum<br>dolor sit amet</p>
Configuration
-
studioLinkEnabled: This boolean value controls whether the 'CX Link' button is visible. Default is
false. -
multiline: This boolean value controls whether the field is single- or multiline. Default is
false. Note: in earlier CX versions, this setting was configured via the DOM attributedata-bsi-multiline. -
fieldHeight: This integer value controls the field height in the form of the content editor. Default is
5. The unit is Eclipse Scout grid height (Property 'h' in GridData).
Formatted Text
The part formatted-text can be applied to any HTML element that can contain text.
The element must not have children with other content element parts (content is removed).
The rich text field with a limited number of formatting options is available as an editor.
For more information about the available configuration options see chapter [rich-text-editor].
<section data-bsi-element="section" data-bsi-element-part="formatted-text">
<h1>Lorem ipsum dolor sit amet</h1>
<p>Proin porta <strong>pharetra est non</strong> molestie.</p>
<p><a href="https://www.example.org/">More</a></p>
</section>
HTML
The html part can be applied to any HTML element.
The code is inserted within the element.
The element must not have children with other content element parts (content is removed).
A text field is used as the editor.
HTML can be written directly into this field.
<div data-bsi-element="html" data-bsi-element-part="html">
<iframe src="https://www.openstreetmap.org/export/embed.html" style="height: 500px;"></iframe>
</div>
This part can be useful for:
-
Prototyping
-
Quick-and-Dirty Hacks
-
Embedding any HTML code
It is not allowed to place any data-bsi-element or data-bsi-element-part attributes inside the HTML code.
This won’t work as expected and leads to invalid content.
|
Video
The video part can be applied to any block level HTML element.
This part can be used to display information and thumbnails of external videos.
It can also be used to embed external videos.
Within the element, various elements can be used as placeholders:
-
Each
<iframe>element is used to embed an external video player. -
Each
<img>element with adata-bsi-video-thumbnailattribute is used to display the video thumbnail. -
Each HTML element with the
data-bsi-video-titleattribute is used to display the video title. -
Each HTML element with the
data-bsi-video-descriptionis used to display the video description. -
Each
<a>element with thedata-bsi-video-linkis used to place a link to the external video. The text of the hyperlink will not be touched, only thehrefattribute will be filled.
| Fetching the video’s title and description may requires an active API key for the appropriate provider. |
Boolean attributes like data-bsi-video-title require an empty value ="" or sample value like ="true".
|
The following external video providers are supported:
- YouTube
-
Without a configured API key, only the thumbnails are automatically picked. More information on obtaining a YouTube API key: https://developers.google.com/youtube/v3/getting-started
Be aware that there are two kinds of URLs involved:
One is the link to the video platform itself for the href attribute on the <a> tag and there is the embedded URL for the <iframe> player.
In the case of YouTube https://www.youtube.com/watch?v=YpnFs4aBLQA is the link to the video platform.
The corresponding embedded URL would be https://www.youtube.com/embed/YpnFs4aBLQA.
If you want to provide sample content for your element you may have to fill both URLs in the right tag.
The following listing illustrates the use of sample content for a simple video element:
<div class="element video" data-bsi-element="video-simple">
<div class="responsive-video" data-bsi-element-part="video" data-bsi-video-data-link="https://www.youtube.com/watch?v=YpnFs4aBLQA"> (1)
<iframe width="1120" height="630" frameborder="0" src="https://www.youtube.com/embed/YpnFs4aBLQA" allow="autoplay; encrypted-media" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe> (2)
</div>
</div>
| 1 | The link to the video platform is placed inside the data-bsi-video-data-link attribute. |
| 2 | The embed video URL is placed inside the src attribute of the <iframe> tag. |
This results in the following content element:
A more complex video element would be the following:
<div class="element video" data-bsi-element="video-thumbnail">
<div class="video-thumbnail" data-bsi-element-part="video" data-bsi-video-data-link="https://www.youtube.com/watch?v=YpnFs4aBLQA"> (1)
<p>
<a href="https://www.youtube.com/watch?v=YpnFs4aBLQA" data-bsi-video-link="" target="_blank"> (2)
<img src="https://i.ytimg.com/vi/YpnFs4aBLQA/maxresdefault.jpg" alt="" data-bsi-video-thumbnail=""/> (3)
</a>
</p>
<h3 data-bsi-video-title="">BSI Tech Lovers</h3> (4)
<p data-bsi-video-description="">Für mehr WOW-Momente mit BSI. Monika Freiburghaus, Ralf Muri, Thomas Lindauer und Urs Frick zeigen Einblicke in ihre persönlichen WOW-Momente.</p> (5)
</div>
</div>
| 1 | The link to the video platform is placed inside the data-bsi-video-data-link attribute. |
| 2 | The link to the video platform is also placed inside the href attribute. |
| 3 | The placeholder image can also be part of the design, it is not mandatory to provide the URL to the real image here. |
| 4 | The content of the first tag marked with data-bsi-video-title will be used as placeholder for the title. |
| 5 | The content of the first tag marked with data-bsi-video-description will be used as placeholder for the video description. |
This results in the following content element:
The video part is quite powerful and can be used in various ways:
-
Enrich your landing page or website with embedded videos.
-
Use video thumbnails in your content.
-
Send emails with link, thumbnail, title and description of an external video.
Image
The image element part can only be placed inside of a <img> or <a> tag.
The source is inserted in the src attribute.
<div data-bsi-element="image-simple">
<img src="example.jpg" alt="Example image" data-bsi-element-part="image" />
</div>
It is not possible to omit the image of an image part.
Therefore it is mandatory to specify an image in the content editor.
Otherwise a validation error will occur.
srcset
By specifying the srcset attribute, the images are also scaled accordingly.
If a srcset attribute is present, the src attribute is set to the image at its highest resolution (according to the defined srcset).
If there is no srcset attribute, the selected image is not scaled and is placed directly as src.
For images with dynamic sources (the image is set through the story), the definition of the srcset is ignored.
If you set an image in the content editor, make sure it is of decent size. After saving the content, the image is scaled to the sizes specified in the srcset attribute and stored on the CX resource server. Depending on the resolution of device that is requesting the landing page/website, a different resolution of the image is loaded.
We highly recommend to set the srcset attribute.
|
<div data-bsi-element="image-simple">
<img src="example-800w.jpg" srcset="example-480w.jpg 480w, example-800w.jpg 800w" alt="Example image" data-bsi-element-part="image" />
</div>
The attribute data-bsi-hide-link controls whether the field URL/internet address and the checkbox Open in new window is visible and editable in the content editor. A design developer may still wrap the image inside an <a> tag in the content element, but the user cannot change the link.
The attribute data-bsi-hide-link is available from CX 1.3.40 onward
|
background-image
An arbitrary element which permits the use of a CSS background-image.
It places the CSS attribute background-image.
The image selection is available as the editor just like with the image part.
<div class="element scene" data-bsi-element="scene">
<div data-bsi-element-part="background-image" style="background-image: url('example.jpg')"></div>
</div>
table
A <div> oder <td> element with a table element as a direct child element.
The table content is filled according to the editor (including colgroup).
Colspan is not supported. On even or odd rows or columns, the CSS class even or odd is placed.
The <th> tag is used for header columns, if specified.
This part does not support <thead> and <tbody> tags.
| It is possible to use an iterator to generate dynamic tables. |
<div class="element table" data-bsi-element="simple-table" data-bsi-element-part="table">
<table>
<tr>
<th>Firstname</th>
<th>Lastname</th>
<th>Age</th>
</tr>
<tr>
<td>John</td>
<td>Doe</td>
<td>42</td>
</tr>
</table>
</div>
iterator
An arbitrary element.
The attribute data-bsi-iterator can be defined on this element.
The attribute data-bsi-iterator-item must be defined on the element or on one of the included elements.
This defines the element to be duplicated.
If data-bsi-iterator is defined and the iterator does not provide any data, the entire element is removed.
Otherwise, only the element to be duplicated is marked with data-bsi-iterator-item.
A text field is used as the editor. The name of the iterator can be placed in this text field.
<div class="element iterator" data-bsi-element-part="iterator" data-bsi-iterator="Customer">
<div class="customer" data-bsi-iterator-item="Customer">
<dl>
<dt>Name</dt>
<dd>{Customer.name}</dd>
<dt>Address</dt>
<dd>{Customer.address}</dd>
</dl>
</div>
</div>
news-snippets
An arbitrary element.
Displays a news item.
Within the element, elements with the data-bsi-property attribute can be defined.
This attribute may have the following values: image, title, headline, or text.
The content of these elements is then replaced accordingly with the value of the selected news item.
In that context, elements from property title, headline, and text are mandatory.
If a link in CX is configured into a news snippet, title and image are automatically supplemented with a link.
When generating the news list, the following CSS classes are automatically set on a news snippet to permit better styling of the individual elements:
| news-nth-X |
the position in the list. E.g., |
| odd |
odd position in the list |
| even |
even position in the list |
| first |
first news snippet |
| last |
last news snippet |
<div class="element news" data-bsi-element="news-snippets" data-bsi-element-part="news-snippets">
<div class="news-image element">
<img class="ns-image" src="placeholder.jpg" data-bsi-property="image"/>
</div>
<div class="news-text element">
<h2 class="ns-title" data-bsi-property="title">Breaking News</h2>
<p class="ns-headline" data-bsi-property="headline">Lorem ipsum dolor sit amet</p>
<p class="ns-text" data-bsi-property="text">Ea vis odio voluptua, pri ei facer denique.</p>
</div>
</div>
form
A <form> element.
The element must contain a submit button and may also contain a reset button.
Those buttons must be <input> elements.
In the form configuration, the reset button can be hidden or displayed.
In that case, the DOM attribute disabled is set on the input element.
The form element must contain an element with the data-bsi-form-validation attribute, and this attribute must contain an element with the data-bsi-form-validation-item attribute.
Within the element with data-bsi-form-validation, the element with data-bsi-form-validation-item is duplicated for each error in the server-side form validation, and the validation message is inserted as the content of the validation item element.
Boolean attributes like data-bsi-form-validation require an empty value ="" or sample value like ="true".
|
In addition to the display in the above-described element, a data-bsi-form-validation-message attribute is set for each defective form field.
It contains the validation error for the respective field.
The attribute can be used to style the field with CSS if there is an error (CSS selector checks for the presence of the attribute) or to display the error message with CSS and/or JavaScript in the respective field.
The following form fields can be marked as mandatory fields in the content editor.
If this is done, the required attribute is set in the relevant HTML element.
Thus, the mandatory elements are styled using the respective CSS selectors.
To exclude a certain form from CX you set the data-bsi-form-ignore attribute on the form tag.
Any form with this marker will be left untouched by CX.
<div class="element form" data-bsi-element="slim-form">
<form class="form form-wrapper" data-bsi-element-part="form" id="form">
<ul class="form-validation" data-bsi-form-validation="">
<li class="form-validation-item" data-bsi-form-validation-item=""></li>
</ul>
<div class="form-content" data-bsi-dropzone="form-content" data-bsi-dropzone-allowed-elements="form-field">
</div>
<div class="form-button-bar">
<input type="submit" class="form-button send" value="Senden" />
<input type="reset" class="form-button reset" value="Zurücksetzen" />
</div>
</form>
</div>
form-field
A <div> element.
Contains a <label> and an <input> element.
In the configuration, the label, initial value, mandatory field, and input type can be defined.
The following input types are supported:
| text |
For a simple text field. |
|
Input field, that accepts a valid e-mail address. |
|
| tel |
Input field, that accepts a phone number. |
| password |
A password input field, that shows |
| number |
An input field, that accepts only numbers. |
| range |
An input field, that shows a numeric range slider. |
| date |
An input field, that can be used to select a certain date. |
| datetime-local |
Input field, that accepts a date and a specific time. |
| time |
Input field, that accepts a time. |
| file |
Input field, that can be used to upload a file. |
To fixate the form field type, the data-bsi-form-field-fixed-type attribute can be used on the element.
Doing so allows the setup of a customized date field, for example.
The type of the form field is then set and cannot be changed in the content editor.
The attribute data-bsi-hide-required controls whether the mandatory checkbox is visible and editable in the content editor. If the checkbox is not shown, the original state of the required attribute is preserved, as defined by the content element.
The attribute data-bsi-hide-required is available from CX 1.3.40 onward
|
| Be aware, that not all browsers support all types of input fields. Some browsers require a polyfill to handle certain types of form fields. It is up to the developer to use polyfills where this is required. |
<div class="form-field" data-bsi-element="date-field" data-bsi-element-part="form-field" data-bsi-form-field-fixed-type="">
<label for="date" class="label">Date</label>
<input id="date" class="input" type="date" value="2009-11-13" required="" />
</div>
Boolean attributes like data-bsi-form-field-fixed-type require an empty value ="" or sample value like ="true".
|
form-checkbox
A <div> element.
Contains a <label> and an <input> element with the checkbox type.
The attribute data-bsi-hide-required controls whether the mandatory checkbox is visible and editable in the content editor. If the checkbox is not shown, the original state of the required attribute is preserved, as defined by the content element.
The attribute data-bsi-hide-required is available from CX 1.3.40 onward
|
<div data-bsi-element="form-checkbox" data-bsi-element-part="form-checkbox" class="element checkbox">
<input type="checkbox" id="checkbox1" />
<label for="checkbox1">Checkbox 1</label>
</div>
form-textarea
A <div> element.
Contains a <label> and a <textarea> element.
The attribute data-bsi-hide-required controls whether the mandatory checkbox is visible and editable in the content editor. If the checkbox is not shown, the original state of the required attribute is preserved, as defined by the content element.
The attribute data-bsi-hide-required is available from CX 1.3.40 onward
|
<div data-bsi-element="form-textarea" data-bsi-element-part="form-textarea" class="form-field form-element">
<label for="textarea" class="label">Description</label>
<textarea id="textarea" rows="5" cols="20" class="textarea" maxlength="500">Lorem ipsum</textarea>
</div>
form-select
A <div> element.
Contains a <label> and a <select> element with the elements option.
The pre-allocation of the element can be controlled with the data-bsi-value and data-bsi-value-list attributes.
The data-bsi-value attribute contains the preal-location, and the values available for selection are defined in the data-bsi-value-list attribute.
A line break (\n or \r\n) separates each of those.
To fixate the values that are available for selection, the data-bsi-form-select-fixed-value-list attribute can be set on the element.
Then, the list of values cannot be changed in the content editor.
Only the preselection can be changed.
The attribute data-bsi-hide-required controls whether the mandatory checkbox is visible and editable in the content editor. If the checkbox is not shown, the original state of the required attribute is preserved, as defined by the content element.
The attribute data-bsi-hide-required is available from CX 1.3.40 onward
|
The multiple attribute is not supported.
|
<div data-bsi-element="form-select" data-bsi-element-part="form-select" class="form-select form-element" data-bsi-value-list="Yes
No
Unsure" data-bsi-value="No">
<label for="select" class="label">Decision</label>
<div class="select-wrapper">
<select size="1" id="select">
<option>Yes</option>
<option selected="">No</option>
<option>Unsure</option>
</select>
<div class="dropdown" role="presentation">a</div>
</div>
</div>
form-radio
A <div> element.
Contains a <label> and a <div> element as a radio group.
The radio group is marked with the data-bsi-radio-group attribute.
The radio group contains radio items.
These are identified with the data-bsi-radio-item attribute and contain a <label> and an <input> element with the radio type.
The preselection of this element can be controlled with the data-bsi-value and data-bsi-value-list attributes.
Same as the form-select part.
The same applies to the data-bsi-form-select-fixed-value-list attribute.
The attribute data-bsi-hide-required controls whether the mandatory checkbox is visible and editable in the content editor. If the checkbox is not shown, the original state of the required attribute is preserved, as defined by the content element.
The attribute data-bsi-hide-required is available from CX 1.3.40 onward
|
<div data-bsi-element="form-radio" data-bsi-element-part="form-radio" class="form-radio form-element">
<label class="label">To be or not to be?</label>
<div data-bsi-radio-group=""> (1)
<div class="form-radio-item" data-bsi-radio-item=""> (2)
<input type="radio" id="radio1" />
<label for="radio1">Radio-Button 1</label>
</div>
</div>
</div>
| 1 | The data-bsi-radio-group element contains the prototype element. |
| 2 | The data-bsi-radio-item element is the prototype element. |
Be aware, that data-bsi-radio-item must be right inside data-bsi-radio-group.
Any intermediate element will be removed.
|
Boolean attributes like data-bsi-radio-item require an empty value ="" or sample value like ="true".
|
basic-dynamic-value-list
This part offers functionality for the integration of BSI Suite value lists into any HTML form, providing REST endpoints for the purpose of retrieving values from such lists.
| This element part is a backport of the dynamic value list template part. Check this chapter for a detailed documentation. You should prefer the template part version, because it is more flexible regarding the DOM structure. |
Fields
-
Value list: A reference to a BSI Suite value list. Mandatory.
-
Initial value: Optional initial value for the HTML form field. Only values belonging to the referenced Value list are available. Only editable if Value list is set.
| It is also possible to define an initial value within the configuration of a story, which supersedes the initial value of the content. |
basic-dynamic-value-list part.<div data-bsi-element="basic-dynamic-value-list" data-bsi-element-part="basic-dynamic-value-list" data-bsi-entity-id="">
<label>Field</label>
<input type="text" class="w-input dynamic-select" data-bsi-entity-key-value="" data-bsi-entity-key-display-text="">
</div>
Usage
<div data-bsi-element="dynamic-value-list" data-bsi-entity-id="{{context.dataModelValue.type}}" >
<input
data-bsi-entity-key-value="{{context.dataModelValue.value}}"
data-bsi-entity-key-display-text="{{context.displayText}}" >
</div>
The DOM structure is identical to the form-field element part.
The data-bsi-* attributes must be placed on the root DIV element and on the nested INPUT element, as shown in the example above.
Design.json
design.json file.{
"elementId": "basic-dynamic-value-list",
"file": "content-elements/forms/basic-dynamic-value-list.html",
"icon": "dropdown",
"label": "Dynamic Value List (Basic)",
"description": "Use dynamic source e.g. CRM",
"parts": [
{
"partId": "basic-dynamic-value-list",
"label": "Dynamic select"
}
]
}
link
The part is defined on the <a> element.
As an option, the link content can be set with the data-bsi-link-fixed-inner-html attribute.
If this attribute is present, only the href attribute of the link can be edited in the content editor.
If the link text is to be placed in a tag within the hyperlink (e.g. in a span next to an <img> tag), the optional data-bsi-link-text-part attribute can be set on the tag.
Boolean attributes like data-bsi-link-fixed-inner-html could require an empty value ="" or sample value like ="true".
|
<div data-bsi-element="cta" class="element cta">
<a href="https://www.bsi-software.com/" data-bsi-element-part="link">More</a>
</div>
data-bsi-link-fixed-inner-html.<div data-bsi-element="cta" class="element cta">
<a href="[dynamic_link]" data-bsi-element-part="link" data-bsi-link-fixed-inner-html>
<span>
<p> Some static content </p>
<p data-bsi-element-part="formatted-text"> Add your text here! </p>
</span>
</a>
</div>
data-bsi-link-text-part.<div data-bsi-element="cta">
<a data-bsi-element-part="link" href="#" role="button">
<i class="icon icon-028-arrow-back"></i>
<span data-bsi-link-text-part>The link text is </span>
</a>
</div>
social-follow
A <div> element, contains <div> elements with a data-bsi-social-follow attribute.
These attributes contain the value of the respective social media service, such as "Facebook", "Twitter", "Pinterest", etc.
The elements with data-bsi-social-follow must have the social-media-item class.
This class is used to manage the visibility of each individual social media channel in the editor.
The elements with data-bsi-social-follow must also contain an <a> element.
The link href can be preselected with a URL or with CX links in square brackets (e.g., [Facebook]).
The <a> element, in turn, can contain additional elements.
<div data-bsi-element="social-follow" data-bsi-element-part="social-follow">
<div data-bsi-social-follow="Facebook" class="social-media-item">
<a href="https://www.facebook.com/">
<img src="facebook.png" alt="Facebook" />
</a>
</div>
<div data-bsi-social-follow="Twitter" class="social-media-item">
<a href="https://www.twitter.com/">
<img src="twitter.png" alt="Twitter" />
</a>
</div>
</div>
social-share
A <div> element, contains <div> elements with data-bsi-social-share.
These attributes contain the value of the respective social media service, such as Facebook, Twitter, Pinterest, or even e-mail.
The elements with data-bsi-social-share must have the social-media-item class.
This class is used to control the visibility of each individual social media channel in the editor.
The elements with data-bsi-social-share must also contain an <a> element.
The link href can be preselected with a URL or with Studio links in square brackets (e.g., [Facebook]).
To share it via e-mail, the content of the href is then a mailto:?subject=(subject.with.title)&body=(description.with.url)
The <a> element, in turn, can contain additional elements.
The URL supports a list of wildcards that are specific to the social share part. When they are issued, all wildcards are automatically URL-encoded. These wildcards cannot be selected in the step configuration in Studio. The following wildcards exist:
| url |
The URL/internet address of the current Studio webpage or the user-defined URL. |
| description |
The description text of the current Studio webpage or the user-defined description text (a maximum of 300 characters). |
| description.short |
Same as description, but a maximum of 140 characters; all additional characters are truncated. |
| description.with.url |
Same as description; in addition, the url is added to the end of the description text. |
| title |
The title of the current Studio webpage or the user-defined title (a maximum of 100 characters). |
| title.with.url |
Same as title, and, in addition, the url is added to the end of the description text. |
| subject.with.title |
Same as title, and, in addition, a text "Share web-site:" is added to the beginning of the title; is used as the subject in an e-mail. |
| preview.image.url |
The URL/internet address for the preview image of the webpage according to the configuration in the content editor. |
<div data-bsi-element="social-share" data-bsi-element-part="social-share">
<div class="social-media-info">Share this page</div>
<div data-bsi-social-share="E-Mail" class="sm-email social-media-item share">
<a href="mailto:?subject=(subject.with.title)&body=(description.with.url)"></a>
</div>
<div data-bsi-social-share="Facebook" class="sm-facebook social-media-item share">
<a href="https://www.facebook.com/sharer/sharer.php?u=(url)" target="_blank"></a>
</div>
<div data-bsi-social-share="LinkedIn" class="sm-linkedin social-media-item share">
<a href="https://www.linkedin.com/shareArticle?mini=true&url=(url)&summary=(title)&source=FancyUnicorns" target="_blank"></a>
</div>
<div data-bsi-social-share="Pinterest" class="sm-pinterest social-media-item share">
<a href="http://pinterest.com/pin/create/button/?url=(url)&description=(title)&media=(preview.image.url)" target="_blank"></a>
</div>
<div data-bsi-social-share="Twitter" class="sm-twitter social-media-item share">
<a href="https://twitter.com/intent/tweet?url=(url)&text=(description.short)" target="_blank"></a>
</div>
<div data-bsi-social-share="WhatsApp" class="sm-whatsapp social-media-item share">
<a href="whatsapp://send?text=(title.with.url)" target="_blank"></a>
</div>
<div data-bsi-social-share="Xing" class="sm-xing social-media-item share">
<a href="https://www.xing.com/spi/shares/new?url=(url)" target="_blank"></a>
</div>
</div>
url-provider
| CX 22.0 |
The URL provider content element part can be used to load dynamic data inside a story. Inside the content editor, the URL provider part only offers a text field to define its name. When using the content inside your story, each URL provider part will produce a separate outlet. This outlets can be connected to any URL provider (e.g. the Charts Step).
<div data-bsi-element="pie-chart" data-bsi-element-part="url-provider" class="mb-4 chart-js">
<img src="./pie-chart-placeholder.png" alt="" class="w-100" data-bsi-element-part="image"/>
</div>
When a story renders the content, the HTML tag with the url-provider part will have a data-bsi-url attribute.
This attribute will contain an absolute URL to the CX server, where the data can be requested.
Using the Chart URL Provider
We created a small Java Script library to use the chart URL provider in a template. Take a look at the repository on GitHub for more information. To integrate the library, install it with NPM:
npm install --save github:bsi-software/bsi-cx-chart#semver:^1.1.2
You also must install a chart library of your choice, currently there are only bindings for Chart.js. You can install it using npm:
npm install --save chart.js
Now, create your content element:
<div data-bsi-element="pie-chart" data-bsi-element-part="url-provider" class="chart-js">
<img src="./pie-chart-placeholder.png" alt="" class="w-100" data-bsi-element-part="image"/> (1)
</div>
| 1 | This will be replaced when the library is initialized, and the data from the URL provider is available. So it’s recommended to put some placeholders here. |
To use the library, you must initialize it:
import 'chart.js/auto'; (1)
import {ChartConfig, ChartUrlProvider} from '@bsi-cx/chart';
import ChartConfigColor from '@bsi-cx/chart/src/config/color';
/**
* @type {{border: ChartConfigColor, background: ChartConfigColor}[]}
*/
const colors = [
ChartConfigColor.of('#ff6384ff', '#ff638466'),
ChartConfigColor.of('#36a2ebff', '#36a2eb66'),
ChartConfigColor.of('#cc65feff', '#cc65fe66'),
ChartConfigColor.of('#ffce56ff', '#ffce5666')
]; (2)
const config = new ChartConfig()
.withColors(...colors);
document.querySelectorAll('.chart-js') (3)
.map(element => new ChartUrlProvider(element, config))
.forEach(chart => chart.render());
| 1 | Initialize the Chart.js library. |
| 2 | Optional, can be used to define your own set of colors. |
| 3 | Select all URL provider parts you want to initialize with the ChartUrlProvider. |
Template Parts
| CX 25.1 |
It is also possible to build content elements based on the Handlebars templating engine. For design developers this means that they have the choice of either building content elements using the HTML Element Parts described in the last chapter, or the new Handlebars Template Parts. Crucially it is not allowed to mix both in one content element.
Template Parts work in quite a different way from Element Parts: All they do is supply a part of the content element editor, as the name suggests. Each Template Part has a defined set of variables that it provides to render the content elements Handlebars template. A full example might look something like this:
design.json.{
"elementId": "medium-quote-hbs",
"type": "template-element",
"file": "content-elements/content/medium-quote-hbs.hbs",
"contextFile": "content-elements/content/medium-quote-hbs.json",
"icon": "heading",
"label": "Medium Quote",
"templateParts": [
{
"partId": "multiline-plain-text",
"partContextId": "text", (1)
"label": "Text"
},
{
"partId": "checkbox",
"partContextId": "italic", (2)
"label": "Text Kursiv anzeigen"
}
]
}
The content element consists of two Template Parts, crucially they have to be explicitly mentioned in the design.json, since - as opposed to the Element Parts they do not have an explicit declaration in the HTML DOM.
The first Template Part lets the user define a multiline text without any markup, the second exposes a boolean value, that in this example is used to set the text to italic, if the user checks the checkbox.
This results in the following content element editor:
The handlebars template and the default values are as follows:
<div class="content-element-wrap" data-bsi-element="medium-quote-hbs">
<div class="paragraph-light" {{#if italic.value}}style="font-style: italic"{{/if}}>{{text.value}}</div> (3)
</div>
{
"text": { (1)
"value": "Consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua." (2)
},
"italic": { (1)
"value": false (2)
}
}
| 1 | The partContextId defines the name of the instance of the Template Part since there can be more than one of the same type. |
| 2 | Each Template Part supplies an object containing specified context variables that can be used to render the template. |
| 3 | The variable name is prefixed with the partContextId when it is referred to in the Handlebars template. |
plain-text
This template part supplies a text input field, into which the user can input a single line of plain text.
multiline-plain-text
This template part supplies a single multiline input field, into which the user can input plain text.
formatted-text
This template part closely mirrors the corresponding formatted-text element part.
It provides a configurable HTML WYSIWYG editor.
Since this part provides a variable with HTML code, you should use the 'triple-stash' syntax of Handlebars to output the variable: {{{textPart.html}}}.
Otherwise the HTML code would be escaped when the template is being rendered.
|
Editor
As with the corresponding element part, the toolbar of the editor can be configured using a htmlEditorConfig.
The picture shows the current default editor.
link
An template part that provides URL and accessibility information for a link.
Variables
-
url: The URL for the link.
-
text: The link text.
-
description: Accessibility information that describes the link for screen readers. This can be added to the
aria-labelattribute. -
openInNewWindow: Boolean value to describe where the link should be opened, can be used in conjunction with
target="_blank".
image
The image template part supplies all values needed to render an image in the content, including accessibility information.
Editor
The screenshot shows two possible configurations of the image template part editor:
- above
-
Alternative text is set to mandatory, the additional accessibility fields are visible.
- below
-
Alternative text is not mandatory and all accessibility fields are invisible.
The second configuration should only be used in very specific circumstances, e.g. multiple sources in a HTML <picture> tag.
Variables
-
altText: Accessibility-feature, alternative text that is also shown if the browser cannot show the picture.
-
srcUrl: The URL that points to the selected image.
-
placeholderSrcUrl: The URL pointing to a placeholder image (used for the content editor).
-
srcset: Srcset-String. Only relevant if sizes have been defined in the design.
-
decorative: Boolean value that can be used to set the accessibility option
aria-hidden="true", which results in screenreaders ignoring the image entirely.
Configuration
The template part can be configured with the following values:
-
altTextMandatory: Makes the alternative text field in the UI mandatory.
-
srcsetSizes: List of size definitions, as used in the
srcsetHTML attribute, see example below. -
hideAccessibilityFields: Hides all accessibility fields in the editor UI.
Example:
"config": {
"hideAccessibilityFields": true,
"altTextMandatory": false,
"srcsetSizes": [
"400w",
"800w",
"1200w"
]
}
checkbox
The checkbox template part is mainly used for control-structures in the Handlebars-template.
It supplies a simple checkbox element and a boolean value that can be used e.g. for {{#if}}-blocks within the content element.
option
The option template part is mainly used for control-structures in the Handlebars-template. It allows to define a list of key/value options in the design and provides the key of the option selected by the user in the content editor.
Since the {{#if}} blocks in Handlebars do not support logical expressions, the variable from the option part is usually used with the Handlebars helpers eq and neq. For example such as:
{{#eq imageCaptionOption.option "yes"}}
<div class="paragraph-light news-image-description">{{image.altText}}</div>
{{/eq}}
The negation works analogous, just using {{#neq a b}} instead of the {{#eq a b}} shown in the example above.
Configuration
The option template part needs one or more configured options that each consists of a value and a text that is displayed to the user in the editor user interface. In the Handlebars template, the value of the selected option is applied to the 'value' variable.
The template part configuration is structured as follows:
-
options: List of options consisting of:
-
text: Display text shown to the user in the content element editor
-
value: The value that is exposed in the variable context of the template part
-
Example:
"config": {
"options": [
{
"text": "Ja",
"value": "yes"
},
{
"text": "Nein",
"value": "no"
}
]
}
dynamic-value-list
This Handlebars template part offers functionality for the integration of BSI Suite value lists into any HTML form, providing REST endpoints for the purpose of retrieving values from such lists.
Variables
-
Value list: A reference to a BSI Suite value list. Mandatory.
-
Initial value: Optional initial value for the HTML form field. Only values belonging to the referenced Value list are available. Only editable if Value list is set.
| It is also possible to define an initial value within the configuration of a story, which supersedes the initial value of the content. |
Backend processing and HTML rendering
The BSI backend processes dynamic value lists in the following manner:
-
The BSI Suite value list is defined in the content using the content editor, and this value is stored in the CX database.
-
During the rendering of the HTML, the ID of the value list is utilized to create a REST endpoint, and the URL of this endpoint is transmitted to and made available in the client’s DOM.
-
The endpoint is invoked by the client to retrieve all values of the value list.
-
The values are processed and transmitted to the CX backend upon form submission, and these values can be utilized in CX stories for further processing. The values automatically possess the correct BSI data model type.
Involved BSI HTML Attributes
-
data-bsi-entity-id: Holds a BSI datamodel entity ID (i.e., an ID referencing the BSI Suite value list). Only used at configuration time, not sent to clients.
-
data-bsi-entity-key-value: Holds an entity key belonging to the element’s data-bsi-entity-id. This value is used to set the form field’s initial value. Only used at configuration time, not sent to clients. The value of this attribute is symmetrically encrypted and set into the HTML element’s value attribute before the HTML is sent to any clients.
-
data-bsi-entity-key-display-text: Human readable label of the key stored in data-bsi-entity-key-value. This attribute is preserved and sent to clients.
-
data-bsi-rest-resource: Target endpoint to lookup values belonging to the element’s data-bsi-entity-id. This attribute is preserved and sent to clients.
Design integration
The integration process consists of two stages. Firstly, the BSI data model entity IDs and keys are set in the content during configuration time. Secondly, these values are retrieved when the HTML form is opened via REST during runtime.
Configuration time
The Handlebars template part’s editor persists the following context variables:
-
dataModelValue: A nested JSON holding the BSI datamodel type and (if set) initial value key.
-
displayText: The initial value key’s human-readable label. May be empty.
{
"dataModelValue": {
"type": "entity:1234",
"value": "entity:1234:key:9876"
},
"displayText": "I belong to key 9876"
}
To correctly encrypt and map BSI datamodel values, the CX backend expects:
-
The HTML element tagged with dynamic-value-list also has to reference and set the BSI HTML attribute data-bsi-entity-id. data-bsi-entity-id is used to create the URL for the lookup-call.
-
The HTML input on the form has to reference the BSI HTML attribute data-bsi-entity-key-value. This enables default values.
<div data-bsi-element="dynamic-value-list" data-bsi-entity-id="{{context.dataModelValue.type}}" >
<input
data-bsi-entity-key-value="{{context.dataModelValue.value}}"
data-bsi-entity-key-display-text="{{context.displayText}}" >
</div>
Runtime
It is the frontend code’s responsibility to retrieve and display a value list’s values.
-
The element tagged with dynamic-value-list contains a REST endpoint to retrieve the values of the value list. It is exposed via the BSI HTML attribute data-bsi-rest-resource. The REST call uses the GET method and returns a JSON containing one attribute called options. This attribute holds a list of id-text pairs. The id is always encrypted.
-
Ensure that the correct encrypted id is sent to the CX backend on a form submit.
-
The element marked with the tag "dynamic-value-list" contains a REST endpoint for retrieving the values of the value list, which is exposed via the BSI HTML attribute "data-bsi-rest-resource". The REST call utilizes the GET method and returns a JSON object containing a single attribute named "options", which holds a list of id-text pairs. The id is always encrypted.
-
It is essential to ensure that the correct encrypted id is transmitted to the CX backend upon form submission.
<div data-bsi-rest-resource="http://your-client.bsi/inbound/default/r/bsi-code-rest-resource?entityId=WcaYlNlwe6VnI72" >
<input
value="kOi4pUb17YbVcvVlaU9-kiQW993"
data-bsi-entity-key-display-text="I belong to the encrypted key kOi4pUb17YbVcvVlaU9-kiQW993" >
</div>
{
"options": [
{
"id": "kOi4pUb17YbVcvVlaU9-kiQW993",
"text": "label 1"
},
{
"id": "mRt7xQe42NcBzxTpoL3-zvER581",
"text": "label 2"
},
{
"id": "fLn2vHd89JmTqyWkdA6-wuTY764",
"text": "label 3"
}
]
}
REST parameters
The REST endpoint exposed via data-bsi-rest-resource accepts the following query parameters:
-
entityId: The encrypted value list type. Mandatory. This parameter is provided by the CX backend and already set. There is no need to adapt it in the frontend.
-
q: Query parameter. Optional. If set, it’s value is used to perform a case-insensitive prefix search on the lookup result’s texts. Only matching results are sent to the client.
-
lang: Requested language parameter. Optional. If set, the value is used to parse a JAVA locale. The locale is used to perform the lookup and returns the result in the corresponding language. It is recommended to set this attribute to the DOM’s language attribute (which is normally set via CX’s content engine). If no value is provided, the request’s language (i.e., the client’s browser language) is used. The last fallback is the CX backend’s system language. Example values: de-DE, de-CH, de, en, en-US, fr, …
http://your-client.bsi/inbound/default/r/bsi-code-rest-resource?entityId=WcaYlNlwe6VnI72&q=searchVal&lang=de
The REST endpoint made available via data-bsi-rest-resource supports the following query parameters:
-
entityId: The encrypted value list type. Mandatory. This parameter is provided by the CX backend and is already set, therefore no adjustments are necessary in the frontend.
-
q: Query parameter. Optional. If specified, its value is utilized to perform a case-insensitive prefix search on the lookup result’s texts. Only matching results are transmitted to the client.
-
lang: Requested language parameter. Optional. If specified, the value is used to parse a JAVA locale. The locale is then used to perform the lookup and return the result in the corresponding language. It is recommended to set this attribute to the DOM’s language attribute, which is typically set via CX’s content engine. If no value is provided, the request’s language (i.e., the client’s browser language) is used. The last fallback is the CX backend’s system language. Example values include de-DE, de-CH, de, en, en-US, fr, etc.
How to create a content element using the value list part
The element is based on the new Handlebars integration in BSI CX designs. To work properly with the CX form engine, your element should also contain two additional template element parts:
-
id: A plain-text Handlebars template part to uniquely identify the form field in a form. Ensure that the HTML form field’s ID attribute is set to this value. Mandatory.
-
name: The form field’s label. Mandatory.
Minimal example implementation
{
"id": {
"value": "dynamic-dropdown-1"
},
"name": {
"value": "Field"
},
"entityTypeAndInitialValue": {
"dataModelValue": {
"type": "",
"value": ""
},
"displayText": ""
}
}
<div data-bsi-element="dynamic-value-list" data-bsi-entity-id="{{entityTypeAndInitialValue.dataModelValue.type}}" >
<label for="{{id.value}}">{{name.value}}</label>
<input id="{{id.value}}" name="{{id.value}}"
value="{{entityTypeAndInitialValue.displayText}}"
data-bsi-entity-key-value="{{entityTypeAndInitialValue.dataModelValue.value}}"
data-bsi-entity-key-display-text="{{entityTypeAndInitialValue.displayText}}" >
</div>
{
"contentElements": [
{
"elementId": "example-dynamic-value-list",
"type": "template-element",
"file": "path/to/my-example.hbs",
"contextFile": "path/to/my-example.json",
"label": "Dynamic select",
"templateParts": [
{
"partId": "plain-text",
"partContextId": "id",
"label": "ID"
},
{
"partId": "plain-text",
"partContextId": "name",
"label": "Field name"
},
{
"partId": "dynamic-value-list",
"partContextId": "entityTypeAndInitialValue",
"label": "Value list"
}
]
}
]
}
The code snippets above represent the HTML component. A possible method for invoking the REST endpoint is provided in the snippet below.
let $valueListElem = findValueListElement(); // Frontend code's responsibility
let dataSourceUrl = $valueListElem.attr('data-bsi-rest-resource');
fetch(dataSourceUrl, {
method: "GET",
body: new URLSearchParams({
q: getQueryParam($valueListElem), // Frontend code's responsibility
lang: document.documentElement.lang })
})
.then(response => {
// ... Frontend code's responsibility
});
If you want to implement a dropdown including autocompletion, you may consider JQuery UI as a starting point.
Data BSI Attributes
Data BSI attribute are regular DOM element attributes, starting with data-bsi-. In a CX design they must be used to amend CX specific semantic information to the DOM or to control processing logic while the DOM is rendered by the CX server.
Control Attributes
data-bsi-remove-if
The presence of an HTML element in the generated content can be controlled by using data-bsi-remove-if. The attribute value can use the following boolean variables:
-
draft: The element is only inserted if the content is being edited in the editor. -
live: The element is only inserted if the content is driven by a story. -
authenticated: The element is only inserted if an authenticated visitor exists on the CX HTTP session (only available in CX websites). -
production: The element is only inserted if the CX server is configured as production system. This is defined by the config property 'bsi.studio.html.output.environment'. -
test: The element is only inserted if the CX server is configured as test system. This is defined by the config property 'bsi.studio.html.output.environment'.
The attribute value may have a negate operator (!). Using this operator you can negate the condition, for instance !authenticated will only be removed in case the visitor is not logged in.
The attribute value can be a complex expression, see example below.
data-bsi-remove-if must not be used for tag elements containing content elements! Otherwise, those cannot be edited in draft mode.
|
Examples
A common and recommended use of data-bsi-remove-if="draft" is to suppress executing JavaScript in the content editor, which may lead to unintended side effects.
<script data-bsi-remove-if="draft">
// this script will not mess around with your content while being edited in the content editor
</script>
A common use case of data-bsi-remove-if="live" mode is to display context information to the user that should only be present while editing a content.
<p data-bsi-remove-if="live">This hint will never be shown in any content driven by a story.</p>
The following example shows a complex expression. If your design includes a script for analytic/tracking tools, you should make sure the script is never executed on a test system and never in the editor.
<script data-bsi-remove-if="!production || draft">
console.log('run only in production');
</script>
data-bsi-hide-[type]-button
data-bsi-hide-edit-button may be used to prevent editing of a content element. The attribute must be located on the same tag as the data-bsi-element attribute.
<div data-bsi-element="..." data-bsi-hide-edit-button="true">...</div>
data-bsi-hide-move-button, data-bsi-hide-copy-button, and data-bsi-hide-remove-button are working in the same manner, but on dropzone level. They will prevent from moving, copying, or removing content elements on a dropzone.
<div data-bsi-dropzone="..." data-bsi-hide-move-button="true" data-bsi-hide-copy-button="true" data-bsi-hide-remove-button="true">...</div>
Form Processor Attributes
The CX server may implement programmed backend logic to validate and process a specific form in a CX website. The following attributes control which form processor is called on a form submit. A CX project that implements its own backend logic may contribute its own form processors.
Set the data-bsi-form-type on a form element to specify which form processor handles the form on submit. Predefined values are login and change-password. A CX project may define custom values, which must map to a form processor implementation in the CX backend server.
The data-bsi-show-form-type-field controls whether the CX content editor shows a drop-down menu to choose a form type in the Form element part. This gives a design author the freedom to create a generic content element for forms, where the content author can choose to create a standard form or a form using a form processor via drop-down menu, or to provide a complete form as a dedicated form element, where the content author cannot change the form type in the content editor.
Set the data-bsi-form-type attribute on a form field element to mark a field interpreted by a form processor. For instance a login form requires the form to have two fields, one must define data-bsi-form-type="user-id" and the other data-bsi-form-type="password" in order to pass the form processor validation. Each form process may define custom values for this attribute.
See dynamic value list template part for more information data-bsi-entity-id, data-bsi-entity-key-value, data-bsi-entity-key-display-text and data-bsi-rest-resource.
The CX backend guarantees that values defined in data-bsi-entity-key-value are consistently encrypted prior to transmission to clients.
|
Websites
Terms
-
Website: The complete website. Contains several pages in different languages.
-
Page: A single page within a website. Pages appear in the navigation and have a content in different languages.
-
Include: A content snippet, which is saved locally once and can be referenced at any point within the website. We differentiate between built-in includes and custom includes. Built-in includes are content snippets that are included in the design.zip, custom includes are created by the user based on a content-element.
-
Website content type: Website content type items are pages from a website with a specific type, like a blog or a press release. Other than website pages, website content type items do not affect the structure of the website. Which means, a user can publish for instance a blog page, without changes to the story that uses the website. Additionally, a website content type has specific fields, like author or tags. With the "Website Content Type Query" step you can query, filter and sort website content type items dynamically and use the results as CX iterator or as REST response in the frontend code of the website.
Concepts
-
CX-Placeholders can be used on every page of a website. Their scope generally involves a single page. For example the placeholder
"{Subject}"has to be mapped once for each page that uses it. A placeholder within an include (e.g. header or footer) only has to be mapped once. An example for that case would be the display of"{Username}"within the header of a portal-website. Placeholders can not be used within page-titles or the navigation. -
Custom includes can’t be nested. Technically, that would be possible, but it would need many checks and visualizations inside the content editor to detect potential infinite recursions and give feedback to the user through reasonable error messages. For that reason we currently don’t support that feature.
-
An include can only be used once within the same page.
-
Multilingualism: All contents and their includes are always created in every active language for their website. E.g.: The website is configured to be German and Italian. On the creation of a new page, two pages will be created: one for Italian and one for German (including their dummy-contents). Within the UI there is no option to create a single page in one specific language only. If necessary, a CX-condition may be used to hide a page for a specific language inside the navigation-tree.
Handlebars, Templates
The template engine Handlebars is available to the design developer at runtime. The templates are parsed server-sided. We’re using the Java-Port by JKnack. In the design, handlebars templates are important especially for display of the navigation.
Handlebars templates can only be used for parts of a website that can’t be edited by the user. We set that rule, because templates are parsed server-sided. When the content-editor receives the HTML source, the templates have already been resolved. If the user would edit a content-element that contained handlebars syntax, we could only save the rendered content that the user edited. The original handlebars code would be lost.
For that reason, handlebars should mostly be used for includes that can’t be edited. Examples for such includes are the master (design.html), the navigation or areas outside a dropzone.
This mechanism can also be used consciously to set multilingual default texts, which are deposited inside the website.json file. An example for an include for the website header in handlebars code could look something like this:
<div data-bsi-element="master-header">
<img src="img/nyan-cat.gif">
<h1 data-bsi-element-part="plain-text">{{bsi.nls "website.title"}}</h1>
</div>
On the initial rendering of the page inside the content editor, the text "website.title" is resolved. As the user saves the page, the initial handlebars code is substituted by the actual text.
Handlebars syntax can also be used in the content-element source files; especially for the NLS-helper to define multilingual default texts for each content-element. Content-elements with Handlebars must have the file extension .hbs.
Important: Files that contain Handlebars syntax aren’t valid HTML documents. They shouldn’t be handled with HTML parsers like JSoup, otherwise the handlebars syntax gets corrupted. Files that include handlebars code should use the suffix .hbs. IntelliJ supports this syntax with respective plugins.
Handlebars Helpers
Besides the built-in standard helper, BSI CX may also use the following helpers:
bsi.nls
This helper is used to display localized texts or resources inside the design. The syntax looks like this: {{bsi.nls '[textKey]'}}.
It can also be used inside the content-elements.html file to deliver localized default texts for different languages.
Text keys are created inside the design.json at the property nls. Text that doesn’t depend on the locale is defined with the key *. The resolution follows from the most specific locale up to the default, e.g.:
{
"nls": {
"hello.world": {
"*": "Hello World",
"de": "Hallo Welt",
"de-CH": "Sali Wält"
}
}
}
bsi.jsonStringify
This helper is used to output a JSON object (or more precise, a DO entity from the BSI customer suite) into a string representation. Example:
<meta name="x-metadata" content="{{bsi.jsonStringify page.metadata}}">
Depending on the actual value of the 'metadata' object, this could output:
<meta name="x-metadata" content="{"value"=123}">
bsi.localeLanguage
This helper is used to output the 'language' part of a Java Locale string. Example, assuming locale is 'de-CH':
<meta name="x-language" content="{{bsi.localeLanguage locale}}">
Output:
<meta name="x-language" content="de">
bsi.localeCountry
This helper is used to output the 'country' part of a Java Locale string. Depending on the actual value of the locale, this helper may return an empty string. Example, assuming locale is 'de-CH':
<meta name="x-country" content="{{bsi.localeCountry locale}}">
Output:
<meta name="x-country" content="CH">
bsi.nl2br
This helper is used to replace newline characters with an HTML BR tag in a plain text string. Example, assuming the value of text is 'Hello\n<b>World</b>':
<div>{{bsi.nl2br text}}</div>
Output:
<div>Hello<br><b>World</b></div>
Handlebars Variables
Following variables are available to the design developer on runtime. The variables can be used in handlebars templates, e.g.: {{navigation.activePage.contentId}}.
Top-Level
-
defaultLocale (String): Default locale of the website. Can be used to compare locales, for instance in an
hreflangattribute to render the valuex-defaultwhen a locale is equals the defaultLocale. Example:en. -
designBaseUrl (String): base URL of the design on the resource server.
-
language (String): Current language as it’s used in the
langattribute of the HTML tag, e.g.:de. -
locale (String): Complete locale. The locale is used to resolve text keys through the
bsi.nlshelper, e.g.de-CH. -
localizedWebsiteBaseUrl (String) The (root) base URL of the website, including the Locale part. Example: http://localhost:8085/inbound/default/e/l/my-website/de-CH
-
navigation (Navigation object): Contains the hierarchical navigation tree, example follows.
-
page (Navigation Item object): The currently active page. This is a shortcut for
navigation.activePage. Note: if the current page is a website content type item which is linked to a navigation item through theactiveWhenWebsiteContentTypeproperty, the 'page' property points to the website content type item, but the 'navigation.activePage' points to the navigation item. There might be a difference regarding the titles. -
paginationInfo (Pagination Info object): Contains the information for pagination. This object is only filled if the pagination feature is specified in the
design.jsonfile. -
resourceBaseUrl (String): base URL of the resource server.
-
title (String): Title of the website. Matches the field "Title" in the website-masters editor. Can be used in combination with the title of the current page to display the title of the HTML document, e.g.:
{{title}} | {{navigation.activePage.pageTitle}}. -
websiteBaseUrl (String) The (root) base URL of the website, without the Locale part. Example: http://localhost:8085/inbound/default/e/l/my-website
Navigation (Object)
-
activePage (Navigation Item object) The currently active page
-
items (array of Navigation Item objects) A hierarchical list of navigation items. Loop through these nested arrays, to render the navigation of the website.
Navigation Item (Object)
-
id (UUID): Primary key according to table BSI_WEBSITE_REV_NAV#ID.
-
contentId (UUID): Primary key according to table BSI_WEBSITE_REV_CONT#ID.
-
url (String): The URL for a website page or website content type item.
-
createDate (String): The create date of the website page or website content type item.
-
updateDate (String): The update date of the website page or website content type item.
-
canonicalUrl (String): The optional canonical URL for the website page or website content type item.
-
active (Boolean):
Trueif the navigation element (page) is active, which means it should appear highlighted in the navigation. -
folder (Boolean):
Trueif BSI_WEBSITE_REV_NAV_LOCALE#NAV_ITEM_TYPE = "folder". Used to distinct pages and folders in the navigation. -
level (Integer): Recursion-level of the navigation element, starting with 0 = Root.
-
image (String): Locale independent absolute image URL. Can be used to render an icon for the website page in the navigation.
-
localizedImage (String): Locale dependent absolute image URL. Can be used to render a localized preview image for the website content type item.
-
title (String): The title of the navigation item.
-
pageTitle (String): The page title of the website page or website content type item.
-
pageTitleShort (String): The short page title of the website page or website content type item. Can be used to render an 'og:title' meta tag in the HTML.
-
description (String): The description of the website page or website content type item. Can be used to render a 'description' meta tag in the HTML.
-
descriptionShort (String): The short description of the website page or website content type item. Can be used to render an 'og:description' meta tag in the HTML.
-
openGraphType (String): The Open Graph type of the website page or website content type item. Can be used to render an 'og:type' meta tag in the HTML.
-
tags (Array of strings): An array of strings with tags for the website page or website content type item, as used in a tag cloud in a blog.
-
noindex (Boolean): Whether the website page or website content type item should be indexed by a search engine crawler. Can be used to render a 'robots/noindex' meta tag in the HTML.
-
nofollow (Boolean): Whether the link to a website page should be followed by a search engine crawler. Can be used to render a 'rel/nofollow' attribute in the HTML.
-
localizedUrls (Object): Object with URL as value and locale as key. Can be used to render a language-switch widget in the HTML.
-
metadata: (Object) Locale independent JSON metadata of the website page or website content type item.
-
localizedMetadata: (Object) Locale dependent JSON metadata of the website page or website content type item.
-
items (array of Navigation Item objects) A hierarchical list of child navigation items.
-
authorName (String): The author name of the website content type item. Only available for the active page.
-
authorImage (String): Absolute image URL. The author image of the website content type item. Can be used to render an image of the author. Only available for the active page.
-
localizedAuthorDescription (String): The localized author description of the website content type item. Only available for the active page.
Pagination (Object)
-
currentPageNo (Number): Current page number.
-
numDataRecordsPerPage (Number): Number of records per page.
-
firstDataRecordOffset (Number): Index of the first record on the current page.
-
totalNumDataRecords (Number): Total number of existing records.
-
firstPageUrl (String): URL of the first page.
-
lastPageUrl (String): URL of the last page.
-
lastPageNo (Number): Page number of the last page.
-
previousPages (Array of Pagination Page objects): Provides information on pages previous to the current page.
-
nextpages (Array of Pagination Page objects): Provides information on the next pages following the current page.
Pagination Page (Object)
-
pageNo (Number): Page number.
-
url (String): URL of the page.
-
index (Number): Offset of the page number to the current page number.
Handlebars Variables Example (as JSON)
The following JSON snippet shows the possible content of a variables object at runtime.
{
"title": "BSI Software",
"language": "de",
"locale": "de-CH",
"page": {
"id": "1006",
"contentId": "2006",
"url": "ueber-bsi.html",
"title": "Über BSI",
"pageTitle": "Erfahren Sie mehr zu BSI",
"level": 0,
"active": true,
"localizedUrls": {
"de-CH": "ueber-bsi.html",
"en-CH": "about-bsi.html"
}
},
"navigation": {
"activePage": {
"id": "1006",
"...": "INFO: same object as in 'page'"
},
"items": [
{
"id": "1002",
"contentId": "2002",
"url": "loesungen.html",
"title": "Lösungen",
"pageTitle": "Lösungen für Ihre Kunden",
"level": 0
},
{
"id": "1003",
"contentId": "2003",
"url": "branchen.html",
"title": "Branchen",
"folder": true,
"level": 0,
"items": [
{
"id": "1004",
"contentId": "2004",
"url": "banking.html",
"title": "Banking",
"pageTitle": "Lösungen fürs Finanzwesen",
"level": 1
},
{
"id": "1005",
"contentId": "2005",
"url": "health.html",
"title": "Health",
"pageTitle": "Lösungen fürs Gesundheitswesen",
"level": 1
}
]
},
{
"id": "1006",
"...": "INFO: same object as in 'page'"
}
]
}
}
The following Handlebars (.hbs) code snippet shows how the variables provided in the example above, are used in the template. The example shows a typical template for the website navigation.
{{#navigation}}
<nav class="navigation">
{{#items}}
<ul class="nav-item-container level-root">
<li class="nav-item level-root">
<!-- root level -->
{{#active}}
<span class="nav-item-active">{{title}}</span>
{{/active}}
{{^active}}
<a href="{{url}}">{{title}}</a>
{{/active}}
</li>
{{#items}}
<ul class="nav-item-container level-1st">
<!-- 1st level -->
<li class="nav-item level-1st">
{{#active}}
<span class="nav-item-active">{{title}}</span>
{{/active}}
{{^active}}
<a href="{{url}}">{{title}}</a>
{{/active}}
</li>
</ul>
{{/items}}
</ul>
{{/items}}
</nav>
{{/navigation}}
Working with Pre-Defined Includes
Pre-defined includes act like a regular content-element in the website editor, but they cannot be edited by the user. Instead pre-defined includes are defined as a Handlebars template, which means it is possible to use all the variables provided by the website. The Handlebars placeholders are not resolved when the page is edited in the website editor, but only when the page is finally rendered in the browser.
This allows to build content-elements which output data available as metadata on the page, for instance the 'page title' or the 'description'. The advantage for the content user is, that he or she must not copy/paste content already defined as metadata.
The following example shows how to make a pre-defined include 'page-info.hbs', which outputs the 'page title' and the description.
<div class="page-info" data-bsi-element="page-info" data-bsi-hide-edit-button="true">
<p>Page title: <b>{{page.pageTitle}}</b></p>
<p>Description: {{page.description}}</p>
</div>
In the design.json you must define the pre-defined include and also define a content-element. You can reference the same file in both places. Note, the example shows only the relevant part of the design.json.
{
"contentElementGroups": [{
"groupId": "content",
"contentElements": [
{
"elementId": "page-info",
"file": "include/page-info.hbs",
"label": "Page Info",
"icon": "info",
"hidden": true
}
]
}],
"website": {
"includes":{
"page-info": {
"includeType": "pre-defined",
"file": "include/page-info.hbs",
"name": "Page Info",
"editable": false
}
}
}
}
Do not forget to add the new content-element/include to the data-bsi-dropzone-allowed-elements attribute in your dropzones.
|
Configuration files
This chapter describes the necessary configuration files.
Website-specific metadata inside design.json
The design.json file is a metadata file for structured configuration data.
Example for the design.json file for website designs:
{
"schemaVersion": "25.1",
"defaultLocale": "de",
"locales": ["de", "en"],
"nls": {
"page": {
"*": "Seite",
"en": "Page"
}
},
"website": {
"maxNavigationLevel": 2,
"pagination": {
"numDataRecordsPerPage": 20,
"numAdjacentPages": 3
},
"includes":{
"page": {
"name": "Vorlage für Inhaltsseiten",
"reference": "page",
"file": "include/page.hbs",
"contextFile": "include/page-context.json"
},
"navigation":{
"name":"Navigation",
"reference":"navigation",
"file":"include/navigation.hbs"
},
"header":{
"name":"Kopfzeile",
"reference":"header",
"file":"include/header.html",
"editable":true
},
"pagination-element": {
"name": "Pagination",
"contentType": "pre-defined",
"file": "include/pagination-element.hbs",
"editable": false
},
"footer":{
"name":"Fusszeile",
"reference":"footer",
"file":"include/footer.html",
"editable":true
}
}
},
"websiteContentTypes": ["blog", "press-release"]
}
-
schemaVersion(String): Needs to be set to 1.0 or greater. For details, see chapter about the design schema version. -
defaultLocale(String): Default language (must be contained inlocales). -
locales(Array of String): List with languages (e.g.:en,de,de-CH). -
nls: Contains a map with NLS keys, which can be used within the .hbs files withbsi.nls. -
website:maxNavigationLeveldefines the maximum depth of the navigation (level of the hierarchy) that can be displayed by the design. To allow better structuring of pages and folders, the website editor allows the creation of websites with greater depth, but such pages or folders can’t be selected through the navigation on runtime. However, an internal link from one page to another page with greater depth is still possible. -
website:paginationis used for pagination (see Pagination). The propertynumDataRecordsPerPagecan be used to specify how many records are to be displayed simultaneously on a page. The propertynumAdjacentPagesdescribes how many lower and higher page numbers are to be displayed in the pagination navigation. For example, ifnumAdjacentPagesis set to 3 and you are on page 2, then the maximum number of pages displayed in the navigation is 1, 2, 3, 4 and 5. -
includes: See below. -
websiteContentTypes(Array of string): Defines which 'website content types' are allowed to be used with this design. Possible values are: "blog", "event", "pop-up", "press-release". Your CX project may contribute additional website content types.
Includes
All files that are inherited to the database on the creation of a website as navigation item or include have to be referenced in the design.json file. All includes from the design.zip are inherited automatically with content-type=built-in, unless content-type=pre-defined is set explicitly.
The file design.html matches the master and is automatically created inside the website internally as an include with the reference master. For that reason this include should not be defined in the design.json file, as it is defined already.
The minimal include is page, which works as a template for new pages within a website.
The key, which is used for the bsi:include-tags, is used as a unique reference to the include.
For each include, the following properties can be configured:
-
name: Displayed name of the include. -
file: Points to a file inside the zip archive, relative to the root of the zip file. Only .html and .hbs are allowed. -
contextFile: Editable includes can have a context for Handlebars content elements, the same as thedesign.hbscan for e.g. Landingpages. -
contentType:built-in(default, if not set manually),pre-definedanduser-defined. -
editable: True, when the include can be edited in the content editor by the user. Default istrue. Must befalsefor all includes that use Handlebars. You should avoid to change the value of this property in design updates. If you must change the value, a 'direct update' of the content is enforced in the design update. This means, that the existing HTML content of the current website cannot be migrated and will be overridden with the content of the include in the new version of the design. Instead of changing the editable property, you should consider to define a new include with a new reference/ID.
Design.hbs
The design.hbs file is equivalent to the "master" of the website.
On the creation of a website, an include with reference master is created with the contents of the design.hbs file.
This include can’t be edited by the user.
However, the design can reference other includes that may be edited, so that e.g. the footer can be edited by the user.
A design can contain exactly one include for a page.
That include uses the dynamic reference {navigation.activePage.contentId}.
For the editor this include has a special meaning:
When displayed in the editor, the full page (including the master) is displayed, but when saved, only the content of the respective page is saved.
Advice: There should be no tags of the template-engine used for includes or pages that the user may edit.
In the example below the value of the editable property from the include definition in the design.json file is shown as an HTML comment, for better understanding.
<html>
<head>
<title>{{title}} | {{page.pageTitle}}</title>
</head>
<body>
<header>
<h1>Unicorn Inc.</h1>
{{#navigation.index.active}}
<img src="logo.png" width="100" height="100">
{{/navigation.index.active}}
{{^navigation.index.active}}
<a href="{{navigation.index.url}}"><img src="logo.png" width="100" height="100"></a>
{{/navigation.index.active}}
<bsi:include id="navigation"><!-- editable: false -->
</header>
<!--
activePage is always set by the framework. When the root-URL '/' is loaded,
'index' is used automatically.
When the include-tag is resolved, it's replaced by the respective HTML-snippet.
Having a single root-element (e.g.: <div>, <p>, ...), that encapsulates the entire DOM of the include, is a technical requirement for any include.
When changed, the attribute "data-ce-include-id" is set on this element with the value of the original attribute "id".
-->
{{#response.ok}}
<bsi:include reference="{navigation.activePage.contentId}"><!-- editable: true -->
{{/response.ok}}
<!--
This include is only loaded when the response is != OK. It takes a special function to set response.ok to false in the content-editor, to even make it editable in the editor.
-->
{{^response.ok}}
<bsi:include id="error"><!-- editable: false -->
{{/response.ok}}
<footer>
<!--
The contents of the "design.hbs" cannot be edited directly, so the part that should be editable by the user needs to be transferred to a separate include. -->
<bsi:include id="footer-text"><!-- editable: true -->
</footer>
</body>
</html>
The include "footer-text" loads the built-in include footer-text.html. This file looks like this:
<div data-ce-content-element="footer-text"
data-ce-element-part="formatted-text">
This is a default-text for the footer. This text can be changed in the editor by the user.
</div>
Content editor
Before the content is displayed in the editor, it has to be preprocessed by the WebsiteProcessor. It resolves includes and starts the template-engine. Just like the live-website, the content editor also creates an object with website variables.
Here is an example for content, when it’s loaded from the database:
<bsi:include reference="footer-text">
{{title}}
Here is an example for content, when it’s shown in the editor:
<div
data-bsi-include-reference="footer-text"
data-bsi-element="footer-text"
data-bsi-element-part="formatted-text">
Ipsem Lorum
</div>
BSI Website
Pagination
Pagination can be used to distribute data supplied by an external system (e.g. via REST) to several pages on a website, so that only part of the data is displayed on one page. With Next and Back buttons on the website page, it is then possible to display a separate set of data on each page.
The pagination must be defined via website: pagination in design.json and provided via an include. The include must exist in the design.json file and have the special include type pre-defined (see Website-specific metadata inside design.json). The include itself should have the extension .hbs and use handlebars. The possible values for the Handlebars templates are documented in the chapter on website variables (see Pagination (Object)). Besides, the attribute data-bsi-hide-edit-button should be set, as includes that use handlebars should not be editable in the website editor. The following is an example of a possible template:
<div class="pagination-element" data-bsi-element="pagination-element" data-bsi-hide-edit-button="true">
<a href="{{pagination.firstPageUrl}}">First Page</a>
{{#each pagination.previousPages}}
<a href="{{url}}">[{{pageNo}}]</a>
{{/each}}
{{pagination.currentPageNo}}
{{#each pagination.nextPages}}
<a href="{{{url}}}">[{{pageNo}}]</a>
{{/each}}
<a href="{{pagination.lastPageUrl}}">Last Page</a>
</div>
This template (include) is then rendered by the template engine at runtime. One possible output is the following. Everything except page 20 are links pointing to the respective pages.
[First Page] [18] [19] 20 [21] [22] [Last Page]
The data to be rendered via pagination can finally be displayed with the table content element. For this, a content element with the element part table should be extended by the attribute data-bsi-show-pagination-field. The following is an example of a possible table template:
<div data-bsi-element="table" data-bsi-element-part="table" data-bsi-show-pagination-field="">
<table>
<tr>
<th>#</th>
<th>Title 1</th>
<th>Title 2</th>
<th>Title 3</th>
</tr>
<tr>
<td>1</td>
<td>Text 1</td>
<td>Text 2</td>
<td>Text 3</td>
</tr>
<tr>
<td>2</td>
<td>Text 1</td>
<td>Text 2</td>
<td>Text 3</td>
</tr>
</table>
</div>
If the attribute data-bsi-show-pagination-field is set, the checkbox with Pagination will be displayed in the configuration of the table content element. In order to use pagination, the checkbox should be activated.
Limitations
There are some limitations to pagination:
-
Pagination has only been implemented for tables (i.e. the element part
table). -
The pagination can only be used directly in a website. We do not support the embedding of such a table in an include. Other content (for example for landing pages) is also not supported.
-
To enable a meaningful display of the handlebars code in the website editor, the Handlebars templates are rendered when the editor is initially loaded. Afterwards, the rendering happens only exceptionally. This can lead to the display being incorrect if page-dependent elements (such as {{navigation.activePage}}) are used in the template.
Metadata / Images for the navigation
Each page and each folder of a website can have JSON metadata and an image to describe the navigation. These two elements are special, because they can only be edited programmatically and within a project-specific step. A possible use case is a REST webservice, which is embedded to the design through javascript. The project-specific step can return the image and the metadata on a GET request. This allows the rendered website to give images and information to the navigation.
These two attributes are invisible per default. However, they can be enabled through the CX setting Website-Editor. Both attributes are the same for every language, so it’s not possible to deposit different images for a German and an English website.
Security - Google reCAPTCHA
It’s highly advised to use the Google reCAPTCHA service "I’m not a robot", when using username/password authentication with non-internal users (public). For that we use the optional CX module com.bsiag.studio.media.googlerecaptcha, which can be configured in the CX settings.
Example for including the dependency in the CX app and the dev pom.xml:
<!-- google reCAPTCHA service for website step -->
<dependency>
<groupId>com.bsiag.studio</groupId>
<artifactId>com.bsiag.studio.media.googlerecaptcha</artifactId>
</dependency>
A script can be included on the website’s design.html:
<script src="https://www.google.com/recaptcha/enterprise.js" async defer></script>
The captcha-field can be added to the login form of the content-elements.html as follows:
<form ... data-bsi-element="login-form">
...
<!-- Content Element: Google reCAPTCHGA Enterprise -->
<div data-bsi-element-part="form-field" class="form-field-container form-field">
<label data-bsi-remove-if="live">{{bsi.nls "captcha"}}</label>
<input data-bsi-remove-if="live" type="text" data-bsi-form-field-type="captcha" value="Google reCAPTCHA" id="g-recaptcha-response" name="g-recaptcha-response">
<div class="g-recaptcha" data-sitekey="the site key from the google api recaptcha registration" data-action="login">
</div>
</div>
The label text can be added to the design.json as follows:
"captcha": {
"*": "Captcha",
"de": "Captcha"
},
After that, the field ID of the captcha field (on the website, that uses the design, simply open the edit dialogue and scroll down to the captcha field ID) has to be set to g-recaptcha-response. This value has to match the settings of the Google reCAPTCHA in the administration view.
The last step is to generate a reCAPTCHA key on the Google Cloud of the customer/company and register it in the settings under Administration - Customer Experience - Settings - Google reCAPTCHA Settings.
For local testing, the URL of the BSI CX website can’t be localhost. For this purpose, the local domain bsiag.local may be used. Simply go to the administration view - CX - Settings under base URL for public links and register a second path, e.g. bsiw0160:
{
"_type": "start.BaseUrlSetting",
"default": false,
"inboundBaseUrl": "http://bsiw0160.bsiag.local:8085/inbound/default",
"qualifier": "default",
"resourceBaseUrl": "http://bsiw0160.bsiag.local:8085/dev/resources"
}
Then select this URL on the smart field base path on the website step.
Here is an approximate guide for the creation of a reCAPTCHA site on the example of BSI (customers have to adapt it to their company):
-
Create project: BSI CX - Website Step → projectId=bsi-cx-website-step
-
Add the service
reCAPTCHA Enterprise APIto the project -
Create login/access data for that api by creating a service account
Service account for bsi cxwith rolerecaptcha enterprise agent→ serviceAccountId=service-account-for-bsi-cx -
Add access key (json file) for that agent service account in IAM section of Google Cloud → credentials json file
-
Create reCAPTCHA site key here (this is just an example)
-
Add domains bsi-software.com and bsiag.local to site → recaptchaSiteKey=6Lcxp-…._lWrq
Documentation:
Creation of websites from existing landingpage templates
The following steps are necessary to create a website template from an existing landingpage template:
-
For navigation inside the website, the navigation has to be created inside an include.
-
Includes for the header- and footer-area have to be defined inside the
design.jsonfile. Furthermore, the Page-Include has to be defined. That area contains the proper content of the website. -
For landingpages images and other resources of the design can be referenced with relative paths. Because the base URL of a website points to the location of the website and not to the design, the handlebars template
{{designBaseUrl}}needs to be put in front of every design resource URL. For example,"img/my-image.jpg"has to be converted to"{{designBaseUrl}}/img/my-image.jpg". The handlebars template is replaced with the design’s correct URL by the CX server on runtime.
Teasers
Teasers are personalized data tailored by CX. An external system can request teaser data via EIP. The response is a JSON object which contains personalized text, images and callback URLs to CX. The external system renders the JSON data into HTML and uses existing tools and libraries in that specific web frontend.
However, teasers can also be used internally in CX, for instance in landingpages, in e-mails and in websites. For that CX provides a special content element, which is rendered server- or client-side. Like the external system, CX requests teaser data from EIP when the content element is rendered.
Depending on the use case, CX design- and content authors can use either server- or client-side rendering:
-
E-mail: only server-side rendering is possible
-
Landingpages: server- or client-side rendering
-
Websites: server- or client-side rendering
Server-side Rendering
The CX design must provide a teaser content element, based on a Handlebars template.
Here is an example for a teaser content element (teaser.hbs):
{{#if numTeasers}}<div class="teaser-display" data-bsi-element="teaser-display"
data-bsi-teaser-rendering="{{teaserRendering.value}}"
data-bsi-teaser-target="{{teaserTarget.value}}">
<img src="{{teasers.[0].imageUrl}}" alt="" class="teaser-image-url">
<div class="teaser-text">{{{teasers.[0].text}}}</div>
<a href="{{teasers.[0].ctaUrl}}" class="button cc-jumbo-button w-inline-block teaser-cta-url">
<div class="teaser-cta-text">{{teasers.[0].ctaText}}</div>
</a>
</div>{{/if}}
Here is an example for the context file for the teaser content element (teaser-context.json):
{
"teaserTarget": {
"value": "Standard"
},
"teaserRendering": {
"value": "server-side"
},
"numTeasers": 1,
"teasers": [
{
"imageUrl": "{{designBaseUrl}}/img/placeholder.svg",
"text": "Lorem ipsum dolor sit amet.",
"ctaUrl": "#",
"ctaText": "Dolore Magna"
}
]
}
The following properties in the JSON are mandatory:
-
numTeasers: a teaser request may return 0 to n teasers. Check this value to control, whether the teaser should appear in the DOM. The CX framework sets this value to 0 if the current participant is not a CRM customer, or EIP returned no teasers for the current participant.
-
teasers: an array of teasers, ordered by priority. Note that the teaser data is not fixed and depends on the content configuration. See the chapter about the JSON design to understand how the teaser data is configured in CX. Usually you build a specific to teaser content element to render a fixed set of teaser data properties. The property names form the contract between the CX content configuration for the teaser, and the Handlebars code in the content element.
You must set these properties in the context JSON, in order to render a proper preview in the CX content editor. All other properties in the context JSON are regular properties required for the Handlebars feature. In this example the design defines two fields 'teaserTarget' and 'teaserRendering' for this content element.
The following data BSI attributes, can be used for teaser content elements:
-
data-bsi-teaser-renderingcontrols whether the content element is rendered by the CX server. If the value is set toserver-side, fixed in the template or by the content editor, the CX framework will load the Handlebars template for this content element, request the teaser data from EIP, will render the Handlebars template into HTML and replace the content element in the DOM. -
data-bsi-teaser-targetthis value is set either fixed in the template or by the content editor. Valid teaser targets are configured in the CX settings. In the content you can specify a target for the teaser content element. This target is added to the request when CX requests a teaser from EIP. Example value:Standard.
Client-side Rendering
If the value for the attribute data-bsi-teaser-rendering is set to client-side the HTML remains untouched by the CX backend.
However, the attribute data-bsi-teaser-endpoint will be added to the DOM.
The attribute value provides an absolute URL to make a REST call from the web frontend to the EIP system.
If your CX design must support client-side rendering, you need to add some JavaScript code to your design. A simple example implementation can look like this:
/**
* Initializes the teasers in the document. This method is a blueprint for other frontend implementations,
* not only in BSI CX designs, but also in external CMS or websites. Here, the endpoint URL is provided by
* the BSI CX backend. The URL consists of these 3 parts:
*
* 1. The EIP host and path to the interface
* 2. The teaser target (e.g. 'Standard')
* 3. The teaser user ID (typically a UUID)
*
* Example: https://eip.bsi-software-com/web-api/get-teaser/Standard/1234567890
*/
function initTeasers() {
let $elements = document.querySelector('[data-bsi-teaser-endpoint]');
if (!$elements) {
return; // Skip. There are no teasers to handle.
}
Array.of($elements).forEach($e => {
let teaserEndpoint = $e.getAttribute('data-bsi-teaser-endpoint');
if (!teaserEndpoint) {
console.log('Teaser endpoint attribute is empty -> Skip teaser request, hide teaser content element.');
$e.classList.add("w-hidden");
return;
}
console.log('Request teaser. Endpoint:', teaserEndpoint);
fetch(teaserEndpoint)
.then(response => {
response.json().then(response => {
console.log('REST response for teaser:', response);
if (!Array.isArray(response)) {
throw new Error('Expected an array as teaser response');
}
let numTeasers = response.length;
if (!numTeasers) {
throw new Error('Teaser response is empty');
}
console.log('Teaser response contains ' + numTeasers + ' teaser elements. Using first element...');
let teaser = response[0];
$e.querySelector('.teaser-text').innerHTML = teaser.text;
$e.querySelector('.teaser-image-url').setAttribute('src', teaser.imageUrl);
$e.querySelector('.teaser-cta-text').innerText = teaser.ctaText;
$e.querySelector('.teaser-cta-url').setAttribute('href', teaser.ctaUrl);
});
})
.catch(reason => {
console.error('REST request for teaser failed', reason);
});
});
}
This function manipulates the DOM to apply the values from the teaser data request.
See the code snippet for the teaser.hbs above, to understand how the HTML looks like for this example.
Also note that some properties values can contain HTML, to avoid security issues, you should always sanitize the HTML before adding it to the DOM.
When the CX backend detects, that it is not possible to request teaser data, for the current participant in CX,
the attribute data-bsi-teaser-endpoint will be empty.
This can happen, if the participant is not a CRM customer.
In that case the design author should skip the REST request and hide the teaser content element, or show a generic placeholder instead.
The JSON Design / Teaser Content Type
You have already learned that teasers are basically JSON data. This means a content author must configure JSON data for a teaser, as he or she would do with HTML content. In order to do that, CX defines the content type 'Teaser'.
CX comes with a special built-in design for this content type. Basically this design type works as a regular HTML based design. CX uses the regular content editor to work with teaser content. However, although we work with HTML content in the content editor, this content is later transformed to JSON data by the CX framework.
In order to do that, the content elements in the JSON design must use the following data BSI attributes:
-
data-bsi-json-propertyDefines the JSON property name. By default, the value for the property name is extracted from the body of the DOM element where this attribute is defined. -
data-bsi-json-value-attributeOptional attribute used in case, where the value is not stored in the body of the DOM element, but must be extracted from an attribute. See Example 2 below.
<div class="content-element-wrap" data-bsi-element="plain-text">
<div class="json-property">{{jsonProperty.value}}</div>
<div class="paragraph-light" data-bsi-json-property="{{jsonProperty.value}}">{{value.value}}</div>
</div>
Example 1: a simple plain text content element for the JSON editor. The content user can freely define a property name in the content editor.
<div class="detail-image-container" data-bsi-element="image">
<div class="json-property">{{jsonProperty.value}}</div>
<img src="{{image.srcUrl}}" width="920" alt="" class="detail-image"
data-bsi-json-property="{{jsonProperty.value}}" data-bsi-json-value-attribute="src">
</div>
Example 2: an image element for the JSON editor. The content user can freely define a property name in the content editor.
Note, the DIV with the class "json-property" is just eye candy to visualize the property name in the content editor (see the image below).
As in a regular design, you can use existing element parts to build your JSON content elements, like the image element part, for choosing images from the CX media library.
You can also build JSON content elements that emit multiple json properties, like a CTA button that emits a ctaText and a ctaUrl.
With that you can provide a reasonable content preview to the content user, although he or she configures just JSON data. This is how the JSON design looks like in the content editor:
Fixed JSON Designs
For rapid prototyping it is very convenient to have a flexible JSON design, which allows to define arbitrary property names.
However, in a real-life project you want to fix property names, because they are the contract between the CX content, the teaser data stored in EIP, the design which must render that data or even an external third party system, which must render teaser data.
For that purpose the CX design developer can make a fixed JSON design. In that design all property names are pre-defined, and they can not be changed by the content author. It is also not possible to add additional content elements or remove existing content elements. All this is achieved by using the existing CX design features, like hiding copy and remove buttons in the content editor or limiting the number of content elements in a dropzone. In comparison to the examples above a fixed plain text element can look like this:
<div class="content-element-wrap" data-bsi-element="plain-text">
<div class="json-property">plainText</div>
<div class="paragraph-light" data-bsi-json-property="plainText">{{value.value}}</div>
</div>
Example 3: a plain text content element for the JSON editor. The property name is fixed (plainText) and can not be changed by the content user.